Общие сведения:
В этом уроке мы создадим шуточное устройство, которое можно использовать как украшение новогоднего стола, а именно говорящую Arduino-шапку Деда Мороза, реагирующую на движение в зоне видимости датчика расстояния и читающую стихи (но вы можете заставить её делать практически всё, что угодно).
Видео:
Нам понадобится:
- 1х Шапка Деда Мороза;
- 1х Arduino / Piranha UNO;
- 1х Battery Shield;
- 1х Trema Shield;
- 2х Trema-модуль Адресный светодиод NeoPixel;
- 1х датчика расстояния HC-SR04;
- 1х MP3-плеер;
- 1х Карта памяти MicroSD;
- 1х Динамик;
- 1х Сервопривод;
- 2х нейлоновые стойки;
- 1х ПВХ-конструктор 1;
- 1х ПВХ-конструктор 2;
- 1х ПВХ-конструктор 3;
- 1х Крепёж;
- 14х Провода типа "мама-мама";
Для реализации проекта нам необходимо установить библиотеки:
- DFPlayer Mini mp3 Arduino Library V1.3 — для работы с плеером;
- iarduino_HC_SR04_int — для работы с ультразвуковыми датчиками расстояния;
- iarduino_NeoPixel — для работы с адресными светодиодами NeoPixel;
- Servo — входит в стандартный набор библиотек Arduino IDE и не требует дополнительной установки;
О том, как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki - Установка библиотек в Arduino IDE.
Описание работы устройства:
После подачи питания говорящая Arduino-шапка готова к работе.
Для того, чтобы включить анимацию, достаточно поднести руку к датчику на указанное расстояние.
Схема сборки:
Arduino / Piranha UNO:

Batery Shield:
Установите Battery Shield на Arduino / Piranha UNO:
Во время установки Battery Shield должен быть в выключенном состоянии!

Trema Shield:
На Battery Shield установите Trema Shield:

MP3-плеер:
Подключите MP3-плеер к Trema Shield и динамик к MP3-плееру:

Так как выводы 0 и 1 зарезервированы под RX и TX, перед загрузкой скетча необходимо отключить плеер от платы, а после загрузки и перед использованием подключить обратно.
Датчик расстояния
Подключите датчик расстояния HC-SR04 к Trema Shield:

Сервопривод
Подключите Сервопривод к Trema Shield:

Адресные светодиоды:
Подключите адресные светодиоды NeoPixel к Trema Shield:

