Вы находитесь в самом начале Вашего удивительного путешествия в мир Arduino, и мы Вас с этим поздравляем! В этом руководстве мы на практике подробно рассмотрим, как работать с платой Arduino Uno, как писать под нее код и как настроить её взаимодействие с различными модулями и датчиками. Впереди нас ждут 15 увлекательных экспериментов разной степени сложности, поэтому, давайте не будем задерживаться и сразу перейдем к практике!

Предварительная подготовка

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

Разработка скетчей для Arduino ведется в программе, именуемом интегрированной средой разработки (IDE). Эту среду разработки предварительно нужно скачать и установить:

  1. Переходим по ссылке https://www.arduino.cc/en/Main/Software;
  2. Листаем до раздела Download the Arduino IDE;
  3. Выбираем нашу операционную систему (Windows, macOs или Linux);
  4. На следующей странице выбираем Just Download;
  5. Устанавливаем приложение;
  6. Готово! Вы восхитительны и готовы к программированию Arduino.

Настройка Arduino IDE

Как же нам теперь написать скетч и загрузить его в Arduino? Это очень и очень просто:

  1. Подключаем плату Arduino Uno к компьютеру кабелем, который идет в комплекте (на плате загорится светодиод, сообщая, что на плату поступает питание, она готова выполнять заложенные в нее команды и готова к загрузке скетча);
  2. Открываем установленную среду разработки Arduino;
  3. В верхней панели открываем Инструменты -> Плата -> Выбираем Arduino/Genuino Uno;
  4. В верхней панели открываем Инструменты -> Порт -> Выбираем Порт, к которому подключена наша плата (обычно напротив нужного порта будет стоять название подключенной платы);
  5. Пишем код нашего скетча в окне редактора;
  6. Проверяем наш скетч на ошибки;
  7. Загружаем скетч в плату;
  8. Готово! Наша плата Arduino прошита и сразу начнет выполнять загруженные в нее команды.

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

Библиотеки Arduino

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

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

  1. Скачиваем библиотеку на компьютер (ссылки на библиотеки будут указаны в экспериментах, где они нужны);
  2. В верхней панели Arduino IDE открываем Скетч -> Подключить библиотеку -> Добавить .ZIP библиотеку;
  3. Выберите загруженный ZIP-файл и нажмите кнопку Open (Открыть);
  4. Готово! Теперь вы можете пользоваться функционалом библиотеки в своем скетче.

Начинаем практику!

Вы уже научились как устанавливать и настраивать Arduino IDE, как загружать и устанавливать библиотеки, а так-же как прошивать плату Arduino. Этого Вам более чем достаточно, чтобы перейти к практикуму и собрать свой первый эксперимент. Давайте не будем долго задерживаться и наконец начнем!

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

Начнем с классического первого проекта Arduino: Мигающий светодиод. Мы не только убедимся в работоспособности платы Arduino, но и познакомимся с простейшем скетчем.

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

Наша первая программа будет включать светодиод на 1 секунду, а потом выключать на 1 секунду и так до бесконечности. Светодиод загорается, когда через него проходит ток с небольшим напряжением. Светодиод работает при постоянном токе, текущем в одном направлении от анода (+) к катоду (−), поэтому более длинная ножка должна быть подключена к положительному контакту. Для работы светодиода также требуется резистор, понижающий силу тока, иначе светодиод может перегореть. На контакте 13 платы Arduino уже встроен резистор, поэтому мы не будем добавлять внешних резисторов.

Выполните следующие шаги, чтобы провести тестирование:

  1. Вставьте длинную ножку (также известную как +5V, положительный контакт или анод) светодиода в контакт 13 на плате Arduino, как показано на рисунке:

  2. Подключите короткую ножку (также известную как отрицательный контакт или катод) к контакту GND (рядом с контактом 13), который также называется землей (ground);
  3. Подключите Вашу Arduino Uno к компьютеру с помощью USB-кабеля;
  4. Введите следующий код в окне среды разработки Arduino IDE:

    // Проект мигающего светодиода
    int led = 13;
    
    void setup() {
      pinMode(led, OUTPUT);
    }
    
    void loop() {
      digitalWrite(led, HIGH);
      delay(1000);
      digitalWrite(led, LOW);
      delay(1000);
    }
  5. Нажмите кнопку Verify (Проверить) в виде галочки и убедитесь, что программа работает правильно.
  6. Теперь нажмите кнопку Upload (Загрузка), чтобы загрузить Ваш скетч в Arduino.
  7. После загрузки скетча, он начнет сразу выполняться и светодиод начнет мигать.

Рассмотрим, что происходит в каждой строке скетча:

1.  // Проект мигающего светодиода
2.  int led = 13;
3.  void setup() {
4.    pinMode(led, OUTPUT);
5.  }
6.  void loop() {
7.    digitalWrite(led, HIGH);
8.    delay(1000);
9.    digitalWrite(led, LOW);
10.   delay(1000);
11. }
  1. Это комментарий. Каждая строка программы, которая начинается с символов //, предназначена для чтения только пользователем и игнорируется Arduino. Таким образом Вы можете описывать что происходит в Вашем коде (этот процесс называется комментированием кода). Если комментарий занимает больше одной строки, начните его с символов /* и закончите символами */. Строки текста внутри этих символов будут так же проигнорированы Arduino:
    /*
      Эти стороки являются комментарием
      и будут проигнорированы
      платой Arduino.
    */
  2. Создаем переменную led и записываем в нее значение нашего пина 13. Теперь каждое упоминание led в скетче обозгначает 13 контакт.
  3. Тут начинается код Настройки. Код, который расположен между фигурными скобками {} после инструкции setup() будет выполнен однократно при запуске платы. С открывающей фигурной скобки { начинается код Настройки.
  4. Сообщает Arduino, что контакт 13 работает в режиме выхода (таким образом мы будем подавать питание на светодиод).
  5. Закрывающая фигурная скобка } обозначает конец кода Настройки.
  6. Создает бесконечный цикл. Код, который находится между фигурными скобками {}, после инструкции loop() будет повторяться, пока на Arduino подается питание.
  7. Инструктирует Arduino подать на пин 13 Высокий сигнал (HIGH), таким образом пин 13 будет выдавать 5 вольт. Через светодиод пойдет ток и заставит его светиться.
  8. Инструктирует Arduino подождать 1 секунду. Т.к. время в Arduino отсчитывается в миллисекундах, а 1 секунда = 1000 миллисекунд, здесь указано значение 1000.
  9. Инструктирует Arduino подать на пин 13 Низкий сигнал (LOW), таким образом пин 13 будет выдавать 0 вольт. Эта инструкция отключает светодиод.
  10. Инструктирует Arduino опять подождать 1 секунду.
  11. Этой закрывающей фигурной скобкой заканчивается наш бесконечный цикл. После этой фигурной скобки скетч возвращается к началу цикла (строка 6).

Вы протестировали Вашу плату Arduino, поняли принцип работы скетча и процесс его загрузки. Неплохо для начала, правда? Впереди нас ждут еще более интересные знания и открытия!

Видео

№2: Бегущий огонек

Давайте заставим наш маячек бегать? Для этого выставим восемь светодиодов в одну линию и заставим их включаться и выключаться в определенное время!

Алгоритм работы нашего скетча будет такой:

  1. 1. Гасим все восемь светодиодов;
  2. 2. Включаем первый светодиод;
  3. 3. Делаем паузу, чтобы увидеть как горит наш светодиод;
  4. 4. Повторяем шаги 1 - 3 для всех остальных светодиодов по очереди;
  5. 5. Возвращаемся в начало цикла loop().

Обратите внимание! В большинстве экспериментов этого руководства используются желтые резисторы, сопротивлением 220 Ом.

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

Схема подключения

Уточнения к скетчу

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

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

Использование функций очень сильно упрощает написание и чтение кода, и в большинстве случаев делает его оптимальным по объёму занимаемой памяти.

Подробнее о функциях можно почитать тут: https://alexgyver.ru/lessons/functions

Скетч

Переменная time будет отвечать за скорость перемещения нашего огонька. Чем это значение ниже, тем быстрее бежит огонек.

int time = 200;

void setup() {
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
}
  
void ledsOff() {
  digitalWrite(2, LOW);
  digitalWrite(3, LOW);
  digitalWrite(4, LOW);
  digitalWrite(5, LOW);
  digitalWrite(6, LOW);
  digitalWrite(7, LOW);
  digitalWrite(8, LOW);
  digitalWrite(9, LOW);
}
  
void loop() {
  ledsOff();
  digitalWrite(2, HIGH);
  delay(time);
  ledsOff();
  digitalWrite(3, HIGH);
  delay(time);
  ledsOff();
  digitalWrite(4, HIGH);
  delay(time);
  ledsOff();
  digitalWrite(5, HIGH);
  delay(time);
  ledsOff();
  digitalWrite(6, HIGH);
  delay(time);
  ledsOff();
  digitalWrite(7, HIGH);
  delay(time);
  ledsOff();
  digitalWrite(8, HIGH);
  delay(time);
  ledsOff();
  digitalWrite(9, HIGH);
  delay(time);
  ledsOff();
}

№3: Светофоры на перекрестке

Давайте создатим систему двух светофоров на перекрестке. В качестве светофора будем использовать три светодиода - красный, желтый и зеленый.

Схема подключения

Скетч

В этом скетче мы будем использовать массивы!

// время горения светодиодов в мсек
#define TIME_GREEN 5000 // Сколько времени горят зеленый и красный
#define TIME_YELLOW 3000 // желтый
#define TIME_BLINK  300 // период мигания желтого светодиода

int pinleds[6] = {2, 3, 4, 8, 9, 10}; // Пины наших светодиодов по списку [green1, yellow1, red1, green2, yellow2, red2]     
boolean blinkyellow = true; // переменная blink для чередования мигания желтого

void setup() {
// Настроим выводы Arduino как выходы
for (int i = 0; i < 6; i++) {
    pinMode(pinleds[i], OUTPUT);
  }   
}

void loop() {
  // список состояний светодиодов 1- горит, 0- потушен
  // [green1,yellow1,red1,green2,yellow2,red2]
  // 1 - зеленый, 2 - красный
  int leds1[] = {HIGH, LOW, LOW, LOW, LOW, HIGH};
  trafficlight(leds1);
  delay(TIME_GREEN);  
  blinkyellow = true;
  // 1 - желтый с зеленым, 2- желтый c красным
  for (int i = 0; i < (TIME_YELLOW/TIME_BLINK); i++) {
    int leds2[]={HIGH, (int)blinkyellow, LOW, LOW, (int)blinkyellow, HIGH};
    trafficlight(leds2);
    delay(TIME_BLINK);
    blinkyellow = !blinkyellow;
  }
  // 1 - красный, 2 - зеленый
  int leds3[]={LOW, LOW, HIGH, HIGH, LOW, LOW};
  trafficlight(leds3);
  delay(TIME_GREEN);  
  blinkyellow = true;
  // 1- желтый c красным,  2 - желтый с зеленым
  //for(int i = 0; i < (TIME_YELLOW/TIME_BLINK); i++)
  for (int i=0; i < TIME_YELLOW; i = i + TIME_BLINK) {
    int leds4[]={LOW, (int)blinkyellow, HIGH, HIGH, (int)blinkyellow, LOW};
    trafficlight(leds4);
    delay(TIME_BLINK);
    blinkyellow = !blinkyellow;
  }
}

// функция зажигания светодиодов светофора
void trafficlight(int statled[5]) {
  for(int i = 0; i < 6; i++) {
    digitalWrite(pinleds[i],statled[i]);   
  } 
}

№4: Управляемый светодиод

В этом проекте мы познакомимся с принципом работы тактовых кнопок. В будущем Вы будете использовать их в множестве проектов.

Принцип работы тактовой кнопки:

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

     
  • Контакты А и С всегда замкнуты, так же как B и D.
  • При нажатии кнопка замыкает цепь, подавая питание. Как только кнопка отпущена, она размыкает цепь, прерывая питание.

Схема подключения

Скетч

В этом скетче мы определяем контакт кнопки и переводим его в режим входа - INPUT, а контакт светодиода - в режим выхода - OUTPUT.

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

const int buttonPin = 2; // Контакт, к которому подключена кнопка
const int ledPin = 13; // Контакт, к которому подключен светодиод
int buttonState = 0; // Задание значения кнопки

void setup() {
  pinMode(ledPin, OUTPUT); // Перевод контакта светодиода в режим выхода
  pinMode(buttonPin, INPUT); // Перевод контакта кнопки в режим входа
}
void loop() {
  buttonState = digitalRead(buttonPin); // Чтение сигнала с контакта 2
  if (buttonState == HIGH) { // Если кнопка нажата
    digitalWrite(ledPin, HIGH); // Устанавливается состояние HIGH (Включение светодиода)
  } else { // В противном случае
    digitalWrite(ledPin, LOW); // отключаем светодиод
  }
}

Видео

№5: Светильник с переключателем

В предыдущем эксперименте, нам приходится зажимать кнопку, чтобы наш светодиод горел

Но намного удобнее было бы сделть переключатель состояния светодиода, не правда ли?

Схема подключения

Тестовый скетч

const int buttonPin = 2; // Контакт, к которому подключена кнопка
const int ledPin = 13; // Контакт, к которому подключен светодиод
boolean currentButtonState = LOW; // Текущее состояние кнопки
boolean ledEnabled = false;  // Включен ли свет?

void setup() {
  pinMode(ledPin, OUTPUT); // Перевод контакта светодиода в режим выхода
  pinMode(buttonPin, INPUT); // Перевод контакта кнопки в режим входа
}

