Эксперимент №4. Светофор на Ардуино

Вступление

Становится уже интереснее. Давайте соберем модель светофора!

В этом эксперименте не будет новых знаний по Ардуино, зато мы изучим пару новых фишек программирования.

Давайте решим как должен работать наш светофор:

  • Предположим, сначала горит зеленый.
  • Затем, когда его время истекает, он начинает мигать; предположим, что с интервалом в 1 сек. 3 раза.
  • Потом ненадолго зажигается желтый (1сек).
  • И включается красный.
  • Спустя несколько секунд цикл повторяется

Необходимые компоненты

  • 3 LED M5: красный, зеленый и желтый
  • 3 резистора на 220R

Схема

Соберем вот эту схему:

Схема сборки светофора на ардуино

Сборка аналогична опыту 2, но тут у нас уже 3 светодиода, и мы задействуем 3 цифровых порта. Каждый светодиод должен быть защищен своим резистором 220 Ом.

Обратите внимание на полярность светодиода: длинная ножка – это плюс, который подключается к цифрому порту, а короткая ножка соединяется с массой (GND).

Сопротивление ставится в разрыв цепи, все равно с какой стороны светодиода – плюса или земли. На схеме сопротивление поставлено со стороны земли (GND).

Вы можете использовать любые цифровые пины ардуино от 2 до 12. Сделайте это как вам удобней, но не забудьте скорректировать номера пинов в скетче.

Скетч

Наш первый скетч выглядит вот так:

/**
 * (C) jarduino.ru 2019
 * Изучение ардуино через опыты.
 *
 * Опыт №4 Светофор. Скетч №1
 * Независимое управление 3-мя светодиодами на примере работы светофора.
 */


// Глобальные константы
const int pinRedLed = 12; // красный подключен к порту 12
const int pinYellowLed = 9; // желтый подклчен к порту 9
const int pinGreenLed = 6; // зеленый подключен к порту 6

const int lightDelay = 2000; // продолжительность свечения зеленого и крассного
const int blinkDelay = 100; // полупериод мигания
const int blinkTimes = 3; // число миганий
const int delayMult = 1; // множитель, ускоряющий работу светофора в указанное здесь число раз

// настройка платы
void setup()
{
  // инициализировть цифровые порты светофора на вывод:
  pinMode(pinRedLed, OUTPUT);
  pinMode(pinYellowLed, OUTPUT);
  pinMode(pinGreenLed, OUTPUT);
}

// главный цикл программы
void loop()
{
  // сначала горит зеленый
  digitalWrite(pinGreenLed, HIGH);
  delay(lightDelay * delayMult);
  digitalWrite(pinGreenLed, LOW);

  // зеленый мигает и гаснет
  for (int i = 0; i < blinkTimes; ++i) // blinks for 3 times
  {
    delay(blinkDelay * delayMult);
    digitalWrite(pinGreenLed, HIGH);

    delay(blinkDelay * delayMult);
    digitalWrite(pinGreenLed, LOW);
  }

  // ненадолго зажигается желтый
  digitalWrite(pinYellowLed, HIGH);
  delay(200 * delayMult);
  digitalWrite(pinYellowLed, LOW);

  // зажигается красный
  digitalWrite(pinRedLed, HIGH);
  delay(lightDelay * delayMult);
  digitalWrite(pinRedLed, LOW);

  // красный мигает и гаснет
  for (int i = 0; i < blinkTimes; ++i) // blinks for 3 times
  {
    delay(blinkDelay * delayMult);
    digitalWrite(pinRedLed, HIGH);

    delay(blinkDelay * delayMult);
    digitalWrite(pinRedLed, LOW);
  }

  // ненадолго зажигается желтый
  digitalWrite(pinYellowLed, HIGH);
  delay(200 * delayMult);
  digitalWrite(pinYellowLed, LOW);
}

Результат

Светофор горит как задумано:

Модель светофора на ардуино.

Объяснение

В скетче даны подробные комментария.

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

Для простоты настройки и тестирования светофора, введен мультипликатор, коэффициент, с помощью которого можно изменять скорость работы светофора:

const float delayMultiply = 0.1; // число миганий

Цикл for

В скетче мы встретили новое ключевое слово языка C/С++ — for, с помощью которого нужная подпрограмма выполняется циклически заданное число раз. Рассмотрим ее пример:

for ( i = 0; i < 5; ++i) {
// …. Повторяемый код
}

i – это переменная, которая хранит номер текущего цикла. Назовем ее счетчиком. Счетчик может начинаться с любого числа. В данном случае он равен 0.

i < 5 задает условие, при котором цикл продолжается.

++ – это инкремент, т.е. увеличение значения на 1. Вместо этого инкремента может быть и другое выражение, которое изменяет счетчик циклов, например декремент —i. Также значения счетчика может быть изменено и внутри самой подпрограммы цикла.

Если мы хотим выйти из цикла, по условию, не выполняя все циклы, можно использовать оператор break («прерывать»), вот так:

  for (i = 0; i < 5; ++i) {
    Serial.println("Уроки Arduino на jarduino.ru");
    break;
  }

В данном случае, цикл будет выполнено всего 1 раз, поскольку в конце подпрограммы стоит принудительное завершение цикла с помощью оператора break.

