Основы · 13 мин чтения

Список команд Arduino: синтаксис, примеры и частые ошибки

Команды Arduino с примерами и подводными камнями: pinMode, digitalWrite, analogRead, analogWrite, millis, Serial, map, tone, pulseIn и attachInterrupt.

Команды Arduino — это готовые функции, которые говорят плате, что делать: pinMode() делает пин выходом, digitalWrite() зажигает светодиод, analogRead() читает датчик, а Serial.println() отправляет сообщение на компьютер. Большинство скетчей Arduino на самом деле — это два десятка базовых команд, по-разному сочетающихся друг с другом. Когда ты понимаешь эти команды и их смысл, можно прочитать любой встретившийся код, от мигающего светодиода до робот-машинки, и написать собственный проект с нуля.

В этом руководстве самые используемые команды Arduino разбиты на четыре категории: ввод-вывод, время, математика и связь. Для каждой команды есть синтаксис, значение параметров, короткий пример, который компилируется и работает на Arduino Uno, и ошибка, которую новички делают чаще всего. В последних разделах посмотрим ещё, чем те же команды отличаются на ESP32 и какие у них аналоги на MicroPython для Raspberry Pi Pico.

Плата Arduino Uno с цифровыми и аналоговыми пинами
Arduino Uno: цифровых пинов 0–13, аналоговых входов A0–A5 и пинов PWM, отмеченных ~, хватит, чтобы попробовать каждую команду из этой статьи.

Что такое команда Arduino и как выполняется скетч?

Код Arduino построен на C/C++; большинство того, что мы называем «командами», на самом деле функции, которые предоставляет ядро Arduino. Функция принимает параметры в скобках (например, номер пина и значение), а некоторые из них возвращают результат: digitalRead() даёт состояние пина, millis() — прошедшее время. В каждом скетче Arduino две обязательные функции: setup() выполняется один раз, когда плата включается или сбрасывается, а loop() затем повторяется бесконечно. Подготовка вроде настройки пинов и Serial.begin() идёт в setup(), а работа, которая должна выполняться постоянно, — в loop().

C++ (Arduino IDE)
1// Her Arduino programı bu iki fonksiyondan oluşur / Every Arduino sketch has these two functions
2void setup() {
3  // Açılışta ya da resetten sonra bir kez çalışır / Runs once at power-up or after reset
4  pinMode(13, OUTPUT);      // dahili LED pini çıkış / built-in LED pin as output
5}
6
7void loop() {
8  // Sonsuza kadar tekrar eder / Repeats forever
9  digitalWrite(13, HIGH);   // LED'i yak / turn the LED on
10  delay(500);               // 500 ms bekle / wait 500 ms
11  digitalWrite(13, LOW);    // LED'i söndür / turn the LED off
12  delay(500);
13}

Если с самого начала помнить три правила, можно избежать многих ошибок компиляции: команды чувствительны к регистру (если написать digitalwrite, получишь ошибку «'digitalwrite' was not declared in this scope» — имя не объявлено), каждая инструкция заканчивается точкой с запятой, а текст пишется в двойных кавычках, тогда как отдельные символы — в одинарных. О типах переменных и конструкциях вроде if и for читай в статье Основы программирования Arduino.

Список команд Arduino: таблицы по категориям

Четыре таблицы ниже охватывают почти все команды, которые понадобятся в повседневных проектах. Первая группа — команды ввода-вывода; они задают роль пина, подают на него напряжение или читают с него значение. Номера пинов своей платы можно посмотреть в утилите «Распиновка».

КомандаСинтаксисЧто делаетПример
pinModepinMode(пин, режим)Делает пин входом или выходомpinMode(13, OUTPUT);
digitalWritedigitalWrite(пин, значение)Ставит на пине HIGH (5 V) или LOW (0 V)digitalWrite(13, HIGH);
digitalReaddigitalRead(пин)Читает, HIGH на пине или LOWint b = digitalRead(2);
analogReadanalogRead(пин)Превращает напряжение 0–5 V в число от 0 до 1023int v = analogRead(A0);
analogWriteanalogWrite(пин, значение)Генерирует сигнал PWM от 0 до 255analogWrite(9, 128);
analogReferenceanalogReference(тип)Выбирает опорное напряжение для analogReadanalogReference(INTERNAL);
tone / noTonetone(пин, частота, длительность)Издаёт звук меандром; noTone его останавливаетtone(8, 440, 500);
pulseInpulseIn(пин, значение, таймаут)Измеряет длительность импульса в µspulseIn(10, HIGH, 30000);
shiftOutshiftOut(пинДанных, пинТактов, порядок, байт)Отправляет байт по одному биту (например, в 74HC595)shiftOut(11, 12, MSBFIRST, 255);
attachInterruptattachInterrupt(прерывание, функция, режим)Вызывает функцию в момент изменения пинаattachInterrupt(digitalPinToInterrupt(2), say, FALLING);

Вторая группа — команды времени. Понять разницу между delay(), которая ждёт, и millis() и micros(), которые измеряют время, — ключ к скетчу, работающему без заминок.

КомандаСинтаксисЧто делаетВнимание
delaydelay(мс)Ждёт заданное число миллисекундПрограмма в это время стоит
delayMicrosecondsdelayMicroseconds(мкс)Ждёт заданное число микросекундТочна от 3 до 16383 µs
millismillis()Возвращает миллисекунды с момента запускаunsigned long; переполняется примерно через 49,7 дня
microsmicros()Возвращает микросекунды с момента запускаНа Uno шаг 4 µs, переполняется примерно через 70 минут

Третья группа — математические команды; ими переводят показание датчика в другой диапазон, ограничивают его или генерируют случайное число. Четвёртая группа, команды связи, позволяет плате обмениваться данными с компьютером и другими устройствами.

КомандаСинтаксисЧто делаетПример результата
mapmap(x, отНиз, отВерх, вНиз, вВерх)Пересчитывает один диапазон в другойmap(512, 0, 1023, 0, 255) → 127
constrainconstrain(x, нижн, верхн)Держит значение между двумя границамиconstrain(300, 0, 255) → 255
min / maxmin(a, b) / max(a, b)Возвращает меньшее или большее значениеmin(7, 3) → 3
absabs(x)Абсолютное значение (модуль)abs(-12) → 12
pow / sqrt / sqpow(основание, степень), sqrt(x), sq(x)Степень, квадратный корень, квадратpow(2, 10) → 1024.0
randomrandom(мин, макс)Случайное целое число (макс не входит)random(1, 7) → от 1 до 6
randomSeedrandomSeed(зерно)Меняет начальную точку случайной последовательностиrandomSeed(analogRead(A3));
КомандаЧто делаетПример
Serial.begin(скорость)Открывает последовательный порт на заданной скорости в бодахSerial.begin(9600);
Serial.print(данные)Выводит данные и остаётся на той же строкеSerial.print(temperature);
Serial.println(данные)Выводит данные и переходит на новую строкуSerial.println("Hello");
Serial.available()Возвращает число байтов, ожидающих чтенияif (Serial.available() > 0)
Serial.read()Читает один байт, возвращает -1, если данных нетchar c = Serial.read();
Serial.readStringUntil(c)Читает текст до заданного символаString s = Serial.readStringUntil('\n');
Serial.parseInt()Извлекает целое число из пришедшего текстаint n = Serial.parseInt();
Serial.write(байт)Отправляет сырой байтSerial.write(65); // 'A'
Wire.begin()Запускает шину I2C (Wire.h)Wire.begin();
SPI.begin()Запускает шину SPI (SPI.h)SPI.begin();

pinMode(): задаём роль пина

pinMode(pin, mode) сообщает плате, будет цифровой пин входом или выходом, и обычно пишется один раз в setup(). Режимов три: OUTPUT делает пин выходом (чтобы управлять светодиодом, зуммером или реле), INPUT — высокоомным входом (для схем с внешним подтягивающим или стягивающим резистором), а INPUT_PULLUP — входом с подключённым к 5 V внутренним резистором около 20–50 kΩ. Аналоговые пины (A0–A5) тоже можно использовать как цифровые через pinMode; например, pinMode(A0, OUTPUT) — правильная команда.

Если забыть pinMode(pin, OUTPUT) и вызвать digitalWrite(pin, HIGH), пин не станет выходом; на Uno команда лишь включит встроенный подтягивающий резистор. Светодиод загорится, но будет выглядеть очень тусклым, потому что ток идёт через резистор 20–50 kΩ. Если светодиод тусклый, первым делом проверь строку pinMode в setup().

digitalWrite() и digitalRead(): цифровой выход и вход

digitalWrite(pin, HIGH) на Uno поднимает выходной пин до 5 V, а digitalWrite(pin, LOW) опускает до 0 V. Брать с пина до 20 mA безопасно (абсолютный максимум — 40 mA); светодиод всегда подключай с резистором, а нужный номинал найдёшь в калькуляторе резистора для светодиода. digitalRead(pin) смотрит на напряжение на пине и возвращает HIGH или LOW; на Uno примерно всё выше 3 V считается HIGH, а всё ниже 1,5 V — LOW. В примере ниже встроенный светодиод загорается, когда нажимаешь кнопку, подключённую к D2:

C++ (Arduino IDE)
1const int BUTON_PIN = 2;   // buton: bir ucu D2, diğer ucu GND / button: one leg to D2, the other to GND
2const int LED_PIN = 13;    // dahili LED / built-in LED
3
4void setup() {
5  pinMode(BUTON_PIN, INPUT_PULLUP);   // dahili pull-up direnci açık / internal pull-up resistor enabled
6  pinMode(LED_PIN, OUTPUT);
7}
8
9void loop() {
10  int durum = digitalRead(BUTON_PIN); // HIGH ya da LOW döner / returns HIGH or LOW
11  if (durum == LOW) {                 // pull-up ile basılı = LOW / with pull-up, pressed = LOW
12    digitalWrite(LED_PIN, HIGH);
13  } else {
14    digitalWrite(LED_PIN, LOW);
15  }
16}

С INPUT_PULLUP логика инвертирована: когда кнопка отпущена, пин благодаря внутреннему резистору читает HIGH, а когда нажата — LOW, потому что соединён с GND. Если подключить кнопку без резистора в режиме INPUT, пин висит в воздухе, и digitalRead() возвращает случайные значения; результат может измениться даже от приближения руки. О дребезге кнопок и правильных способах подключения читай в статье Как подключить кнопку к Arduino.

analogRead() и analogWrite(): аналоговый вход и PWM