void loop() {
  currentButtonState = digitalRead(buttonPin); // Чтение сигнала с контакта 2

  if (currentButtonState) {
    ledEnabled = !ledEnabled;
  }
  
  digitalWrite(ledPin, ledEnabled);
}

Если мы загрузим этот скетч в нашу ардуино, то увидим, что он работает, но не очень стабильно. То нажатие на кнопку работает, то нет. Это происходит из-за так называемого «дребезга кнопки».

Что такое «дребезг кнопки»?

Кнопки -это механические устройства, с системой пружинного контакта. Когда вы нажимаете на кнопку, сигнал не просто меняется от низкого до высокого, он в течении нескольких миллисекунд скачет от одного значения до другого. Именно из-за этого, мы не можем узнать точное состояние кнопки, считав сигнал с входа контакта.

Как с этим бороться?

Тут нет ничего сложного. Для предотвращения дребезга нам необходимо пройтись по этим простым шагам:

  1. Указываем предыдущее и текущее состояние кнопки. Изначально это LOW;
  2. Считаем текущее состояние кнопки;
  3. Если текущее состояние кнопки отличается от предыдущего состояния, ждем 5 мс, потому что кнопка, возможно, изменила свое состояние;
  4. После 5 мс, считываем состояние кнопки и сохраняем его как текущее состояние;
  5. Если предыдущее состояние кнопки было LOW, а текущее состояние HIGH, переключаем состояние светодиода;
  6. Устанавливаем предыдущее состояние кнопки как текущее состояния кнопки;
  7. Возвращаемся к шагу 2.

Звучит немного запутано, но, на деле, это всего несколько строк кода. Давайте попробуем исправить наш скетч.

const int buttonPin = 2; // Контакт, к которому подключена кнопка
const int ledPin = 13; // Контакт, к которому подключен светодиод

// 1. Указываем предыдущее и текущее состояние кнопки. Изначально это LOW;
boolean currentButtonState = LOW; // Текущее состояние кнопки
boolean lastButtonState = LOW; // Предыдущее состояние кнопки

boolean ledEnabled = false;  // Включен ли свет?

void setup() {
  pinMode(ledPin, OUTPUT); // Перевод контакта светодиода в режим выхода
  pinMode(buttonPin, INPUT); // Перевод контакта кнопки в режим входа
}

void loop() {

  currentButtonState = digitalRead(buttonPin); // 2. Считываем текущее состояние кнопки

  if (lastButtonState != currentButtonState) {
    delay(5); // 3. Если текущее состояние кнопки отличается от предыдущего состояния, ждем 5 мс
    
    currentButtonState = digitalRead(buttonPin); // 4. После 5 мс, считываем состояние кнопки и сохраняем его как текущее состояние;

    if (lastButtonState == LOW && currentButtonState == HIGH) {
      // 5. Если предыдущее состояние кнопки было LOW, а текущее состояние HIGH, переключаем состояние светодиода
      ledEnabled = !ledEnabled;
    }
  }
  
  lastButtonState = currentButtonState; // 6. Устанавливаем предыдущее состояние кнопки как текущее состояния кнопки
  
  digitalWrite(ledPin, ledEnabled); // Передаем светодиоду его состояние
}

Теперь все работает замечательно, а вы восхитительны!

№6: Диммер освещения

В этом проекте Вы познакомитесь с потенциометром, аналоговым сигналом и понятим ШИМ-модуляции! Разве это не круто?

Итак, потенциометр управляет постоянным аналоговым сигналом. Но что это такое?

Проще всего это объяснить на примере:

Люди воспринимают мир в аналоговом режиме, это значит, что все, что мы видим и слышим — это непрерывная передача информации нашему мозгу. Этот непрерывный поток и есть аналоговые данные.

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

Центральный контакт потенциометра посылает сигнал на аналоговый вход Arduino (любой контакт с метками от A0 до A5) для считывания значения.

На самом деле светодиод постоянно включается и выключается, но это происходит так быстро, что благодаря компенсации наши глаза видят постоянное свечение светодиода, но с разным уровнем освещенности. Этот эффект известен под термином инерция зрения.

Чтобы создать этот эффект, в Arduino используется технология, называемая широтно-импульсной модуляцией (ШИМ). Arduino создает импульсы, очень быстро включая и выключая питание. Длительность включения и отключения питания (так называемая ширина импульса) в цикле определяет средний уровень выхода, и, изменяя значение ширины импульса, устройство может симулировать значения напряжения в диапазоне от 0 до 5 В (т.е. от отключенного до включенного состояния).

Если питание от Arduino подается в течение половины времени и отключается также на половину времени, средний уровень выхода составит 2.5 В, т.е. по центру между 0 и 5 В. Если сигнал подается 80% всего времени, а отключен в течение 20%, средний уровень выхода составит 4 В, и т.д.

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

