Урок 2. Структура скетча: setup(), loop() и комментарии

В прошлом уроке (ссылка выше) мы скачали IDE, подключили плату и даже загрузили первый пример Blink. Теперь давайте разберёмся, из чего вообще состоит любая программа для Arduino и как она работает.

НАЧИНАЕМ ПРОГРАММИРОВАТЬ ARDUINO — основные понятия

Программа на Arduino называется скетчем, по-английски «sketch». Программа, код, скрипт, скетч — синонимы. У любого скетча есть строгая структура, и если её нарушить — программа-скетч просто не скомпилируется. Но хорошая новость в том, что эта структура очень простая и запоминается раз и навсегда.

Любой скетч состоит из двух основных частей: функция setup(), функция loop(). Всё остальное — это уже детали, переменные и библиотеки, которые мы будем изучать позже. А пока разберём костяк.

СТРУКТУРА СКЕТЧА

В скетчах есть очень важная и удобная штука — комментарии. Это способ оставить заметки в коде, которые не будут восприниматься как код при компиляции.

Если нужен однострочный комментарий, то комментарий обозначается двумя слешами — //
Если комментарий нужен на много строк, то весь текст комментария оборачивается в слеши и звездочки — /* здесь пишем комментарий */

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

// Однострочный комментарий. Начинается с двух слешей, и после них в этой строке можно писать что угодно на любом языке.

/*
   Многострочный комментарий.
   Такие используют, если нужно закомментировать много строк текста или кода.
   digitalWrite(5, HIGH); - этот код не будет выполняться, потому что он внутри комментария.
*/

pinMode(3, INPUT); // Можно комментарий написать в конце строки после кода, код будет работать и комментарий не помешает.

void setup()

Начнём с первой части любой программы на Arduino — функции setup(). Эта функция выполняется всего один раз, сразу после включения микроконтроллера или после нажатия кнопки сброса. Внутри неё вы пишете всё, что должно произойти в самом начале: настройка пинов, инициализация датчиков и прочих модулей, установка начальных значений переменных, открытие Serial-порта.

Функция setup() всегда пишется с маленькой буквы, после неё ставятся круглые скобки, а весь код внутри заключается в фигурные скобки { … }. Запомните это раз и навсегда — именно так выглядят все функции в языке Arduino. Если вы забудете фигурные скобки или поставите точку с запятой не там или не поставите где нужно, программа выдаст ошибку.

void setup(){
   // Здесь будем писать функции инициализации библиотек, обозначения пинов и т.д.
   // Обычно в функции SETUP пишут такой код:
   pinMode(3, OUTPUT); // Говорим программе, что пин D3 платы будет использоваться в режиме вывода сигнала - OUT put.
   Serial.begin(9600); // Говорим программе, что мы будем использовать передачу данных по Serial (технология передачи данных по двум проводам - UART, Serial).
   int svetodiod = 5; // Можно создать переменную
}

void loop()

Вторая важнейшая функция — это loop(). Её название происходит от английского слова «цикл», и она действительно работает как бесконечный цикл. После того как setup() отработала один раз, управление передаётся в loop(), и эта функция выполняется снова и снова, пока плата включена.

Именно внутри loop() вы пишете основную логику вашего устройства: считываете показания датчиков, обрабатываете нажатия кнопок, управляете светодиодами и отправляете данные. Как только loop() доходит до конца, она запускается заново — и так бесконечно.

Визуально вся программа выглядит так: сверху могут быть комментарии и объявления переменных, затем идёт блок setup(), а после него — блок loop(). Больше ничего не нужно для того, чтобы написать работающий скетч. Всё остальное — это уже расширения и усложнения, которые мы будем изучать постепенно.

Давайте посмотрим на реальный пример (блок с кодом ниже). Откройте уже знакомый вам Blink и посмотрите на него внимательно. В начале вы увидите комментарий с описанием, потом идёт setup(), в котором один пин настраивается на выход, а после — loop(), где светодиод включается, ждёт секунду, выключается и снова ждёт.