analogRead(pin) с помощью 10-битного преобразователя (АЦП) превращает напряжение от 0 до 5 V на пинах A0–A5 в число от 0 до 1023. Одно измерение занимает около 100 µs, так что можно делать примерно 10 000 измерений в секунду. Чтобы перевести число в напряжение, используют value × 5.0 / 1023; показание 512, например, означает около 2,5 V. Опорное напряжение можно изменить через analogReference(): вариант INTERNAL использует внутреннее опорное 1,1 V у Uno и повышает разрешение для малых напряжений. Подробности о разрешении и опорном напряжении — в статье АЦП и аналого-цифровое преобразование.

Вопреки названию, analogWrite(pin, value) не выдаёт настоящее аналоговое напряжение. Она очень быстро включает и выключает пин, создавая сигнал PWM: 0 — всегда выключен, 255 — всегда включён, а 127 — примерно половину времени включён. Из-за этого среднего значения светодиод кажется нам горящим вполсилы, а мотор крутится медленно. На Uno PWM дают только пины 3, 5, 6, 9, 10 и 11, отмеченные на плате знаком ~; частота на большинстве пинов 490 Hz, а на пинах 5 и 6 — 980 Hz. analogWrite() сама делает пин выходом, так что pinMode не обязателен. Код ниже задаёт яркость светодиода потенциометром и выводит показание вместе с напряжением в монитор порта; подробности о коэффициенте заполнения и частоте — в статье Что такое PWM?

C++ (Arduino IDE)
1const int POT_PIN = A0;   // potansiyometrenin orta bacağı / potentiometer wiper
2const int LED_PIN = 9;    // ~9 PWM pini + 220 ohm direnç / ~9 PWM pin + 220 ohm resistor
3
4void setup() {
5  Serial.begin(9600);
6}
7
8void loop() {
9  int ham = analogRead(POT_PIN);         // 0-1023 arası / between 0 and 1023
10  float volt = ham * (5.0 / 1023.0);     // sayıyı gerilime çevir / convert the reading to volts
11  analogWrite(LED_PIN, ham / 4);         // 0-1023 -> 0-255 PWM parlaklığı / 0-1023 -> 0-255 PWM brightness
12
13  Serial.print(F("Ham / raw: "));
14  Serial.print(ham);
15  Serial.print(F("  Gerilim / voltage: "));
16  Serial.print(volt, 2);                 // 2 ondalık basamak / 2 decimal places
17  Serial.println(F(" V"));
18  delay(200);
19}
На пине без PWM analogWrite() не может выдавать промежуточные значения: при значениях меньше 128 она ставит на пине LOW, а при 128 и выше — HIGH. Пины A0–A5 у Uno тоже не дают PWM, а у классической Uno R3 нет настоящего аналогового выхода (ЦАП); запись analogWrite(A0, 100) не даст 2 V. Если нужно настоящее аналоговое напряжение, используй плату с ЦАП (например, Uno R4, у которой ЦАП на A0) или добавь к выходу PWM RC-фильтр.

delay(), millis() и micros(): команды времени

delay(ms) ждёт заданное число миллисекунд, и в это время ни одна другая строка в loop() не выполняется; delay(1000) — это целая секунда паузы. Для коротких пауз в микросекундах есть delayMicroseconds(), которая точна от 3 до 16383 µs. millis(), напротив, не ждёт; она возвращает время с момента запуска платы в миллисекундах. Всегда храни результат в переменной unsigned long: если взять int, на Uno значение переполнится, как только превысит 32 767, то есть примерно через 33 секунды, и отсчёт времени сломается. Примерно каждые 49,7 дня millis() снова начинается с нуля; способ с вычитанием ниже правильно работает и через этот переход.

C++ (Arduino IDE)
1const int LED_PIN = 13;
2const unsigned long ARALIK = 500;   // yanıp sönme aralığı (ms) / blink interval (ms)
3unsigned long oncekiZaman = 0;      // son değişim anı / time of the last toggle
4
5void setup() {
6  pinMode(LED_PIN, OUTPUT);
7}
8
9void loop() {
10  unsigned long simdi = millis();
11  if (simdi - oncekiZaman >= ARALIK) {               // taşmada da doğru çalışır / safe across overflow
12    oncekiZaman = simdi;
13    digitalWrite(LED_PIN, !digitalRead(LED_PIN));    // LED'i tersine çevir / toggle the LED
14  }
15  // Burada buton, sensör ve ekran işleri de yapılabilir / button, sensor and display tasks can run here
16}

С таким шаблоном loop() никогда не останавливается; пока светодиод мигает, в том же цикле можно читать кнопку, измерять датчик и обновлять дисплей. micros() работает так же, но в микросекундах; на Uno она идёт шагами по 4 µs и переполняется примерно через 70 минут. Как выполнять несколько задач одновременно, подробно разбираем в статье millis() и delay() в Arduino.

Serial.begin(), Serial.print() и Serial.read(): последовательная связь

Команды Serial позволяют Arduino общаться с компьютером по USB-кабелю, и это твой главный инструмент отладки. Serial.begin(9600) открывает последовательный порт на скорости 9600 бит в секунду; если скорость в мониторе порта не совпадает, на экране будут непонятные символы. Serial.print() выводит данные и остаётся на той же строке, а Serial.println() после вывода переходит на новую. Второй параметр выбирает формат: Serial.print(78, HEX) выводит «4E», Serial.print(78, BIN) — «1001110», а Serial.print(3.14159, 3) показывает «3.142» (по умолчанию у дробных чисел 2 знака после запятой). Постоянный текст внутри F("...") экономит место в 2 КБ ОЗУ у Uno.

Чтобы читать данные, сначала через Serial.available() проверяют, есть ли в буфере ожидающие байты; приёмный буфер Uno — 64 байта. Serial.read() берёт из буфера один байт и возвращает -1, если читать нечего. Код ниже включает и выключает светодиод символами 1 и 0, отправленными из монитора порта:

C++ (Arduino IDE)
1const int LED_PIN = 13;
2
3void setup() {
4  Serial.begin(9600);                    // Seri Monitör de 9600 olmalı / Serial Monitor must be 9600 too
5  pinMode(LED_PIN, OUTPUT);
6  Serial.println(F("1: LED açık / on, 0: LED kapalı / off"));
7}
8
9void loop() {
10  if (Serial.available() > 0) {          // tamponda bayt var mı? / any bytes waiting?
11    char c = Serial.read();              // ilk baytı al / read the first byte
12    if (c == '1') {                      // '1' karakteri, 1 sayısı değil / the character '1', not the number 1
13      digitalWrite(LED_PIN, HIGH);
14      Serial.println(F("LED açık / on"));
15    } else if (c == '0') {
16      digitalWrite(LED_PIN, LOW);
17      Serial.println(F("LED kapalı / off"));
18    }
19    // '\n' ve '\r' gibi diğer karakterler atlanır / other characters such as '\n' and '\r' are ignored
20  }
21}

Обрати внимание: сравнение идёт с '1' (в одинарных кавычках, то есть с символом): набранная на клавиатуре 1 соответствует в таблице ASCII числу 49, так что условие c == 1 никогда не выполнится. Для многозначных чисел можно использовать Serial.parseInt(), а чтобы забрать целую строку — Serial.readStringUntil('\n'). О настройках конца строки, скорости и плоттере монитора порта читай в статье Как пользоваться монитором порта Arduino.

map() и constrain(): масштабирование и ограничение значений

map(value, fromLow, fromHigh, toLow, toHigh) пропорционально переносит число из одного диапазона в другой. За ней стоит формула: (x − fromLow) × (toHigh − toLow) / (fromHigh − fromLow) + toLow. Например, map(512, 0, 1023, 0, 255) возвращает 127, а не 127.6, потому что map() работает только с целыми числами и отбрасывает дробную часть. Обратную зависимость можно получить, поменяв местами границы целевого диапазона: map(x, 0, 1023, 255, 0). map() не ограничивает результат; если вход выходит за заданный диапазон, выход тоже выходит за него. Поэтому её обычно сочетают с constrain(x, low, high).

C++ (Arduino IDE)
1const int LDR_PIN = A0;   // LDR + 10k gerilim bölücü / LDR + 10k voltage divider
2const int LED_PIN = 9;    // ~9 PWM pini / ~9 PWM pin
3
4void setup() {
5  pinMode(LED_PIN, OUTPUT);
6}
7
8void loop() {
9  int isik = analogRead(LDR_PIN);           // odada ölçülen aralık ~200-800 / measured range in the room ~200-800
10  int pwm = map(isik, 200, 800, 255, 0);    // karanlıkta parlak, aydınlıkta sönük / bright in the dark, dim in the light
11  pwm = constrain(pwm, 0, 255);             // aralık dışı değerleri kes / clip out-of-range values
12  analogWrite(LED_PIN, pwm);
13  delay(20);
14}

В этом примере фоторезистор (LDR) в комнате даёт значения от 200 до 800. Если ночью показание падает до 100, map() вычисляет 297; без constrain() analogWrite() получила бы это значение вне диапазона 0–255. На Uno такое значение в зависимости от пина даёт разные неожиданные результаты: на пинах 3, 5, 6 и 11, использующих 8-битные таймеры, 297 переполняется и ведёт себя как 41, так что в полной темноте светодиод почти гаснет вместо того, чтобы ярко гореть. Поскольку на Uno constrain(), min(), max() и abs() определены как макросы, не пиши внутри скобок x++ или другой вызов функции: выражение может вычисляться несколько раз и давать неожиданные результаты. О подключении фоторезистора читай в статье Фоторезистор (LDR).

tone() и noTone(): звук с зуммером

tone(pin, frequency, duration) генерирует на пине меандр с заполнением 50% и заставляет пассивный зуммер или небольшой динамик звучать на этой частоте. Длительность задаётся в миллисекундах и не обязательна; без неё звук продолжается, пока не вызовешь noTone(pin). Важная деталь: tone() не ждёт; она запускает звук и сразу переходит к следующей строке. Поэтому, играя ноты одну за другой, после каждого вызова tone() нужна delay() не короче ноты, иначе услышишь только последнюю ноту.

C++ (Arduino IDE)
1const int BUZZER_PIN = 8;   // pasif buzzer'ın + ucu / passive buzzer + leg
2
3void setup() {
4  int notalar[] = {262, 294, 330, 349, 392};   // Do Re Mi Fa Sol (Hz) / C D E F G (Hz)
5  for (int i = 0; i < 5; i++) {
6    tone(BUZZER_PIN, notalar[i], 200);         // 200 ms çal / play for 200 ms
7    delay(250);                                // tone() beklemez, sen bekle / tone() does not wait, so you do
8  }
9  noTone(BUZZER_PIN);                          // sesi kapat / stop the sound
10}
11
12void loop() {
13}
  • Пределы частоты: на Uno тоны ниже 31 Hz не генерируются, а верхний предел — 65 535 Hz; полезный диапазон с зуммером — примерно 100–5000 Hz.
  • Один звук за раз: тон может играть только на одном пине одновременно; чтобы издать звук на другом пине, сначала останови предыдущий через noTone().
  • Конфликт с PWM: на Uno tone() использует Timer2, поэтому пока звучит звук, analogWrite() на пинах 3 и 11 сбивается.
  • Активный зуммер: у него свой генератор, поэтому его частоту через tone() не изменить; активный зуммер просто включай и выключай через digitalWrite(). О различиях читай в статье Что такое зуммер?

pulseIn(): измеряем длительность импульса

pulseIn(pin, HIGH) ждёт, пока пин уйдёт в HIGH, измеряет, сколько он остаётся в HIGH, и возвращает результат в микросекундах; можно измерять импульсы длительностью от 10 µs до 3 минут. Самое известное применение — ультразвуковой датчик расстояния HC-SR04: датчик выдаёт импульс длиной во время, которое звук тратит на путь туда и обратно. Звук распространяется в воздухе со скоростью около 343 м/с, или 0,0343 см/µs; поскольку путь двойной, расстояние = длительность × 0.0343 / 2.

C++ (Arduino IDE)
1const int TRIG_PIN = 9;
2const int ECHO_PIN = 10;
3
4void setup() {
5  Serial.begin(9600);
6  pinMode(TRIG_PIN, OUTPUT);
7  pinMode(ECHO_PIN, INPUT);
8}
9
10void loop() {
11  digitalWrite(TRIG_PIN, LOW);
12  delayMicroseconds(2);
13  digitalWrite(TRIG_PIN, HIGH);            // 10 µs tetik darbesi / 10 µs trigger pulse
14  delayMicroseconds(10);
15  digitalWrite(TRIG_PIN, LOW);
16
17  unsigned long sure = pulseIn(ECHO_PIN, HIGH, 30000UL);   // en fazla 30 ms bekle / wait at most 30 ms
18  if (sure == 0) {
19    Serial.println(F("Menzil dışı / out of range"));
20  } else {
21    float mesafe = sure * 0.0343 / 2.0;    // ses hızı 343 m/s, gidiş-dönüş / speed of sound 343 m/s, round trip
22    Serial.print(mesafe, 1);
23    Serial.println(F(" cm"));
24  }
25  delay(100);
26}

Третий параметр, таймаут, очень важен. Без него pulseIn() при отсутствии импульса ждёт 1 секунду и возвращает 0; если провод датчика отошёл, вся программа на каждом проходе цикла замирает на секунду. Таймаут 30000 µs соответствует примерно 5 метрам, чего хватает для дальности HC-SR04 в 4 метра. О подключении и точности датчика читай в статье Датчик расстояния HC-SR04.

attachInterrupt(): мгновенная реакция через прерывания

attachInterrupt(digitalPinToInterrupt(pin), function, mode) запускает указанную функцию в момент изменения пина, чем бы ни был занят loop() в это время. Такая функция называется обработчиком прерывания (ISR). Режим может принимать четыре значения: RISING срабатывает на переход из LOW в HIGH, FALLING — из HIGH в LOW, CHANGE — на любое изменение, а LOW — всё время, пока на пине LOW. На Uno и Nano так вызывать прерывания могут только пины D2 и D3; на Mega — пины 2, 3, 18, 19, 20 и 21. Это идеально, чтобы не пропустить быстро вращающийся энкодер, импульсы анемометра или очень короткое нажатие кнопки.

C++ (Arduino IDE)
1const int BUTON_PIN = 2;            // Uno'da kesme pinleri: 2 ve 3 / interrupt pins on the Uno: 2 and 3
2volatile unsigned long sayac = 0;   // ISR'de değişen değişken volatile olmalı / variables changed in an ISR must be volatile
3
4void say() {                        // kesme fonksiyonu (ISR): kısa tut / interrupt service routine: keep it short
5  sayac++;
6}
7
8void setup() {
9  Serial.begin(9600);
10  pinMode(BUTON_PIN, INPUT_PULLUP);
11  attachInterrupt(digitalPinToInterrupt(BUTON_PIN), say, FALLING);   // HIGH'dan LOW'a geçişte / on a HIGH-to-LOW edge
12}
13
14void loop() {
15  noInterrupts();                   // 4 baytlık değeri güvenle kopyala / copy the 4-byte value safely
16  unsigned long kopya = sayac;
17  interrupts();
18
19  Serial.print(F("Basma sayısı / presses: "));
20  Serial.println(kopya);
21  delay(500);
22}
Объявляй каждую переменную, которая меняется внутри ISR, как volatile, и делай функцию как можно короче. Внутри ISR delay() не работает, millis() не идёт, а Serial.print() может подвесить программу; просто обнови флаг или счётчик, а настоящую работу делай в loop(). Механические кнопки дребезжат, поэтому за одно нажатие счётчик может увеличиться несколько раз; как бороться с дребезгом, и все подробности о прерываниях — в статье Прерывания в Arduino.

random() и randomSeed(): генерация случайных чисел

random(max) возвращает случайное целое число от 0 до max−1, а random(min, max) — от min до max−1; верхняя граница в результат не входит. Поэтому для кубика от 1 до 6 пишут random(1, 7). Arduino не создаёт настоящую случайность; она вычисляет последовательность чисел по фиксированной формуле, так что если не вызвать randomSeed(), при каждом запуске платы будут те же числа в том же порядке. На практике это решается шумом неподключённого аналогового пина в качестве зерна:

C++ (Arduino IDE)
1void setup() {
2  Serial.begin(9600);
3  randomSeed(analogRead(A3));     // boştaki A3 pininin gürültüsü / noise from the unconnected A3 pin
4}
5
6void loop() {
7  int zar = random(1, 7);         // 1-6 arası; üst sınır dahil değil / 1 to 6; the upper bound is exclusive
8  Serial.print(F("Zar / dice: "));
9  Serial.println(zar);
10  delay(1000);
11}

Команды Arduino на ESP32: на что обратить внимание

ESP32 программируется в Arduino IDE теми же командами, и большая часть кода из этой статьи работает сразу, стоит лишь поменять номера пинов. Но из-за другого железа некоторые команды ведут себя иначе. Главное отличие — напряжение: пины ESP32 работают на 3,3 V, digitalWrite(pin, HIGH) выдаёт 3,3 V, а сигналы 5 V подключать к пинам напрямую нельзя. Чтобы лучше узнать плату, загляни в статью Что такое ESP32?

КомандаArduino UnoESP32
digitalWrite(pin, HIGH)5 V3,3 V
analogRead()10 бит, 0–1023, A0–A512 бит, 0–4095; при включённом Wi-Fi только пины ADC1 (GPIO 32–39)
analogWrite()Только пины ~ (3, 5, 6, 9, 10, 11), 490/980 HzПочти любой GPIO, способный работать на выход; частота задаётся через ledcAttach(pin, freq, resolution)
attachInterrupt()Только D2 и D3Все GPIO; добавь к ISR IRAM_ATTR
tone()Использует Timer2, сбивает PWM на 3 и 11Работает на аппаратном блоке LEDC
Serial.begin()Обычно 9600Обычно 115200 (на этой скорости идут и сообщения загрузки)
delay()Полностью занимает процессор ожиданиемОтдаёт время FreeRTOS; Wi-Fi продолжает работать в фоне
C++ (Arduino IDE)
1const int POT_PIN = 34;     // ADC1 pini, Wi-Fi açıkken de okur / ADC1 pin, works with Wi-Fi on
2const int LED_PIN = 2;      // çoğu DevKit'te dahili LED / onboard LED on most DevKits
3const int BUTON_PIN = 4;    // butonun diğer ucu GND / other button leg to GND
4
5volatile bool basildi = false;
6
7void IRAM_ATTR butonISR() { // ESP32'de ISR IRAM'e yerleşmeli / on the ESP32 the ISR should live in IRAM
8  basildi = true;
9}
10
11void setup() {
12  Serial.begin(115200);
13  pinMode(BUTON_PIN, INPUT_PULLUP);
14  attachInterrupt(digitalPinToInterrupt(BUTON_PIN), butonISR, FALLING);   // her GPIO kesme destekler / every GPIO supports interrupts
15}
16
17void loop() {
18  int ham = analogRead(POT_PIN);                     // 0-4095 (12 bit)
19  analogWrite(LED_PIN, map(ham, 0, 4095, 0, 255));   // 8 bit PWM / 8-bit PWM
20  if (basildi) {
21    basildi = false;
22    Serial.println("Butona basıldı / button pressed");
23  }
24  Serial.println(ham);
25  delay(200);
26}

Поскольку analogRead() на ESP32 возвращает 0–4095, диапазон map() меняется соответственно. IRAM_ATTR перед ISR помещает код в быструю внутреннюю память; без него плата в некоторых ситуациях может перезагрузиться с ошибкой «Guru Meditation Error». Аналоговые показания ESP32 нелинейны у краёв диапазона (около 0 V и 3,3 V); если нужно более точное измерение, функция analogReadMilliVolts() возвращает откалиброванное значение в милливольтах.

Raspberry Pi Pico: аналоги команд Arduino на MicroPython

Если программировать Raspberry Pi Pico в Arduino IDE (ядро arduino-pico), все команды из этой статьи работают под теми же именами; analogRead() по умолчанию возвращает 10 бит, а analogWrite() даёт PWM 1 kHz. Однако большинство используют Pico с MicroPython. Там команды пишутся через объекты модулей machine и time; логика та же, меняется только синтаксис. Пины Pico тоже на 3,3 V, а аналоговые входы — пины GP26, GP27 и GP28. Для начала загляни в статью Что такое Raspberry Pi Pico?

Команда ArduinoАналог на MicroPythonПримечание
pinMode(15, OUTPUT)led = Pin(15, Pin.OUT)Объект Pin создаётся один раз
pinMode(14, INPUT_PULLUP)btn = Pin(14, Pin.IN, Pin.PULL_UP)Встроенный подтягивающий резистор
digitalWrite(15, HIGH)led.value(1)Работает и led.on()
digitalRead(14)btn.value()Возвращает 0 или 1
analogRead(A0)ADC(26).read_u16()Возвращает 0–65535
analogWrite(15, 128)PWM(Pin(15)).duty_u16(32768)Заполнение от 0 до 65535
delay(500)time.sleep_ms(500)Работает и time.sleep(0.5)
millis()time.ticks_ms()Для разности используй time.ticks_diff()
Serial.println(x)print(x)Выводится в оболочке Thonny
tone(pin, 440)pwm.freq(440) и pwm.duty_u16(32768)Заглушить: duty_u16(0)
pulseIn(pin, HIGH, 30000)machine.time_pulse_us(pin, 1, 30000)При таймауте возвращает отрицательное значение
attachInterrupt(...)btn.irq(trigger=Pin.IRQ_FALLING, handler=f) (по спаду сигнала)f получает объект пина как параметр
random(1, 7)random.randint(1, 6)Верхняя граница входит
map() / constrain()Встроенных нет, пишутся вручнуюДля constrain: min(max(x, 0), 255)
MicroPython
1from machine import Pin, ADC, PWM
2import time
3
4led = PWM(Pin(15))                       # pinMode + analogWrite karşılığı / pinMode + analogWrite equivalent
5led.freq(1000)                           # 1 kHz PWM
6buton = Pin(14, Pin.IN, Pin.PULL_UP)     # INPUT_PULLUP karşılığı / INPUT_PULLUP equivalent
7pot = ADC(26)                            # GP26 = ADC0
8
9def map_deger(x, giris_min, giris_max, cikis_min, cikis_max):   # Arduino map() karşılığı / Arduino map() equivalent
10    return (x - giris_min) * (cikis_max - cikis_min) // (giris_max - giris_min) + cikis_min
11
12onceki = time.ticks_ms()                 # millis() karşılığı / millis() equivalent
13while True:
14    ham = pot.read_u16()                 # 0-65535 (analogRead)
15    led.duty_u16(ham)                    # parlaklık / brightness (analogWrite)
16    if buton.value() == 0:               # digitalRead; basılı = 0 / pressed = 0
17        print("Butona basıldı / button pressed")
18    if time.ticks_diff(time.ticks_ms(), onceki) >= 1000:   # saniyede bir / once a second
19        onceki = time.ticks_ms()
20        print("Yüzde / percent:", map_deger(ham, 0, 65535, 0, 100))
21    time.sleep_ms(50)                    # delay(50)

Как видно из таблицы, random.randint(1, 6) в MicroPython включает верхнюю границу и даёт тот же результат, что random(1, 7) на Arduino; time.ticks_diff() обеспечивает ту же защиту от переполнения, что и вычитание с millis(). Именно эти мелкие различия больше всего путают при переходе между двумя платформами. О других базовых командах MicroPython читай в статье Основы MicroPython.

Идеи проектов на этих командах

  • Светофор: зажигай по очереди три светодиода с помощью pinMode(), digitalWrite() и delay(); пошагово объясняем в статье Светофор на Arduino.
  • Электронный кубик: генерируй число от 1 до 6 через random() и при нажатии кнопки показывай результат светодиодами или на 7-сегментном индикаторе.
  • Парктроник: измеряй расстояние через pulseIn(), превращай его в интервал сигнала через map() и подавай предупреждение через tone().
  • Автоматический ночник: читай уровень освещённости через analogRead() и задавай яркость analogWrite() с помощью map() и constrain().
  • Пульт через последовательный порт: управляй светодиодом, реле или сервоприводом командами с компьютера через Serial.read().
  • Тест реакции: зажигай светодиод в случайный момент через random() и измеряй время своей реакции на кнопку с точностью до миллисекунды через attachInterrupt() и millis().

Частые ошибки и как их исправить

ОшибкаСимптомРешение
Забыт pinMode()Светодиод очень тусклый или вовсе не горитНапиши pinMode(pin, OUTPUT) в setup()
Кнопка без резистора в режиме INPUTdigitalRead() возвращает случайные 0 и 1Используй INPUT_PULLUP; при нажатии читается LOW
Опечатка в имени команды'digitalwrite' was not declared in this scope (имя не объявлено)Следи за регистром букв: digitalWrite
Не совпадает скоростьНепонятные символы в мониторе портаВыставь в мониторе порта ту же скорость, что в Serial.begin()
analogWrite() на пине без PWMСветодиод только полностью горит или полностью гаснетИспользуй пин со знаком ~ (3, 5, 6, 9, 10, 11)
millis() хранится в intОтсчёт времени ломается примерно через 33 секундыИспользуй unsigned long
Кубик на random(1, 6)Шестёрка (6) никогда не выпадаетПиши random(1, 7); верхняя граница не входит
Ожидание дробей от map()Результат всегда целый, мелкие изменения теряютсяПри необходимости напиши свою формулу с float
pulseIn() без таймаутаБез датчика каждый проход цикла замирает на 1 секундуПередай 30000 третьим параметром
delay() или Serial внутри ISRПрограмма зависает или ведёт себя странноВ ISR меняй только флаг volatile
Если неясно, что именно делает команда, напиши маленький тестовый скетч и выведи результат через Serial.println(); в Arduino IDE 2 можно ещё щёлкнуть правой кнопкой по имени команды и выбрать «Перейти к определению» (Go to Definition), чтобы увидеть её исходный код. Если код не компилируется, первая строка сообщения об ошибке обычно указывает на проблему; самые частые сообщения и способы их исправить — в статье Ошибки компиляции Arduino.