Обратите внимание! У платы Arduino только контакты 3, 5, 6, 9, 10 и 11 поддерживают режим ШИМ-модуляции.

На рисунке ниже показаны примеры работы ШИМ в виде формы сигнала.

Очень много теории и мало что понятно? Ничего страшного, на практике все окажется совсем не сложно, а знания, которые Вы получите окажутся бесценными!

Схема подключения

Не имеет значения, какой из внешних контактов потенциометра подключать к плюсу, а какой к минусу.

Скетч

При работе этого скетча контакт А0 настроен на работу с потенциометром, а контакт 9 служит выводом для питания светодиода.

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

Напряжение регулируется в пределах от 0 до 5 В, тем самым и устанавливается яркость светодиода.

int potPin = A0; // Контакт аналогового входа, подключенный к потенциометру
int potValue = 0; // Значение, считываемое с потенциометра
int led = 9; // Контакт 9 подключен к светодиоду (поддерживает ШИМ)

// Выполняется однократно в начале программы
void setup() {
  pinMode(led, OUTPUT); // Перевод контакта 9 в режим вывода
}

// Непрерывное выполнение
void loop() {
  potValue = analogRead(potPin); // Чтение значения потенциометра с контакта A0
  analogWrite(led, potValue / 4); // Отправка значения потенциометра на светодиод для управления яркостью с помощью ШИМ
  delay(10); // Задержка 10 мс
}

Видео

№7: Радуга на RGB светодиоде

RGB светодиод отличается от обычного тем, что содержит три кристалла - Red (Красный), Green (Зеленый), Blue(Синий), которые могут синтезировать любой цвет или оттенок. Давайте попробуем сделать настоящую радугу?

Сначала познакомимся с RGB светодиодом поближе. В нашем эксперименте будет принимать участие rgb светодиод с общим минусом (катод), но на своем пути в мире ардуино вы можете встретить и светодиоды с общим плюсом (анодом).

Посмотрите внимательно на контакты нашего светодиода.


В таблице ниже представлены семь основных цветов радуги, разложенные на базовые цвета RGB.

Цвет R G B
Красный 255 0 0
Оранжевый 255 125 0
Желтый 255 255 0
Зеленый 0 255 0
Голубой 0 255 255
Синий 0 0 255
Фиолетовый 255 0 255

Наш светодиод будет переливаться от красного цвета до фиолетового, проходя через все семь основных цветов. Удивительно, но, на самом деле, тут нет ничего сложного!

Давайте разберем принцип работы нашей программы:

  1. Примем за начальную точку отсчета красный цвет (255,0,0).
  2. Будем постепенно увеличивать значение зеленой составляющей G до достижения значения оранжевого (255, 125, 0), а затем и желтого цвета (255, 255, 0).
  3. Постепенно уменьшим значение красной составляющей R до значения зеленого цвета (0, 255, 0).
  4. Постепенно увеличим значение синей составляющей B до значения голубого цвета (0, 255, 255).
  5. Постепенно уменьшим количество зеленой составляющей G до значения синего цвета (0, 0, 255).
  6. Постепенно увеличим значение красной составляющей R до значения фиолетового цвета (255, 0, 255).
  7. Выдерживаем паузу и переходим к шагу 1.

Схема подключения

Скетч

// пауза перед каждым изменением цвета радуги
#define MAX_PAUSE 1
#define MIN_PAUSE 30

// пин подключения среднего вывода потенциометра
const int PIN_POT=A0;

int pot; // переменная для хранения значения потенциометра 

const int RED = 11; // вывод красной ноги RGB-светодиода 
const int GREEN = 10; // вывод зеленой ноги RGB-светодиода 
const int BLUE = 9; // вывод синей ноги RGB-светодиода 

int red; // переменная для хранения R-составляющей цвета 
int green; // переменная для хранения G-составляющей цвета 
int blue; // переменная для хранения B-составляющей цвета 

void setup() {
  // Нам ничего не нужно делать при включении платы
}

// Функция установки цвета RGB-светодиода
void setRGB(int r, int g, int b) {
  analogWrite(RED, r);
  analogWrite(GREEN, g);
  analogWrite(BLUE, b);
  
  pot = analogRead(PIN_POT); // Снимаем показания потенциометра
  delay(map(pot, 0, 1023, MIN_PAUSE, MAX_PAUSE)); // Скалируем показания потенциометра (от 0 до 1023) на наш диапазон паузы (от 1 до 30)
}

