Главная chevron_right Уроки chevron_right Программирование chevron_right Использование функций в скетче Arduino

Использование функций в скетче Arduino

Разбираемся, как создавать и использовать собственные функции в скетче Arduino — с примерами, пояснениями и типичными ошибками.

Разбивка кода на функции — один из ключевых навыков программирования. Функция — это именованный блок кода, который выполняет определённую задачу и возвращает управление туда, откуда был вызван. Чаще всего функции создают тогда, когда одно и то же действие нужно повторить несколько раз в разных местах программы.

Если вы раньше программировали на BASIC, то функции в Arduino — это аналог подпрограмм (GOSUB), только значительно мощнее и гибче.

Зачем выносить код в функции

  • Порядок в коде. Функции помогают структурировать программу и мыслить о ней по частям, а не как о едином потоке инструкций.
  • Одно место — одна задача. Логику нужно продумать и отладить только один раз, а не в каждом месте, где она используется.
  • Меньше ошибок при изменениях. Если нужно что-то исправить, достаточно поправить одну функцию, а не искать все её копии по всему скетчу.
  • Компактность. Повторно используемые блоки кода сокращают размер скетча.
  • Переносимость. Хорошо написанную функцию легко скопировать в другой проект.
  • Читаемость. Код с функциями читается почти как описание алгоритма на человеческом языке.

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

Анатомия функции

Диаграмма «Анатомия функции C»: тип возвращаемого значения (любой тип данных C; void, если ничего не возвращается), имя функции, параметры (любой тип данных C), оператор return (тип совпадает с объявлением) и обязательные фигурные скобки.

Разберём структуру по элементам:

  • Тип возвращаемого значения — указывает, какой тип данных вернёт функция. Если функция ничего не возвращает, пишем void.
  • Имя функции — произвольное, но осмысленное. Называйте функции так, чтобы было понятно, что они делают.
  • Параметры в скобках — данные, которые передаются в функцию. Если параметров нет, скобки всё равно обязательны: ().
  • Тело функции в фигурных скобках {} — сам код.
  • return — возвращает результат. Тип значения должен совпадать с объявленным типом функции.

Пример: функция умножения

Создадим простую функцию myMultiplyFunction, которая принимает два числа и возвращает их произведение.

Чтобы вызвать функцию, передаём ей аргументы нужного типа:

void loop(){
int i = 2;
int j = 3;
int k;

k = myMultiplyFunction(i, j); // теперь k содержит 6
}

Функцию myMultiplyFunction() нужно объявить вне loop() и setup() — до или после них, на ваш выбор. Полный скетч выглядит так:

void setup(){
  Serial.begin(9600);
}

void loop() {
  int i = 2;
  int j = 3;
  int k;

  k = myMultiplyFunction(i, j); // теперь k содержит 6
  Serial.println(k);
  delay(500);
}

int myMultiplyFunction(int x, int y){
  int result;
  result = x * y;
  return result;
}

Как проверить результат

Откройте Serial Monitor (скорость 9600 бод) и убедитесь, что в консоль выводятся ожидаемые произведения чисел. Если вывода нет — проверьте, вызвана ли Serial.begin() в setup().

Ещё один пример: обработка показаний датчика

Эта функция читает датчик пять раз с помощью analogRead(), вычисляет среднее значение из пяти измерений, масштабирует результат до 8 бит (диапазон 0–255) и инвертирует его.

int ReadSens_and_Condition(){
  int i;
  int sval = 0;

  for (i = 0; i < 5; i++){
    sval = sval + analogRead(0);    // датчик на аналоговом пине 0
  }

  sval = sval / 5;    // среднее значение
  sval = sval / 4;    // масштабирование до 8 бит (0 - 255)
  sval = 255 - sval;  // инвертировать выход
  return sval;
}

Вызов функции — просто присвоение переменной:

int sens;

sens = ReadSens_and_Condition();

Обратите внимание: даже если функция не принимает параметров и ничего не возвращает, скобки () и точка с запятой ; при вызове обязательны.

Типичные ошибки

  • Объявление функции внутри loop() или setup() — так делать нельзя. Arduino (и C++ в целом) не поддерживает вложенные функции. Компилятор выдаст ошибку.
  • Несоответствие типов. Если функция объявлена как int, а вы пытаетесь вернуть из неё float, данные будут обрезаны без предупреждения.
  • Забытый return. Если функция должна что-то возвращать, но return отсутствует — поведение непредсказуемо.
  • Пропущенные скобки при вызове. Написать myFunction; вместо myFunction(); — это не вызов функции, а просто ссылка на её адрес. Код скомпилируется, но функция не выполнится.

Практические замечания

  • Давайте функциям говорящие имена: readTemperature() лучше, чем func1().
  • Если функция используется только в одном месте, но делает что-то сложное — всё равно выносите её отдельно. Это упростит отладку.
  • Функции можно вызывать из других функций — это нормальная практика.
  • Порядок объявления функций в файле .ino не важен: Arduino IDE автоматически создаёт прототипы функций перед компиляцией.