Команды из этой статьи — строительные блоки большинства проектов на Arduino; остальное — в основном дополнительные функции из библиотек. Если попробовать каждую команду в своей маленькой схеме и увидеть результат в мониторе порта, они запомнятся гораздо лучше, чем при заучивании. В приложении BlueGrays есть проекты, использующие эти команды: зажигание светодиода, управление светодиодом кнопкой, измерение света фоторезистором, настройка частоты зуммера, измерение расстояния HC-SR04 и кубик — пошагово, со схемами и кодом с комментариями.

Частые вопросы

Какие команды Arduino используются чаще всего?

Чаще всего используются pinMode(), digitalWrite(), digitalRead(), analogRead(), analogWrite(), delay(), millis(), Serial.begin() и Serial.println(). Добавь map() для масштабирования значений, tone() для звука, pulseIn() для датчиков расстояния и attachInterrupt() для прерываний — и можно написать почти все проекты для начинающих.

На каком языке пишутся команды Arduino?

Код Arduino построен на C и C++; ядро Arduino добавляет в язык готовые функции вроде pinMode() и digitalWrite(). Команды на английском, чувствительны к регистру, а каждая инструкция заканчивается точкой с запятой. ESP32 и Raspberry Pi Pico при желании можно программировать и на MicroPython.

Чем отличаются digitalWrite и analogWrite?

digitalWrite() ставит пин в одно из двух состояний: HIGH (5 V на Uno) или LOW (0 V). analogWrite() очень быстро включает и выключает пин, создавая сигнал PWM от 0 до 255, так что можно регулировать яркость светодиода или скорость мотора. На Uno analogWrite() работает только на пинах, отмеченных знаком ~.

Что означают void setup и void loop?

setup() — функция, которая выполняется один раз при включении или сбросе платы; сюда идёт подготовка вроде pinMode() и Serial.begin(). loop() — функция, которая бесконечно повторяется после завершения setup(). Слово void перед ними означает, что эти функции не возвращают значения.

Чем отличаются Serial.print и Serial.println?

Serial.print() выводит данные и оставляет курсор на той же строке, так что следующие данные добавляются сразу за ними. Serial.println() после вывода отправляет символы конца строки (\r\n), так что следующие данные начинаются с новой строки. Чтобы показать несколько значений рядом в одной строке, сначала используй print(), а в конце — println().

Что делает функция map в Arduino?

map() пропорционально переводит число из одного диапазона в другой; например, превращает значение 0–1023 от analogRead() в диапазон 0–255 для analogWrite() или в 0–180 градусов для сервопривода. Она работает только с целыми числами и не ограничивает результат, поэтому её рекомендуют сочетать с constrain(), чтобы обрабатывать входы вне диапазона.

Все проекты
Мигающий светодиод
#1Новичок

Мигающий светодиод

Самое первое «привет» в электронике: включение и выключение одного светодиода. Arduino включает и выключает светодиод через короткие промежутки, поэтому свет быстро мигает. Ты самым простым способом в

4.6(188)
1127
Управление светодиодом кнопкой (удержание)
#9Новичок

Управление светодиодом кнопкой (удержание)

Как дверной звонок: свет горит, пока ты нажимаешь, и гаснет, когда отпускаешь. Arduino всё время слушает кнопку; пока она нажата, светодиод горит, а как только ты убираешь палец, он гаснет. Эта просте

4.8(113)
229
Измерение освещённости с LDR
#11Новичок

Измерение освещённости с LDR

Хочешь увидеть яркость своей комнаты в виде числа? LDR — маленькая деталь, реагирующая на свет, — измеряет освещённость вокруг, а Arduino каждые полсекунды выводит значение на экран компьютера. Закрой

4.6(81)
139
Настройка частоты зуммера
#14Средний

Настройка частоты зуммера

Ты управляешь звуком зуммера четырьмя кнопками: одна включает звук, другая выключает, а две оставшиеся делают его выше или ниже. Удерживай кнопку вверх или вниз, и высота звука меняется шаг за шагом,

4.6(69)
118
Измерение расстояния HC-SR04 (вывод в порт)
#27Новичок

Измерение расстояния HC-SR04 (вывод в порт)

Датчик HC-SR04 работает совсем как летучая мышь, которая находит дорогу в темноте: он посылает звук, которого ты не слышишь, слушает эхо и измеряет, насколько далеко предмет перед ним. Arduino каждые

4.6(46)
71
Светодиодный эффект Knight Rider с регулируемой скоростью
#8Средний

Светодиодный эффект Knight Rider с регулируемой скоростью

Свет бежит от одного края шести светодиодов к другому, разворачивается и бежит обратно. Поверни ручку потенциометра, и скорость этого бега сразу изменится: в одном крайнем положении свет почти летит,

4.5(60)
120
Управление сервоприводом потенциометром
#36Новичок
Premium

Управление сервоприводом потенциометром

Когда ты поворачиваешь ручку потенциометра, рычаг сервопривода поворачивается вместе с ней: одно крайнее положение ручки — ноль градусов, другое — пол-оборота. Arduino всё время читает положение ручки

4.6(37)
52
Игральный кубик
#79Средний
Premium

Игральный кубик

Больше никаких потерянных кубиков во время настольных игр: при каждом нажатии кнопки на семисегментном индикаторе появляется случайное число от 1 до 6. Каждое нажатие даёт ровно один результат, и он т

5.0(48)
67