Вот скетч-пример — 01. Blink. Я добавил перевод оригинальных комментариев:

/*
  Blink - Мигание

  Turns an LED on for one second, then off for one second, repeatedly.
  Включает светодиод на одну секунду, затем выключает на одну секунду, и так повторяется бесконечно.

  Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO
  it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to
  the correct LED pin independent of board which is what you should use.
  На многих ардуинах есть светодиод, которым можно управлять. На ардуино уно, мега и зеро
  светодиод подключен к пину 13, на плате MKR1000 - к пину 6. Для простоты использования этого светодиода
  в среде Arduino IDE добавлена константа LED_BUILTIN, в которой записан номер пина, к которому подключен
  светодиод на выбранной в настройках плате.

  The Arduino Nano 33 BLE Sense has an internal RGB LED, but you can connect an
  external LED to any digital pin.
  На Arduino Nano 33 BLE Sense есть встроенный трёхцветный RGB светодиод,
  но этот скетч может управлять одноцветным светодиодом на любом пине.

  modified 8 May 2014
  by Scott Fitzgerald
  modified 2 Sep 2016
  by Arturo Guadalupi
  modified 8 Sep 2016
  by Colby Newman

  This example code is in the public domain.
  Этот скетч-пример доступен на официальном сайте Arduino.

  https://www.arduino.cc/en/Tutorial/BuiltInExamples/Blink
*/

// the setup function runs once when you press reset or power the board - в функции настроек, setup(), настраиваем пин со светодиодом на выход сигнала
void setup() {
  // initialize digital pin LED_BUILTIN as an output. - включаем цифровой пин LED_BUILTIN на выход сигнала
  pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever - цикличная функция loop() крутится в цикле по кругу, бесконечно
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level) - включаем светодиод, подаём напряжение на пин
  delay(1000);                       // wait for a second - ждём секунду, то есть одну тысячу миллисекунд. 1 сек - 1000 мсек
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off (LOW is the voltage level) - выключаем светодиод, убирая напряжение
  delay(1000);                       // wait for a second - ждём секунду.
}

Теперь вы понимаете каждую строчку этого кода! Вы знаете, почему светодиод мигает именно так, и можете легко изменить скорость мигания, поменяв цифры в delay(). Это и есть понимание структуры — вы больше не просто копируете код, вы начинаете его читать и понимать.

Попробуйте поэкспериментировать: поменяйте значения в delay(), добавьте свой комментарий, можете создать новую переменную и указать в ней номер пина, можете подключить внешний светодиод, обязательно через резистор 220-1000 ом. Не бойтесь ошибаться с кодом — ошибки в Arduino IDE подсвечиваются красным, и программа не даст загрузить кривой код на плату. Но с подключением компонентов будьте осторожны! Удачи!

Лайфхак — если в скетче вместо LED_BUILTIN написать 1 или 0, то будет мигать один из двух других светодиодов на плате. Если указать пин 0, то будет мигать светодиод с подписью RX, если 1 — то TX. Но так можно побаловаться, только если не используете Serial общение с платой, так как сигналы идут именно на пины 0 и 1 и сигналы будут путаться и ничего не заработает. Короче, просто в почти пустом скетче можно побаловаться.

Прикольный скетч ниже. Мигание всех трёх светодиодов на плате, которыми можно управлять, на пинах 0, 1 и 13. Правда, после прошивки этого скетча, для следующей прошивки надо будет нажать кнопку RESET на плате, затем отпустить, когда начнется загрузка, чтобы микроконтроллер перестал посылать сигналы на пины 0 и 1 и не мешал загрузке.

/*
  Мигаем тремя встроенными светодиодами:
  - L (пин 13)  -> зажигается от HIGH
  - RX (пин 0)  -> зажигается от LOW
  - TX (пин 1)  -> зажигается от LOW
  
  Эффекты: 1) все сразу, 2) волна вперёд, 3) волна назад
*/

// Пины встроенных светодиодов
#define LED_L   13  // Встроенный светодиод (зажигается от HIGH)
#define LED_RX  0   // RX светодиод (зажигается от LOW)
#define LED_TX  1   // TX светодиод (зажигается от LOW)

// Массив пинов для удобства перебора
byte pins[] = {LED_L, LED_RX, LED_TX};
int pinCount = 3;

// Тайминг
unsigned long effectStartTime = 0;
int currentEffect = 0;        // 0 - все сразу, 1 - волна вперёд, 2 - волна назад
int step = 0;
unsigned long effectDuration = 5000; // 5 секунд на эффект

void setup() {
  // Настраиваем все пины как выходы
  for (int i = 0; i < pinCount; i++) {
    pinMode(pins[i], OUTPUT);
  }
  
  // Гасим все при старте
  allOff();
  
  effectStartTime = millis();
}

void loop() {
  unsigned long currentTime = millis();
  
  // Переключение эффектов каждые 5 секунд
  if (currentTime - effectStartTime >= effectDuration) {
    effectStartTime = currentTime;
    currentEffect = (currentEffect + 1) % 3;
    step = 0;
    allOff();
  }
  
  switch (currentEffect) {
    case 0:
      effectAllTogether();
      break;
    case 1:
      effectWaveForward();
      break;
    case 2:
      effectWaveBackward();
      break;
  }
}

// ---- ВКЛЮЧЕНИЕ/ВЫКЛЮЧЕНИЕ с учётом полярности ----
void setLED(int pin, bool state) {
  if (pin == LED_L) {
    // L светодиод: HIGH = ВКЛ, LOW = ВЫКЛ
    digitalWrite(pin, state ? HIGH : LOW);
  } else {
    // RX и TX: LOW = ВКЛ, HIGH = ВЫКЛ (инвертированы)
    digitalWrite(pin, state ? LOW : HIGH);
  }
}

// ---- ПОГАСИТЬ ВСЕ ----
void allOff() {
  setLED(LED_L, false);
  setLED(LED_RX, false);
  setLED(LED_TX, false);
}

// ---- ЭФФЕКТ 1: Все сразу мигают ----
void effectAllTogether() {
  static bool ledState = LOW;
  static unsigned long lastToggle = 0;
  
  if (millis() - lastToggle >= 200) { // Мигаем каждые 200 мс
    lastToggle = millis();
    ledState = !ledState;
    
    setLED(LED_L, ledState);
    setLED(LED_RX, ledState);
    setLED(LED_TX, ledState);
  }
}

// ---- ЭФФЕКТ 2: Волна вперёд (L -> RX -> TX) ----
void effectWaveForward() {
  static unsigned long lastStep = 0;
  
  if (millis() - lastStep >= 150) { // Шаг каждые 150 мс
    lastStep = millis();
    
    // Гасим все
    allOff();
    
    // Зажигаем текущий светодиод
    switch (step) {
      case 0: setLED(LED_L, true); break;   // Сначала L
      case 1: setLED(LED_RX, true); break;  // Потом RX
      case 2: setLED(LED_TX, true); break;  // Потом TX
    }
    
    step++;
    if (step >= pinCount) {
      step = 0;
    }
  }
}

// ---- ЭФФЕКТ 3: Волна назад (TX -> RX -> L) ----
void effectWaveBackward() {
  static unsigned long lastStep = 0;
  
  if (millis() - lastStep >= 150) {
    lastStep = millis();
    
    allOff();
    
    // Зажигаем в обратном порядке
    switch (step) {
      case 0: setLED(LED_TX, true); break;  // Сначала TX
      case 1: setLED(LED_RX, true); break;  // Потом RX
      case 2: setLED(LED_L, true); break;   // Потом L
    }
    
    step++;
    if (step >= pinCount) {
      step = 0;
    }
  }
}

🙂

Похожие записи

Добавить комментарий