void loop() {
  // от красного к желтому
  red = 255;
  green = 0;
  blue = 0;

  for (green = 0; green < 255; green++) {
    setRGB(red, green, blue);
  }

  // от желтому к зеленому
  for (red = 255; red > 0; red--) {
    setRGB(red, green, blue);
  }

  // от зеленого к голубому
  for (blue = 0; blue < 255; blue++) {
    setRGB(red, green, blue);
  }
  
  // от голубого к синему
  for (green = 255; green > 0; green--) {
    setRGB(red, green, blue);
  }

  // от синего к фиолетовому
  for (red = 0; red < 255; red++) {
    setRGB(red, green, blue);
  }

  // от фиолетового обратно к красному
  for (blue = 255; blue > 0; blue--) {
    setRGB(red, green, blue);
  }
}

№8: Проигрыватель простых мелодий

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

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

Ниже перечислены ноты и соответствующие им частоты. Период — это продолжительность времени в микросекундах, в течение которого генерируются колебания определенной частоты. Мы вдвое уменьшили это число, чтобы получить значения переменной timeHigh, которые используются в коде для имитации ноты.

Схема подключения

Скетч

Давайте воспроизведем простую мелодию.

В строке 2 мы сообщаем Arduino, что мелодия будет состоять из 15 нот. Затем мы сохраняем ноты мелодии в символьном массиве в виде текстовой строки в том порядке, в котором они должны воспроизводится.

После этого сохраняем значение длительности звучания каждой ноты в целочисленном массиве.

В строке 5 мы определяем темп, в котором будет воспроизводится мелодия.

Чтобы изменить мелодию, достаточно поменять значения в строках 2, 3, 4, 5.

int speakerPin = 9; // Контакт, к которому подключен пьезоизлучатель
int length = 15; // Число нот
char notes[] = "ccggaagffeeddc "; // Ноты мелодии в символьном массиве, пробел - пауза
int beats[] = { 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 4 }; // Длительности звучания каждой ноты
int tempo = 300;

void playTone(int tone, int duration) { // Создавая функцию типа void мы указываем компилятору, что никаких значений возвращаться не будет
  for (long i = 0; i < duration * 1000L; i += tone * 2) { // L - означает большое значение, без этого параметра максимальное значение будет 32.767
    digitalWrite(speakerPin, HIGH);
    delayMicroseconds(tone);
    digitalWrite(speakerPin, LOW);
    delayMicroseconds(tone);
  }
}

// Определение timeHigh для определенной ноты
void playNote(char note, int duration) {
  char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
  int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
  for (int i = 0; i < 8; i++) { // Воспроизведение тона, соответствующего ноте
    if (names[i] == note) {
      playTone(tones[i], duration);
    }
  }
}

void setup() {
  pinMode(speakerPin, OUTPUT); // Переводим speakerPin в режим вывода
}

// Воспроизведение тона
void loop() {
  for (int i = 0; i < length; i++) {
    if (notes[i] == ' ') {
      delay(beats[i] * tempo); // Конец мелодии
    } else {
      playNote(notes[i], beats[i] * tempo);
    }
    delay(tempo / 2); // Пауза между нотами
  }
}

№9: Игра «Саймон говорит»

Электронная версия игры «Саймон говорит» была весьма популярной в конце 1970-х и начала 1980-х годов. Задачей игрока было запомнить последовательность вспышек и звуков и затем воспроизвести ее. Эта игра развивает не только зрительную и слуховую память, но и скорость реакции. Ее до сих пор можно купить в магазинах. Но гораздо интереснее собрать такую игрушку самому. Этим мы сегодня и займемся!

Схема подключения

Скетч

Скетч генерирует случайную последовательность, в которой будут зажигаться светодиоды. Случайное значение переменной y, генерируемое в цикле последовательности, определяет, на какой светодиод подается питание (например, если переменной y присваивается значение 2, загорается светодиод, подключенный к контакту 2).

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

Для корректной компиляции скетча нужно подключить библиотеку Tone (Скачать).

#include <Tone.h>
Tone speakerpin;
int starttune[] = {NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_F4, NOTE_G4};
int duration2[] = {100, 200, 100, 200, 100, 400, 100, 100, 100, 100, 200, 100, 500};
int note[] = {NOTE_C4, NOTE_C4, NOTE_G4, NOTE_C5, NOTE_G4, NOTE_C5};
int duration[] = {100, 100, 100, 300, 100, 300};
int button[] = {2, 3, 4, 5}; // Контакты, к которым подключены кнопки
int ledpin[] = {8, 9, 10, 11}; // Контакты, к которым подключены светодиоды
int turn = 0; // Счетчик включений
int buttonstate = 0; // Проверка состояния кнопки
int randomArray[100]; // Массив, содержащий до 100 вводов
int inputArray[100];