Функции

Обратите внимание на большое число повторяющихся похожих блоков кода. Блоки отличаются лишь номером пина, и временем задержки.

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

Более правильным здесь будет написать пару функций.

Функция – это именованная подпрограмма, которая принимает входные параметры (обычно есть хотя бы один параметр), использует их внутри себя (в теле функции) и может возвращать выходные параметры.

Давайте сделаем две функции:

  • lightUp – зажигает заданный светодиод на заданное время.
  • blink – мигает заданным светодиодом заданное число раз с заданным интервалом.

Каждая из этих функций будет иметь входные параметры:

  • lightUp:
    • pinLed – пин светодиода
    • nDelay – продолжительность свечения светодиода
  • blink
    • pinLed – пин светодиода
    • nDelay — продолжительность свечения светодиода и, одновременно, продолжительность паузы до следующего включения.
    • nTimes – число циклов мигания

Все параметры будут целыми (int) константами (const). Запомните: если параметр функции или иная переменная не должна изменяться в ходе работы программы, настоятельно рекомендуется объявлять их как контенту, используя ключевое слово const. Это сократит число возможных ошибок в программе.

Для определения функции используется следующий шаблон:

<тип возвращаемого значения> <название функции>(<список параметров>) {
  <подпрограмма - тело функции>
}

Список параметров – это перечисление через запятую объявление переменных, которые одновременно будут параметрами данной функции.

О возвращаемых параметрах мы расскажем в следующих экспериментах. Наши функции не будут возвращать никакие параметры. В этом случае в качестве типа возвращаемого параметра надо указывать тип void.

Вот так будет выглядеть наша первая функция, зажигающая светодиод на заданное время:

// Функция зажигает заданный светодиод на заданное время и гасит его.
void lightUp(const int pinLed, const int nDelay)
{
  digitalWrite(pinLed, HIGH); //// turn on green LED
  delay(nDelay);
  digitalWrite(pinLed, LOW); // turn off green LED
}

Вот так мы ее используем:

lightUp(pinGrenLed, lightDelay); // зажечь светодиод, подключенный к пину pinGrenLed на lightDelay мсек.

Посмотрите как изменился наш скетч после введения функций:

/**
 * (C) jarduino.ru 2019
 * Изучение ардуино через опыты.
 *
 * Опыт №4.2 Светофор, продолжение
 * 
 * Использование функций для минимизации повторяющихся фрагментов
 */


// Глобальные константы
const int pinRedLed = 12; // красный подключен к порту 12
const int pinYellowLed = 9; // желтый подклчен к порту 9
const int pinGrenLed = 6; // зеленый подключен к порту 6

const int lightDelay = 5000; // продолжительность свечения зеленого и крассного
const int blinkDelay = 500; // полупериод мигания
const int yelloDelay = 1000; // полупериод мигания
const int blinkTimes = 3; // число миганий

const float delayMultiply = 0.1; // число миганий

// настройка платы
void setup()
{
  // инициализировть цифровые порты светофора на вывод:
  pinMode(pinRedLed, OUTPUT);
  pinMode(pinYellowLed, OUTPUT);
  pinMode(pinGrenLed, OUTPUT);
}

// Функция зажигает заданный светодиод на заданное время и гасит его.
void lightUp(const int pinLed, const int nDelay)
{
  digitalWrite(pinLed, HIGH); //// turn on green LED
  delay(nDelay);
  digitalWrite(pinLed, LOW); // turn off green LED
}

// Функция мигает светодиодом с заданным полупериодом заданное число раз
void blink(
  const int pinLed, // пин светодиода
  const int nDelay, // полупериод мигания
  const int nTimes // число миганий
)
{
  for (int i = 0; i < nTimes; ++i) // blinks for 3 times
  {
    delay(nDelay);
    lightUp(pinLed, nDelay);
  }
}

// Главный цикл программы
void loop()
{
  // Зажечь зеленый
  lightUp(pinGrenLed, lightDelay * delayMultiply);

  // Помигать зеленым
  blink(pinGrenLed, blinkDelay * delayMultiply, blinkTimes);

  // Ненадолго зажечь желтый
  lightUp(pinYellowLed, yelloDelay * delayMultiply);

  // Зажечь красный
  lightUp(pinRedLed, lightDelay * delayMultiply);
}

Стало не сколь прямолинейно, но зато гораздо гибче.

Заключение

Наша программа стала уже сложнее, а результат – интереснее.

Конечно, в реальной жизни светофор работает намного сложнее. Используя описанные здесь принципы, можно смоделировать работу любого светофора и применить его в дорожных симуляторах, в играх и при обучении детей правилам дорожного движения.

Здесь мы познакомились с такими концепциями программирования как циклы и функции и подготовились для создания чего-то более интересного.

Главная мысль этого урока: старайтесь минимизировать повторяющийся код. Это поможет в дальнейшей модернизации программы и в повторном использовании кода.

Помните, что основновной процесс написания программы – это именно непрерывный рефакторинг, т.е. постоянное улучшение программы. Модульность программы и устранение повторений – это первичные условия успешного рефакторинга в дальнейшем.

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *