Проект «Машинка-музыкант»

В этом уроке

  • Используем машинку не по назначению — создаём музыкальный автомат
  • Генерация тона по нотам
  • Создание собственных мелодий

Видео версия урока

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

В этом разделе мы «соединили» воедино музыкальный автомат и нашу машинку! Она будет двигаться по трассе, на которой нанесены метки — это ноты. Бампер будет их считывать, контроллер — анализировать и воспроизводить звуки при помощи зуммера.

Установка зуммера на машинку

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

Принцип работы "Машинки-музыканта"

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

Давайте загрузим скетч и разберёмся с тем, как он работает.

#include <Wire.h>                                         // Подключаем библиотеку для работы с шиной I2C
#include <iarduino_I2C_Motor.h>                           // Подключаем библиотеку для работы с мотором
#include <iarduino_I2C_Bumper.h>                          // Подключаем библиотеку для работы с бампером I2C-flash
iarduino_I2C_Motor  mot_R (0x0A);                         // Объявляем объект mot_R для правого мотора
iarduino_I2C_Motor  mot_L (0x0B);                         // Объявляем объект mot_L для левого мотора
iarduino_I2C_Bumper  bum (0x0C);                          // Объявляем объект bum для работы с бампером I2C-flash
#define zumPin 6                                          // Зуммер подключен к 6 пину
uint8_t speedMot = 20;                                    // Скорость мотора, % ШИМ
int limitValue = 1500;                                    // Граничное значение, при котором датчик срабатывает на ноту
const int fre[7] = {262, 294, 330, 349, 392, 440, 466};   // Массив частот нот (до, ре, ми, фа, соль, ля, си бемоль)
uint8_t arr[7] = {0,0,0,0,0,0,0};                         // Массив считанных значений
uint8_t oldArr[7] = {0,0,0,0,0,0,0};                      // Массив предыдущих значений
uint8_t deltaArr[7] = {0,0,0,0,0,0,0};                    // Массив изменившихся с прошлого измерения значений
void setup() {
  mot_R.begin();                                          // Инициируем работу с правым мотором
  mot_L.begin();                                          // Инициируем работу с левым мотором
  mot_R.setDirection(true);                               // Задаём направление вращения правого мотора 
  mot_L.setDirection(false);                              // Задаём направление вращения левого мотора  
  bum.begin();                                            // Инициируем работу с бампером (датчиком линии)
}
void loop() { 
  if (bum.getLineAnalog(8) < limitValue && bum.getLineAnalog(9) < limitValue){    // Если линия под двумя датчиками, едем прямо
    mot_L.setSpeed(speedMot, MOT_PWM);                    // Включаем левый мотор
    mot_R.setSpeed(speedMot, MOT_PWM);                    // Включаем правый мотор
  }  
  else if (bum.getLineAnalog(8) < limitValue){            // Иначе, если линия только под левым датчиком
    mot_L.setSpeed(speedMot-speedMot/3, MOT_PWM);         // Уменьшаем скорость левого мотора
    mot_R.setSpeed(speedMot, MOT_PWM);                    // Включаем правый мотор
  }
  else if (bum.getLineAnalog(9) < limitValue){            // Иначе, если линия только под правым датчиком
    mot_L.setSpeed(speedMot, MOT_PWM);                    // Включаем левый мотор
    mot_R.setSpeed(speedMot-speedMot/3, MOT_PWM);         // Уменьшаем скорость правого мотора
  } 
  for (uint8_t i = 0; i <= 6; i++){                       // Чтение датчиков
    arr[i] = bum.getLineAnalog(i+1) < limitValue ? 1 : 0; // Если значение с датчика меньше граничного, записываем в массив 1, иначе 0 
  }                                                                                                  
  for (uint8_t i = 0; i <= 6; i++){                       // Вычисление разницы текущего и предыдущего массива
    deltaArr[i] = abs(arr[i] - oldArr[i]);                // Записываем в массив отличия (по модулю)
  }
  for (uint8_t i = 0; i <= 6; i++){                       // Обновляем предыдущий массив
      oldArr[i] = arr[i];
  }
  uint8_t sum = 0;                                        // Создаём переменную
   for (uint8_t i = 0; i <= 6; i++){                      // Вычисляем  сумму значений массива
    sum += arr[i];
  }
 if (sum == 0) noTone(zumPin);                            // Если все элементы массива равным 0, выключаем зуммер
  else {
    for (uint8_t i = 0; i <= 6; i++){                     // Иначе ищем первое изменившееся значение в массиве
      if (deltaArr[i] != 0) {                              
        tone(zumPin, fre[i]);                             // Воспроизводим зуммером соответствующую ноту
        break;                                            
      }}}}

В начале скетча Вы видите несколько настроек. Например, переменная speedMot (8 строка) определяет скорость движения машинки, а limitValue (9 строка) — граничное значение с датчика, которое мы принимаем за фон. Если ноты воспроизводятся нечётко, подкорректируйте это значение. Вы можете вывести считанные датчиком значения в монитор порта, чтобы увидеть, какие значения соответствуют фону, а какие — нотам. Также Вам, возможно, потребуется немного опустить бампер пониже. Вы можете сделать это, прикрутив его снизу адаптера бампера (а не сверху, как он установлен сейчас).

Принцип создания собственных мелодий

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

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

Нота
Частота звучания в первой октаве
Частота звучания во второй октаве
до
262
523
до #
277
554
ре
294
587
ре #
311
622
ми
330
659
фа
349
699
фа #
370
740
соль
392
784
соль #
415
831
ля
440
880
ля #
466
932
си
494
988

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

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

Дополнительное задание.

1) Наша мелодия звучит в первой октаве. Сделайте так, чтобы она прозвучала во второй октаве (на слух она станет выше).

2) Создайте трассу для любой другой мелодии. Можете взять уже существующую или придумать свою.

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

Поздравляю с изучением данного урока!
Следующий урок:
№16. Движение по линии «Вижу трассу!».
приступить к изучению

Продукт в магазине

«ROBORACE» — Образовательный набор на базе Arduino

В магазин

Обсуждение

Гарантии и возврат. Используя сайт, Вы соглашаетесь с условиями.