void setup() {
  Serial.begin(9600);
  speakerpin.begin(12); // Контакт, к которому подключен пьезоизлучатель

  for (int x = 0; x < 4; x++) {
    pinMode(ledpin[x], OUTPUT); // Перевод контактов, к которым подключены светодиоды, в режим вывода
  }

  for (int x = 0; x < 4; x++) {
    pinMode(button[x], INPUT); // Перевод контактов, к которым подключены кнопки, в режим ввода
    digitalWrite(button[x], HIGH); // Включение внутреннего подтягивания; кнопки активны в верхнем положении (логическая инверсия)
  }

  // Генерация "большей случайности" функции randomArray, чтобы последовательность каждый раз менялась
  randomSeed(analogRead(0));

  for (int thisNote = 0; thisNote < 13; thisNote ++) {
    speakerpin.play(starttune[thisNote]); // Воспроизведение следующей ноты

    if (thisNote == 0 || thisNote == 2 || thisNote == 4 || thisNote == 6) {
      digitalWrite(ledpin[0], HIGH);
    }

    if (thisNote == 1 || thisNote == 3 || thisNote == 5 || thisNote == 7 || thisNote == 9 || thisNote == 11) {
      digitalWrite(ledpin[1], HIGH);
    }

    if (thisNote == 8 || thisNote == 12) {
      digitalWrite(ledpin[2], HIGH);
    }

    if (thisNote == 10) {
      digitalWrite(ledpin[3], HIGH);
    }

    delay(duration2[thisNote]);
    speakerpin.stop(); // Остановка для воспроизведения следующей ноты
    digitalWrite(ledpin[0], LOW);
    digitalWrite(ledpin[1], LOW);
    digitalWrite(ledpin[2], LOW);
    digitalWrite(ledpin[3], LOW);
    delay(25);
  }
  delay(1000);
}

void loop() {
  // Генерация массива, назначаемого игроку
  for (int y = 0; y <= 99; y++) {
    digitalWrite(ledpin[0], HIGH);
    digitalWrite(ledpin[1], HIGH);
    digitalWrite(ledpin[2], HIGH);
    digitalWrite(ledpin[3], HIGH);

    // Воспроизведение следующей ноты
    for (int thisNote = 0; thisNote < 6; thisNote ++) {
      speakerpin.play(note[thisNote]);
      // Продолжение ноты
      delay(duration[thisNote]); // Остановка для воспроизведения
      // следующей ноты
      speakerpin.stop();
      delay(25);
    }

    digitalWrite(ledpin[0], LOW);
    digitalWrite(ledpin[1], LOW);
    digitalWrite(ledpin[2], LOW);
    digitalWrite(ledpin[3], LOW);
    delay(1000);

    // Ограничения переменной turn
    for (int y = turn; y <= turn; y++) {
      Serial.println("");
      Serial.print("Turn: ");
      Serial.print(y);
      Serial.println("");
      randomArray[y] = random(1, 5);

      // Присвоение случайного номера (1-4)
      // Включение светодиодов в случайном порядке
      for (int x = 0; x <= turn; x++) {
        Serial.print(randomArray[x]);
        for (int y = 0; y < 4; y++) {
          if (randomArray[x] == 1 && ledpin[y] == 8) {
            digitalWrite(ledpin[y], HIGH);
            speakerpin.play(NOTE_G3, 100);
            delay(400);
            digitalWrite(ledpin[y], LOW);
            delay(100);
          }

          if (randomArray[x] == 2 && ledpin[y] == 9) {
            digitalWrite(ledpin[y], HIGH);
            speakerpin.play(NOTE_A3, 100);
            delay(400);
            digitalWrite(ledpin[y], LOW);
            delay(100);
          }

          if (randomArray[x] == 3 && ledpin[y] == 10) {
            digitalWrite(ledpin[y], HIGH);
            speakerpin.play(NOTE_B3, 100);
            delay(400);
            digitalWrite(ledpin[y], LOW);
            delay(100);
          }

          if (randomArray[x] == 4 && ledpin[y] == 11) {
            digitalWrite(ledpin[y], HIGH);
            speakerpin.play(NOTE_C4, 100);
            delay(400);
            digitalWrite(ledpin[y], LOW);
            delay(100);
          }
        }
      }
    }
    input();
  }
}