Trema-модули NeoPixel соединены друг c другом, а первый модуль можно подключить к любому выводу Arduino (в скетче используется вывод D4). Чем больше модулей в цепи, тем больше тока она потребляет, поэтому в схеме используется стабилизированный источник питания Battery Shield на 5В постоянного тока.
ПВХ-конструктор
Так как проект не предполагает использования готовых корпусов, для изготовления всех необходимых элементов корпуса воспользуемся готовыми пластинами №1, №2 и №3 из которых, используя любой режущий инструмент, можно вырезать детали необходимого размера. Более подробно о том, как это сделать, вы можете увидеть в видео выше.
В данном проекте мы сделали следующие детали (размер одной "пустой" клетки 5х5мм, размеры приведены по количеству "сквозных" клеток):
- 1х пластина для основания 12х13;
- 1х пластина для крепления Piranha Uno 12х9;
- 4х опоры 5х3;
- 2х пластина для крепления сервопривода 11х4;
- 1х узкая пластина для крепления верхней "губы" 8х4;
- 1х широкая пластина для имитации верхней "губы" 8х7;
- 1х широкая пластина для имитации нижней "губы" 7х5;
- 2х квадрат для крепления модуля NeoPixel 5х5;
- 1х перемычка для крепления двух модулей NeoPixel 12х3;
- 1х пластина для крепления ультразвукового датчика расстояния 12х10;
Код программы (скетч):
#include <DFRobotDFPlayerMini.h>                                                              //  подключаем библиотеку для работы с MP3-плеером
#include <iarduino_NeoPixel.h>                                                                //  подключаем библиотеку для работы со светодиодами NEoPixel
#include <iarduino_HC_SR04_int.h>                                                             //  подключаем библиотеку для работы с датчиком расстояния HC-SR04
#include <Servo.h>                                                                            //  подключаем библиотеку для работы с сервоприводом
DFRobotDFPlayerMini myDFPlayer;                                                               //  объявляем объект myDFPlayer для работы с плеером
uint8_t       neo_pin            = 4;                                                         //  указываем вывод, к которому подключены светодиоды NeoPixel
uint16_t      modul_number       = 2;                                                         //  указываем количество модулей в цепи
iarduino_NeoPixel led(neo_pin, modul_number * 4 );                                            //  объявляем объект led для работы со светодиодами NeoPixel
iarduino_HC_SR04_int sensor(3, 2);                                                            //  объявляем объект sensor для работы с датчиком расстояния HC-SR04
Servo Servo_1;                                                                                //  создаём объект Servo_1 для работы с сервоприводом
bool          flg_first          = true;                                                      //  флаг первого запуска
bool          flg_moroz;                                                                      //  флаг блока "мороз"
bool          flg_terminator;                                                                 //  флаг блока "терминатор"
uint8_t       i                  = 0;                                                         //  переменная номера трека
uint8_t       r, g, b;                                                                        //  переменная для хранения цветов RGB для каждого светодиода
uint16_t      angle;                                                                          //  переменная угла поворота сервопривода
uint16_t      eye                = 255;                                                       //  переменная яркости глаза "терминатора"
uint32_t      timer1, timer2;                                                                 //  счётчики времени при срабатывании анимации
uint32_t      hcsr_waiting_time  = 30000;                                                     //  время ожидания повтора анимации
uint32_t      action_time;                                                                    //  время работы анимации
void setup() {
  Serial.begin(9600);                                                                         //  инициируем работу Монитора последовательного порта на скорости 9600бод
  myDFPlayer.begin(Serial);                                                                   //  указываем последовательный порт для общения с MP3-плеером
  Servo_1.attach(A0);                                                                         //  указываем вывод, к которому подключен сервопривод
  Servo_1.write(0);                                                                           //  указываем угол, на который должен повернуться сервопривод (возвращаем в начальное положение)
  led.begin();                                                                                //  инициализируем адресные светодиоды
  led.setColor(NeoPixelAll, 0, 0, 0);                                                         //  указываем всем светодиодам погаснуть
  led.write();                                                                                //  выключаем светодиоды
  sensor.distance();                                                                          //  запрашиваем значение расстояния
  delay(500);                                                                                 //  задержка 0.5сек
}
void loop() {
  //==========================================================================================//  БЛОК НАСТРОЙКИ РАБОТЫ ДАТЧИКА РАССТОЯНИЯ
  if (sensor.distance() >= 15 && sensor.distance() <= 40) {                                   //  Проверяем, если расстояние до препятствия от 15 до 40см, то
    if (millis() > timer1 + hcsr_waiting_time || flg_first) {                                 //  проверяем, прошло ли заданное время после последней сработки, или сброшен ли флаг при первом запуске, и если да, то
      i++;                                                                                    //  Увеличиваем номер трека на 1
      if(i>5) i = 1;                                                                          //  Если значение номера трека больше 5, то сбрасываем значение стрека в 0
      flg_first   = false;                                                                    //  Снимаем флаг первого запуска
      timer1      = millis();                                                                 //  обновляем значение таймера,
      myDFPlayer.volume(20);                                                                  //  выставляем уровень громкости плеера
      myDFPlayer.play(i);                                                                     //  указываем плееру включить трек
      if (i == 1) {action_time = 8400;  flg_moroz = true;  flg_terminator = false; }          //  Если номер трека 1, то устанавливаем время работы анимации, ставим флаг МОРОЗ, сбрасываем флаг ТЕРМИНАТОР
      if (i == 2) {action_time = 9700;  flg_moroz = true;  flg_terminator = false; }          //  Если номер трека 2, то устанавливаем время работы анимации, ставим флаг МОРОЗ, сбрасываем флаг ТЕРМИНАТОР
      if (i == 3) {action_time = 11000; flg_moroz = true;  flg_terminator = false; }          //  Если номер трека 3, то устанавливаем время работы анимации, ставим флаг МОРОЗ, сбрасываем флаг ТЕРМИНАТОР
      if (i == 4) {action_time = 10600; flg_moroz = true;  flg_terminator = false; }          //  Если номер трека 4, то устанавливаем время работы анимации, ставим флаг МОРОЗ, сбрасываем флаг ТЕРМИНАТОР
      if (i == 5) {action_time = 13600; flg_moroz = false; flg_terminator = true;  }          //  Если номер трека 5, то устанавливаем время работы анимации, ставим флаг ТЕРМИНАТОР, сбрасываем флаг МОРОЗ
    }
  }
  //==========================================================================================//
  if (flg_moroz && !flg_terminator) {                                                         //  Если был установлен флаг МОРОЗ, а флаг ТЕРМИНАТОР был сброшен, то
    if (millis() < timer1 + action_time) {                                                    //  проверяем, прошло ли заданное время на выполнение анимации, и если нет, то
      //======================================================================================//  БЛОК НАСТРОЙКИ ПОДСВЕТКИ
      if (millis() > timer2 + 500) {                                                          //  Проверяем, прошло ли время с момента последнего выполнения блока, и если нет, то
        timer2 = millis();                                                                    //  обновляем значение таймера,
        uint16_t f;                                                                           //  определяем переменную как коэффициент яркости
        if (f == 0  ) {f = 42; } else                                                         //  f=60°  (0 ... 42  ... 255   =   0° ... 60°  ... 360°)
        if (f == 42 ) {f = 85; } else                                                         //  f=120° (0 ... 85  ... 255   =   0° ... 120° ... 360°)
        if (f == 85 ) {f = 127;} else                                                         //  f=180° (0 ... 127 ... 255   =   0° ... 180° ... 360°)
        if (f == 127) {f = 170;} else                                                         //  f=240° (0 ... 170 ... 255   =   0° ... 240° ... 360°)
        if (f == 170) {f = 212;} else                                                         //  f=300° (0 ... 212 ... 255   =   0° ... 300° ... 360°)
        if (f == 212) {f = 0;  }                                                              //  f=360° (0 ... 255 ... 255   =   0° ... 360° ... 360°)
        if (f < 85)   {          b = 0; r = f * 3; g = 255 - r;} else                         //  перелив от зелёного к красному, через жёлтый
        if (f < 170)  {f -= 85;  g = 0; b = f * 3; r = 255 - b;} else                         //  перелив от красного к синему  , через фиолетовый
                      {f -= 170; r = 0; g = f * 3; b = 255 - g;}                              //  перелив от синего   к зелёному, через голубой
        for (uint8_t t = 0; t < 8; t++) {led.setColor(t, r, g, b);}                           //  устанавливаем выбранный цвет для очередного светодиода
      }
      //======================================================================================//  БЛОК НАСТРОЙКИ СЕРВОПРИВОДА
      if (i == 1) {                                                                           //  ТРЕК 1
        if (millis() > timer1 + 350  && millis() < timer1 + 450  ) {angle = 45;} else         //  здрА
        if (millis() > timer1 + 750  && millis() < timer1 + 850  ) {angle = 20;} else         //  вствУ
        if (millis() > timer1 + 950  && millis() < timer1 + 1050 ) {angle = 20;} else         //  йтЕ
        if (millis() > timer1 + 1150 && millis() < timer1 + 1250 ) {angle = 20;} else         //  рЕ
        if (millis() > timer1 + 1350 && millis() < timer1 + 1450 ) {angle = 20;} else         //  бЯ
        if (millis() > timer1 + 1550 && millis() < timer1 + 1650 ) {angle = 45;} else         //  тА
        if (millis() > timer1 + 2350 && millis() < timer1 + 2450 ) {angle = 45;} else         //  кАк
        if (millis() > timer1 + 2750 && millis() < timer1 + 2850 ) {angle = 20;} else         //  жЕ
        if (millis() > timer1 + 2950 && millis() < timer1 + 3050 ) {angle = 20;} else         //  вЫ
        if (millis() > timer1 + 3150 && millis() < timer1 + 3250 ) {angle = 20;} else         //  вЫ
        if (millis() > timer1 + 3350 && millis() < timer1 + 3450 ) {angle = 20;} else         //  рОс
        if (millis() > timer1 + 3750 && millis() < timer1 + 3850 ) {angle = 20;} else         //  лИ
        if (millis() > timer1 + 4250 && millis() < timer1 + 4350 ) {angle = 20;} else         //  бОль
        if (millis() > timer1 + 4550 && millis() < timer1 + 4650 ) {angle = 20;} else         //  шИ
        if (millis() > timer1 + 4750 && millis() < timer1 + 4850 ) {angle = 20;} else         //  мИ
        if (millis() > timer1 + 5150 && millis() < timer1 + 5250 ) {angle = 45;} else         //  стА
        if (millis() > timer1 + 5350 && millis() < timer1 + 5450 ) {angle = 20;} else         //  лИ
        if (millis() > timer1 + 5850 && millis() < timer1 + 5950 ) {angle = 45;} else         //  А
        if (millis() > timer1 + 6150 && millis() < timer1 + 6250 ) {angle = 20;} else         //  мЕ
        if (millis() > timer1 + 6350 && millis() < timer1 + 6450 ) {angle = 20;} else         //  нЯ
        if (millis() > timer1 + 6550 && millis() < timer1 + 6650 ) {angle = 20;} else         //  тО
        if (millis() > timer1 + 7050 && millis() < timer1 + 7150 ) {angle = 20;} else         //  всЕ
        if (millis() > timer1 + 7150 && millis() < timer1 + 7250 ) {angle = 20;} else         //  Уз
        if (millis() > timer1 + 7450 && millis() < timer1 + 7550 ) {angle = 45;} else         //  нА
        if (millis() > timer1 + 7750 && millis() < timer1 + 7850 ) {angle = 20;} else         //  лИ?
                                                                   {angle =  0;}              //  согласные
      }
      //======================================================================================//
      if (i == 2) {                                                                           //  ТРЕК 2
        if (millis() > timer1 + 150  && millis() < timer1 + 250  ) {angle = 20;}else          //  Я
        if (millis() > timer1 + 650  && millis() < timer1 + 750  ) {angle = 20;} else         //  рЕ
        if (millis() > timer1 + 750  && millis() < timer1 + 850  ) {angle = 20;} else         //  бЯ
        if (millis() > timer1 + 950  && millis() < timer1 + 1050 ) {angle = 45;} else         //  тА
        if (millis() > timer1 + 1450 && millis() < timer1 + 1600 ) {angle = 45;} else         //  стА
        if (millis() > timer1 + 1650 && millis() < timer1 + 1750 ) {angle = 20;} else         //  рЫй
        if (millis() > timer1 + 1950 && millis() < timer1 + 2100 ) {angle = 20;} else         //  дЕд
        if (millis() > timer1 + 2650 && millis() < timer1 + 2750 ) {angle = 20;} else         //  мнЕ
        if (millis() > timer1 + 2850 && millis() < timer1 + 2950 ) {angle = 20;} else         //  У
        if (millis() > timer1 + 3050 && millis() < timer1 + 3150 ) {angle = 20;} else         //  же
        if (millis() > timer1 + 3500 && millis() < timer1 + 3600 ) {angle = 20;} else         //  пЯть
        if (millis() > timer1 + 4000 && millis() < timer1 + 4100 ) {angle = 20;} else         //  тЫ
        if (millis() > timer1 + 4150 && millis() < timer1 + 4250 ) {angle = 20;} else         //  сЯч
        if (millis() > timer1 + 4550 && millis() < timer1 + 4650 ) {angle = 20;} else         //  лЕт
        if (millis() > timer1 + 5350 && millis() < timer1 + 5450 ) {angle = 20;} else         //  в Ян
        if (millis() > timer1 + 5550 && millis() < timer1 + 5650 ) {angle = 45;} else         //  вА
        if (millis() > timer1 + 5750 && millis() < timer1 + 5850 ) {angle = 20;} else         //  рЕ
        if (millis() > timer1 + 6050 && millis() < timer1 + 6150 ) {angle = 20;} else         //  И
        if (millis() > timer1 + 6150 && millis() < timer1 + 6250 ) {angle = 20;} else         //  фЕв
        if (millis() > timer1 + 6350 && millis() < timer1 + 6450 ) {angle = 45;} else         //  рА
        if (millis() > timer1 + 6650 && millis() < timer1 + 6750 ) {angle = 20;} else         //  лЕ
        if (millis() > timer1 + 7150 && millis() < timer1 + 7250 ) {angle = 20;} else         //  Я
        if (millis() > timer1 + 7550 && millis() < timer1 + 7650 ) {angle = 20;} else         //  гУ
        if (millis() > timer1 + 7900 && millis() < timer1 + 8000 ) {angle = 20;} else         //  лЯ
        if (millis() > timer1 + 8000 && millis() < timer1 + 8100 ) {angle = 20;} else         //  Ю
        if (millis() > timer1 + 8500 && millis() < timer1 + 8600 ) {angle = 20;} else         //  пО
        if (millis() > timer1 + 8750 && millis() < timer1 + 8850 ) {angle = 20;} else         //  зЕм
        if (millis() > timer1 + 9000 && millis() < timer1 + 9100 ) {angle = 20;} else         //  лЕ
                                                                   {angle =  0;}              //  согласные
      }
      //======================================================================================//
      if (i == 3) {                                                                           //  ТРЕК 3
        if (millis() > timer1 + 250   && millis() < timer1 + 350   ) {angle = 20;} else       //  тОль
        if (millis() > timer1 + 550   && millis() < timer1 + 650   ) {angle = 20;} else       //  кО
        if (millis() > timer1 + 1000  && millis() < timer1 + 1100  ) {angle = 45;} else       //  встА
        if (millis() > timer1 + 1300  && millis() < timer1 + 1400  ) {angle = 20;} else       //  нУ
        if (millis() > timer1 + 1550  && millis() < timer1 + 1650  ) {angle = 20;} else       //  Я
        if (millis() > timer1 + 1900  && millis() < timer1 + 2000  ) {angle = 20;} else       //  с пО
        if (millis() > timer1 + 2150  && millis() < timer1 + 2250  ) {angle = 20;} else       //  стЕ
        if (millis() > timer1 + 2450  && millis() < timer1 + 2550  ) {angle = 20;} else       //  лИ
        if (millis() > timer1 + 3100  && millis() < timer1 + 3200  ) {angle = 45;} else       //  нА
        if (millis() > timer1 + 3500  && millis() < timer1 + 3600  ) {angle = 20;} else       //  чИ
        if (millis() > timer1 + 3700  && millis() < timer1 + 3800  ) {angle = 45;} else       //  нА
        if (millis() > timer1 + 3900  && millis() < timer1 + 4000  ) {angle = 20;} else       //  Ют
        if (millis() > timer1 + 4150  && millis() < timer1 + 4250  ) {angle = 20;} else       //  сЯ
        if (millis() > timer1 + 4400  && millis() < timer1 + 4500  ) {angle = 20;} else       //  мЕ
        if (millis() > timer1 + 4600  && millis() < timer1 + 4700  ) {angle = 20;} else       //  тЕ
        if (millis() > timer1 + 5000  && millis() < timer1 + 5100  ) {angle = 20;} else       //  лИ
        if (millis() > timer1 + 5600  && millis() < timer1 + 5700  ) {angle = 45;} else       //  кАк
        if (millis() > timer1 + 6050  && millis() < timer1 + 6150  ) {angle = 20;} else       //  встрЯ
        if (millis() > timer1 + 6450  && millis() < timer1 + 6550  ) {angle = 20;} else       //  хнУ
        if (millis() > timer1 + 6700  && millis() < timer1 + 6800  ) {angle = 20;} else       //  Я
        if (millis() > timer1 + 6950  && millis() < timer1 + 7050  ) {angle = 20;} else       //  рУ
        if (millis() > timer1 + 7100  && millis() < timer1 + 7200  ) {angle = 45;} else       //  кА
        if (millis() > timer1 + 7300  && millis() < timer1 + 7400  ) {angle = 20;} else       //  вОм
        if (millis() > timer1 + 8200  && millis() < timer1 + 8300  ) {angle = 20;} else       //  всЁ
        if (millis() > timer1 + 8600  && millis() < timer1 + 8700  ) {angle = 20;} else       //  пО
        if (millis() > timer1 + 9000  && millis() < timer1 + 9100  ) {angle = 20;} else       //  крО
        if (millis() > timer1 + 9250  && millis() < timer1 + 9350  ) {angle = 20;} else       //  Ет
        if (millis() > timer1 + 9500  && millis() < timer1 + 9600  ) {angle = 20;} else       //  сЯ
        if (millis() > timer1 + 9850  && millis() < timer1 + 9950  ) {angle = 20;} else       //  снЕж
        if (millis() > timer1 + 10150 && millis() < timer1 + 10250 ) {angle = 20;} else       //  кОм
                                                                     {angle =  0;}            //  согласные
      }
      //======================================================================================//
      if (i == 4) {                                                                           //  ТРЕК 4
        if (millis() > timer1 + 200   && millis() < timer1 + 300   ) {angle = 20;} else       //  нО
        if (millis() > timer1 + 550   && millis() < timer1 + 600   ) {angle = 20;} else       //  сЕй
        if (millis() > timer1 + 900   && millis() < timer1 + 1000  ) {angle = 45;} else       //  чАс
        if (millis() > timer1 + 1350  && millis() < timer1 + 1450  ) {angle = 20;} else       //  Я
        if (millis() > timer1 + 1750  && millis() < timer1 + 1850  ) {angle = 20;} else       //  О
        if (millis() > timer1 + 2050  && millis() < timer1 + 2150  ) {angle = 20;} else       //  чЕнь
        if (millis() > timer1 + 2450  && millis() < timer1 + 2550  ) {angle = 20;} else       //  дОб
        if (millis() > timer1 + 2900  && millis() < timer1 + 3000  ) {angle = 20;} else       //  рЫй
        if (millis() > timer1 + 3650  && millis() < timer1 + 3750  ) {angle = 20;} else       //  И
        if (millis() > timer1 + 3950  && millis() < timer1 + 4150  ) {angle = 20;} else       //  с рЕ
        if (millis() > timer1 + 4150  && millis() < timer1 + 4250  ) {angle = 20;} else       //  бЯ
        if (millis() > timer1 + 4300  && millis() < timer1 + 4400  ) {angle = 45;} else       //  тА
        if (millis() > timer1 + 4450  && millis() < timer1 + 4550  ) {angle = 20;} else       //  мИ
        if (millis() > timer1 + 4650  && millis() < timer1 + 4750  ) {angle = 20;} else       //  дрУ
        if (millis() > timer1 + 4900  && millis() < timer1 + 5000  ) {angle = 20;} else       //  жУ
        if (millis() > timer1 + 5850  && millis() < timer1 + 5950  ) {angle = 20;} else       //  нИ
        if (millis() > timer1 + 6250  && millis() < timer1 + 6350  ) {angle = 20;} else       //  кО
        if (millis() > timer1 + 6650  && millis() < timer1 + 6750  ) {angle = 20;} else       //  гО
        if (millis() > timer1 + 6900  && millis() < timer1 + 7000  ) {angle = 20;} else       //  нЕ
        if (millis() > timer1 + 7050  && millis() < timer1 + 7150  ) {angle = 45;} else       //  зА
        if (millis() > timer1 + 7200  && millis() < timer1 + 7300  ) {angle = 20;} else       //  мО
        if (millis() > timer1 + 7350  && millis() < timer1 + 7450  ) {angle = 45;} else       //  рО
        if (millis() > timer1 + 7650  && millis() < timer1 + 7750  ) {angle = 20;} else       //  жУ
        if (millis() > timer1 + 8200  && millis() < timer1 + 8300  ) {angle = 20;} else       //  нИ
        if (millis() > timer1 + 8550  && millis() < timer1 + 8650  ) {angle = 20;} else       //  кО
        if (millis() > timer1 + 8850  && millis() < timer1 + 8950  ) {angle = 20;} else       //  гО
        if (millis() > timer1 + 9300  && millis() < timer1 + 9400  ) {angle = 20;} else       //  не
        if (millis() > timer1 + 9600  && millis() < timer1 + 9700  ) {angle = 45;} else       //  зА
        if (millis() > timer1 + 9850  && millis() < timer1 + 9950  ) {angle = 20;} else       //  стУ
        if (millis() > timer1 + 10150 && millis() < timer1 + 10250 ) {angle = 20;} else       //  жУ
                                                                     {angle =  0;}            //  согласные
      }
      //======================================================================================//
      Servo_1.write(angle);                                                                   //  указываем сервоприводу отклониться на угол angle
      led.write();                                                                            //  записываем указанные цвета в модули
      delay(10);                                                                              //  задержка 0.1сек
    }
    else{                                                                                     //  Если время выполнения анимации закончилось, то
      flg_moroz = false;                                                                      //  сбрасываем флаг МОРОЗ
    }
  } else
  //==========================================================================================//  ТРЕК 5
  if (!flg_moroz && flg_terminator) {                                                         //  Если флаг МОРОЗ сброшен, а флаг ТЕРМИНАТОР установлен, то
    if (millis() < timer1 + action_time) {                                                    //  проверяем, прошло ли заданное время на выполнение анимации, и если нет, то
      //======================================================================================//  БЛОК НАСТРОЙКИ ПОДСВЕТКИ
      if (millis() > timer1 + 2950 && millis() < timer1 + 8000 ) {                            //  проверяем, прошло ли заданное время на выполнение анимации, и если нет, то
        for (uint8_t t = 0; t < 4; t++) {led.setColor(t, eye, 0, 0);}                         //  включаем 4 светодиода одного модуля красным цветом
      }
      if (millis() >= timer1 + 8000 && millis() < timer1 + 13500 ) {                          //  проверяем, прошло ли заданное время на выполнение анимации, и если нет, то
        if (millis() > timer2 + 15) {                                                         //  каждые 15мс
          timer2 = millis();                                                                  //  обновляем счётчик и
          if (eye > 0) eye--;                                                                 //  уменьшаем значение яркости "глаза" на 1
          else         eye = 0;                                                               //  Если же значение долшло до 0, то так и оставляем это значение
        }
        for (uint8_t t = 0; t < 4; t++) {led.setColor(t, eye, 0, 0);}                         //  Устанавливаем яркость
      }
      //======================================================================================//  БЛОК НАСТРОЙКИ СЕРВОПРИВОДА
      if (millis() > timer1 + 4750 && millis() < timer1 + 4850 ) {angle = 45;} else           //  hA
      if (millis() > timer1 + 5050 && millis() < timer1 + 5150 ) {angle = 45;} else           //  stA
      if (millis() > timer1 + 5150 && millis() < timer1 + 5250 ) {angle = 45;} else           //  lA
      if (millis() > timer1 + 5300 && millis() < timer1 + 5400 ) {angle = 20;} else           //  vI
      if (millis() > timer1 + 5650 && millis() < timer1 + 5750 ) {angle = 45;} else           //  stA
      if (millis() > timer1 + 6150 && millis() < timer1 + 6250 ) {angle = 45;} else           //  bA
      if (millis() > timer1 + 6400 && millis() < timer1 + 6500 ) {angle = 20;} else           //  bY
                                                                 {angle =  0;}                //  согласные
      //======================================================================================//
      Servo_1.write(angle);                                                                   //  указываем сервоприводу отклониться на угол angle
      led.write();                                                                            //  записываем указанные ранее цвета в модули
      delay(10);                                                                              //  задержка 0.1сек
    } else {                                                                                  //  Если время выполнения анимации закончилось, то
      flg_terminator = false;                                                                 //  сбрасываем флаг ТЕРМИНАТОР
      }
  }
  else {                                                                                      //  Если время выполнения анимации закончилось, то
  //==========================================================================================//  БЛОК РАБОТЫ УСТРОЙСТВА БЕЗ НАЛИЧИЯ ПРЕПЯТСТВИЯ В ЗОНЕ ДАТЧИКА
    eye = 255;                                                                                //  указываем значение яркости глаза максимальным
    myDFPlayer.pause();                                                                       //  останавливаем плеер
    led.setColor(NeoPixelAll, 0, 0, 0);                                                       //  устанавливаем цвет всех светодиодов в чёрный(гасим)
    led.write();                                                                              //  записываем указанный цвет в модули
  }
}
Алгоритм работы скетча:
До кода void setup() определяются переменные, подключаются необходимые библиотеки.
В коде void setup() инициализируется MP3-плеер, адресные светодиоды NeoPixel, датчик расстояния, сервопривод, выключаются светодиоды, если до этого они горели.
Код void loop() делится 2 части:
- ЧАСТЬ 1: Проверяется, обнаружил ли датчик расстояния препятствие в указанной зоне:- Если да, то запускается трек с мелодией (который при каждом следующем срабатывании увеличивается на 1), ставится флагflg_morozилиflg_terminatorв данной зоне (в зависимости от трека, который будет играть), обнуляется счётчик времениtimer1, фиксирующий наступление события, чтобы в следующий раз оно сработало через времяhcsr_waiting_time_near, настраивается времяaction timeдля каждого конкретного трека;
 
- Если да, то запускается трек с мелодией (который при каждом следующем срабатывании увеличивается на 1), ставится флаг
- ЧАСТЬ 2: Проверяется, был ли установлен флаг наличия препятствия:- Если был установлен флаг flg_moroz:- Проверяем, истекло время выполнения анимации timer1 + action_timeили нет;- Если не истекло, то задаём режим работы светодиодных модулей (строки 60-73) и сервопривода (строки75-202);
- Для того, чтобы соотнести слова трека и движение сервопривода, применён следующий метод:- на букве "А" сервопривод отклоняется на 45 градусов;
- на всех гласных, кроме "А", сервопривод отклоняется на 20 градусов;
- на всех согласных сервопривод не отклоняется (0 градусов);
- Помимо этого, вам понадобится любой аудиоредактор, чтобы можно было определить время произнесения тех или иных букв с точностью до миллисекунд;
 
- Если истекло, то гасим все светодиоды и сбрасываем флаг;
 
- Если не истекло, то задаём режим работы светодиодных модулей (строки 
 
- Проверяем, истекло время выполнения анимации 
- Если был установлен флаг flg_terminator:- Проверяем, истекло время выполнения анимации timer1 + action_timeили нет;- Если не истекло, то задаём режим работы светодиодных модулей (строки 216-226) и сервопривода (строки228-235);
- Для того, чтобы соотнести слова трека и движение сервопривода, применён следующий метод:
- на букве "А" сервопривод отклоняется на 45 градусов;
- на всех гласных, кроме "А", сервопривод отклоняется на 20 градусов;
- на всех согласных сервопривод не отклоняется (0 градусов);
- Помимо этого, вам понадобится любой аудиоредактор, чтобы можно было определить время произнесения тех или иных букв с точностью до миллисекунд;
- Если истекло, то гасим все светодиоды и сбрасываем флаг;
 
- Если не истекло, то задаём режим работы светодиодных модулей (строки 
 
- Проверяем, истекло время выполнения анимации 
 
- Если был установлен флаг 
Про треки:
Для того, чтобы плеер верно мог воспроизвести ваши треки, их необходимо разместить на SD-карте следующим образом:
- Переименуйте все ваши треки, которые вы хотите воспроизвести с плеера, используя следующую форму: 000Х.mp3, где Х - порядковый номер трека;
- Создайте в основной директории карты новую папку и переименуйте её в "mp3";
- Скопируйте ваши переименованные файлы с компьютера на карту памяти;
Готово! Теперь, зная количество и названия треков, внесите изменения в скетче там, где указывается порядковый номер трека и условия его воспроизведения (строки 43-53)
В данном проекте для примера был использован случайный трек. Вы же, используя вышеуказанные методы, можете "научить" шапку произносить тот текст, который сами захотите.
Ссылки:
- Библиотека iarduino_NeoPixel;
- Библиотека DFPlayer Mini mp3 Arduino Library V1.3;
- Библиотека iarduino_HC_SR04_int;
- Wiki - Установка библиотек в Arduino IDE;
- Wiki - Piranha UNO;
- Wiki - Battery Shield;
- Wiki - Trema Shield;
- Wiki - Trema-модуль Адресные светодиоды NeoPixel;
- Wiki - Ультразвуковой датчик измерения расстояния HC-SR04;

Обсуждение