Arduino тыква — готовимся в Хэллоуину

Общие сведения:

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

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

Видео:

Нам понадобится:

    Для реализации проекта нам необходимо установить библиотеки:

    О том, как устанавливать библиотеки, Вы можете ознакомиться на странице 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-плееру:

    Адресные светодиоды:

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

    Все используемые в уроке Trema-модули NeoPixel соединены друг c другом, а первый модуль можно подключить к любому выводу Arduino. Номер вывода указывается в скетче (в примере используется вывод D5). Чем больше модулей в цепи, тем больше тока она потребляет, поэтому в схеме используется стабилизированный источник питания Battery Shield на 5В постоянного тока.

    Код программы (скетч):

    #include <DFRobotDFPlayerMini.h>                                        //  подключаем библиотеку для работы с MP3-плеером
    #include <iarduino_NeoPixel.h>                                          //  подключаем библиотеку для работы со светодиодами NEoPixel
    #include <iarduino_HC_SR04_int.h>                                       //  подключаем библиотеку для работы с датчиком расстояния HC-SR04
    
    DFRobotDFPlayerMini myDFPlayer;                                         //  объявляем объект myDFPlayer для работы с плеером
    
    uint8_t       neo_pin      = 5;                                         //  указываем вывод, к которому подключены светодиоды NeoPixel
    uint16_t      modul_number = 4;                                         //  указываем количество модулей в цепи
    iarduino_NeoPixel led(neo_pin, modul_number * 4 );                      //  объявляем объект led для работы со светодиодами NeoPixel
    iarduino_HC_SR04_int sensor(3, 2);                                      //  объявляем объект sensor для работы с датчиком расстояния HC-SR04
    
    uint8_t       j;                                                        //  Объявляем переменную для хранения значения сдвига спектра цветов для всех светодиодов (от 0 до 255)
    uint8_t       r, g, b;                                                  //  Объявляем переменную для хранения цветов RGB для каждого светодиода
    uint32_t      timer1, timer2;                                           //  счётчики времени при срабатывании анимации
    uint32_t      timer3;                                                   //  счётчик времени при включении светодиодной подсветки тыквы
    uint32_t      hcsr_waiting_time_far   = 600000;                         //  время ожидания повтора анимации ДВИЖЕНИЕ ВДАЛЕКЕ
    uint32_t      hcsr_waiting_time_near  = 60000;                          //  время ожидания повтора анимации ДВИЖЕНИЕ ВБЛИЗИ
    uint32_t      action_time             = 10000;                          //  время работы анимации
    uint8_t       flg_near, flg_far;                                        //  флаги для включения анимации
    uint8_t       rev_flg;                                                  //  флаг включения реверса при счёте
    uint8_t       p;                                                        //  переменная для хранения номера светодиода при сдвиге
    uint8_t       l;                                                        //  переменная для хранения значения яркости светодиодов
    
    void setup() {
      Serial.begin(9600);                                                   //  инициируем работу Монитора последовательного порта на скорости 9600бод
      myDFPlayer.begin(Serial);                                             //  указываем последовательный порт для общения с MP3-плеером
      led.begin();                                                          //  инициализируем адресные светодиоды
      led.setColor(NeoPixelAll, 0, 0, 0);                                   //  указываем всем светодиодам погаснуть
      led.write();                                                          //  выключаем светодиоды
      randomSeed(analogRead(0));                                            //  инициируем генератор псевдослучайных чисел
      sensor.distance();                                                    //  запрашиваем значение расстояния
      delay(500);                                                           //  задержка 0.5сек
    }
    
    void loop() {
      //====================================================================//  
      //====================================================================//  
        if (sensor.distance() >= 1 && sensor.distance() <= 15) {            //  Проверяем, если расстояние до препятствия от 1 до 15см, то
          if(millis() > timer1 + hcsr_waiting_time_near || flg_near ==0){   //  проверяем, прошло ли заданное время после последней сработки, или сброшен ли флаг при первом запуске, и если да, то
            flg_near = true;                                                //  ставим флаг,
            timer1 = millis();                                              //  обновляем значение таймера,
            myDFPlayer.volume(20);                                          //  выставляем уровень громкости плеера
            myDFPlayer.play(4);                                             //  указываем плееру включить 4 трек
          }
        }
        if (sensor.distance() >= 30 && sensor.distance() <= 70) {           //  Проверяем, если расстояние до препятствия от 30 до 70 см, то
          if(millis() > timer2 + hcsr_waiting_time_far || flg_far ==0){     //  проверяем, прошло ли заданное время после последней сработки, или сброшен ли флаг при первом запуске, и если да, то
            flg_far = true;                                                 //  ставим флаг,
            timer2 = millis();                                              //  обновляем значение таймера,
            myDFPlayer.volume(15);                                          //  выставляем уровень громкости плеера
            myDFPlayer.play(5);                                             //  указываем плееру включить 5 трек
            l = 0;                                                          //  обнуляем переменную для изменения значения яркости светодиодов
          }
        }
      //====================================================================// 
      //====================================================================// 
      if (flg_near) {                                                       //  Если был установлен флаг ПРЕПЯТСТВИЕ БЛИЗКО, то
        if(millis() < timer1 + action_time){                                //  проверяем, прошло ли заданное время на выполнение анимации, и если нет, то
          //============================================================//  //  БЛОК РАБОТЫ ВНУТРЕННЕЙ ПОДСВЕТКИ
          if (millis() > timer3 + 500) {                                    //  Проверяем, прошло ли время с момента последнего выполнения блока, и если нет, то
            timer3 = 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);}     //  устанавливаем выбранный цвет для очередного светодиода
          }
          //============================================================//  //  БЛОК РАБОТЫ "ГЛАЗ" - эффект "бегающих" по кругу глаз
            p++;                                                            //  увеличиваем значение переменной на 1 при каждом выполнении цикла
            if (p == 4) p = 0;                                              //  при превышении значения сбрасываем его
            led.setColor(8 + p, 255, 0, 0);                                 //  указываем указанному светодиоду на левом модуле гореть красным
            led.setColor(12 + p, 255, 0, 0);                                //  указываем указанному светодиоду на правом модуле гореть красным
            led.setColor(7 + (p ? p : 4), 127, 127, 127);                   //  указываем всем остальным светодиодам на левом модуле гореть белым
            led.setColor(11 + (p ? p : 4), 127, 127, 127);                  //  указываем всем остальным светодиодам на правом модуле гореть белым
          //============================================================//
            led.write();                                                    //  записываем указанные цвета в модули
            delay(100);                                                     //  задержка 0.1сек
        } else {                                                            //  Если время выполнения анимации закончилось, то
          led.setColor(NeoPixelAll, 0, 0, 0);                               //  устанавливаем цвет всех светодиодов в чёрный(гасим)
          led.write();                                                      //  записываем указанный цвет в модули
          }
      }
      //====================================================================//  
      //====================================================================//  
      else if (flg_far) {                                                   //  Если был установлен флаг ПРЕПЯТСТВИЕ ДАЛЕКО, то
        if(millis() < timer2 + action_time){                                //  проверяем, прошло ли заданное время на выполнение анимации, и если нет, то
          if (!rev_flg) {                                                   //  проверяем, установлен флаг реверса, и если нет, то
            l++;                                                            //  увеличиваем значение яркости на 1 при кажом выполнении цикла
            r = l; g = 0; b = 0;                                            //  устанавливаем светодиодам внутри красный цвет, и задаём яркость
            for (uint8_t e = 0; e < 8; e++) {                               //  проходим по указанным светодиодам (установленным внутри)
              led.setColor(e, r, g, b);                                     //  задаём указанным светодиодам яркость и цвет
            }
            led.write();                                                    //  записываем указанный цвет и яркость в модули
            delay(19);                                                      //  задержка 19мск
            if (l == 255) rev_flg = 1;                                      //  при достижении максимального значения яркости устанавливаем флаг реверса
          } else {                                                          //  Если установлен флаг реверса, то
              l--;                                                          //  уменьшаем значение яркости на 1 при каждом выполнении цикла
              r = l; g = 0; b = 0;                                          //  устанавливаем светодиодам внутри красный цвет, и задаём яркость
              for (uint8_t e = 0; e < 8; e++) {                             //  проходим по указанным светодиодам (установленным внутри)
                led.setColor(e, r, g, b);                                   //  задаём указанным светодиодам яркость и цвет
              }
              led.write();                                                  //  записываем указанный цвет и яркость в модули
              delay(19);                                                    //  задержка 19мск
              if (l == 0) rev_flg = 0;                                      //  при достижении минимального значения яркости сбрасываем флаг реверса
            }
        } else {                                                            //  Если время выполнения анимации закончилось, то
          led.setColor(NeoPixelAll, 0, 0, 0);                               //  устанавливаем цвет всех светодиодов в чёрный(гасим)
          led.write();                                                      //  записываем указанный цвет в модули
        }
      }
    }

    Алгоритм работы скетча:

    До кода void setup() определяются переменные, подключаются необходимые библиотеки.

    В коде void setup() инициализируется MP3-плеер, адресные светодиоды NeoPixel, датчик расстояния, выключаются светодиоды, если до этого они горели.

    Код void loop() делится 2 части:

    • Проверяется, обнаружил ли датчик расстояния препятствие в одной из 2 указанных зон:
      • Если препятствие находится вблизи, то запускается трек 4, ставится флаг наличия препятствия flg_near в данной зоне, обнуляется счётчик времени timer1, фиксирующий наступление события, чтобы в следующий раз оно сработало через время hcsr_waiting_time_near;
      • Если препятствие находится вдалеке, то запускается трек 5, ставится флаг наличия препятствия flg_far в данной зоне, обнуляется счётчик времени timer2, фиксирующий наступление события, чтобы в следующий раз оно сработало через время hcsr_waiting_time_far;
    • Проверяется, был ли установлен флаг наличия препятствия:
      • Если препятствие было обнаружено вблизи (установлен флаг flg_near):
        • Проверяем, истекло время выполнения анимации timer1 + action_time или нет;
          • Если не истекло, то задаём режим работы светодиодных модулей;
          • Если истекло, то гасим все светодиоды;
      • Если препятствие было обнаружено вдалеке (установлен флаг flg_far):
        • Проверяем, истекло время выполнения анимации timer2 + action_time или нет;
          • Если не истекло, то задаём режим работы светодиодных модулей;
          • Если истекло, то гасим все светодиоды;

      Ссылки:

      Обсуждение