// Проверка ввода на соответствие последовательности
void input() {
  for (int x = 0; x <= turn;) {
    for (int y = 0; y < 4; y++) {
      buttonstate = digitalRead(button[y]);

      // Проверка нажатия кнопки
      if (buttonstate == LOW && button[y] == 2) {
        digitalWrite(ledpin[0], HIGH);
        speakerpin.play(NOTE_G3, 100);
        delay(200);
        digitalWrite(ledpin[0], LOW);
        inputArray[x] = 1;
        delay(250);
        Serial.print(" ");
        Serial.print(1);
        // Проверка введенного пользователем значения на соответствие
        // сгенерированному массиву
        if (inputArray[x] != randomArray[x]) {
          fail(); // Если нет, вызывается функция fail
        }
        x++;
      }

      if (buttonstate == LOW && button[y] == 3) {
        digitalWrite(ledpin[1], HIGH);
        speakerpin.play(NOTE_A3, 100);
        delay(200);
        digitalWrite(ledpin[1], LOW);
        inputArray[x] = 2;
        delay(250);
        Serial.print(" ");
        Serial.print(2);
        if (inputArray[x] != randomArray[x]) {
          fail();
        }
        x++;
      }

      if (buttonstate == LOW && button[y] == 4) {
        digitalWrite(ledpin[2], HIGH);
        speakerpin.play(NOTE_B3, 100);
        delay(200);
        digitalWrite(ledpin[2], LOW);
        inputArray[x] = 3;
        delay(250);
        Serial.print(" ");
        Serial.print(3);
        if (inputArray[x] != randomArray[x]) {
          fail();
        }
        x++;
      }

      if (buttonstate == LOW && button[y] == 5) {
        digitalWrite(ledpin[3], HIGH);
        speakerpin.play(NOTE_C4, 100);
        delay(200);
        digitalWrite(ledpin[3], LOW);
        inputArray[x] = 4;
        delay(250);
        Serial.print(" ");
        Serial.print(4);
        if (inputArray[x] != randomArray[x]) {
          fail();
        }
        x++;
      }
    }
  }
  delay(500);
  turn++; // Увеличение значения переменной turn
}

// Функция, используемая в случае, если игрок ошибся в последовательности
void fail() {
  for (int y = 0; y <= 2; y++) {
    // Индикация светодиодами
    // для обозначения ошибки
    digitalWrite(ledpin[0], HIGH);
    digitalWrite(ledpin[1], HIGH);
    digitalWrite(ledpin[2], HIGH);
    digitalWrite(ledpin[3], HIGH);
    speakerpin.play(NOTE_G3, 300);
    delay(200);
    digitalWrite(ledpin[0], LOW);
    digitalWrite(ledpin[1], LOW);
    digitalWrite(ledpin[2], LOW);
    digitalWrite(ledpin[3], LOW);
    speakerpin.play(NOTE_C3, 300);
    delay(200);
  }
  delay(500);
  turn = -1; // Сбросить значение переменной turn для начала новой игры
}

№10: Игра «Кто быстрее?»

В этой игре вы сможете посоревноваться со своими друзьями в скорости реакции! Все очень просто, слышишь сигнал - жмешь на кнопку! Кто первый - тот и победил.

Схема подключения

Скетч

#define BUZZER_PIN   10  // пин с пищалкой
#define PLAYER_COUNT 2   // количество игроков
// вместо перечисления всех пинов по-одному, мы объявляем пару
// списков: один с номерами пинов с кнопками, другой — со
// светодиодами. Списки также называют массивами (англ. array)
int buttonPins[PLAYER_COUNT] = {3, 13};
int ledPins[PLAYER_COUNT] = {8, 12};
  
void setup() {
  pinMode(BUZZER_PIN, OUTPUT);

  for (int player = 0; player < PLAYER_COUNT; ++player) {
    // при помощи квадратных скобок получают значение в массиве
    // под указанным в них номером. Нумерация начинается с нуля
    pinMode(ledPins[player], OUTPUT);
    pinMode(buttonPins[player], INPUT_PULLUP);
  }
}
  
void loop() {
  // даём сигнал «пли!», выждав случайное время от 2 до 7 сек
  delay(random(2000, 7000));
  tone(BUZZER_PIN, 3000, 250); // 3 килогерца, 250 миллисекунд
  
  for (int player = 0; ; player = (player+1) % PLAYER_COUNT) {
    // если игрок номер «player» нажал кнопку...
    if (!digitalRead(buttonPins[player])) {
      // ...включаем его светодиод и сигнал победы на 1 сек
      digitalWrite(ledPins[player], HIGH);
      tone(BUZZER_PIN, 4000, 1000);
      delay(1000);
      digitalWrite(ledPins[player], LOW);
      break; // Есть победитель! Выходим (англ. break) из цикла
    }
  }
}

На сегодня это все!

К сожалению, наше руководство подошло к концу, но Ваш путь только начинается! Читайте литературу, развивайтесь и, самое главное, создавайте!
Удачи Вам и до новых встреч!