Бегущие под музыку огни
Содержание
ToggleЗадача
- Сделать реакцию ленты на звук как в вирусном видео (см. внизу страницы)
Базовые уроки
Подключение
- Лента подключается к внешнему питанию и пину D2
- В данной схеме Arduino питается от внешнего источника, подключение к USB необязательно
- Микрофон к питанию и аналоговому пину (А0)
- Пин Gain микрофона задаёт усиление звука (значения также написаны на модуле):
- Никуда не подключен (float) – 60dB (максимум)
- GND – 50dB
- VCC – 40dB
Библиотеки
Программа
Примечание: можно настроить нижний порог шумов, добавив строчку sound.setTrsh(величина)
в блок setup()
. По умолчанию порог задан 40, его можно увеличить, чтобы снизить чувствительность системы к шумам.
// бегущие частицы 1D, версия с FastLED #define STRIP_PIN 2 // пин ленты #define SOUND_PIN A0 // пин звука #define COLOR_STEP 151 // шаг цвета, интересные 151, 129 #define LEDS_AM 300 // количество светодиодов #define P_SPEED 2 // скорость движения #include <FastLED.h> CRGB leds[LEDS_AM]; #include "VolAnalyzer.h" VolAnalyzer sound(SOUND_PIN); byte curColor = 0; // текущий цвет void setup() { FastLED.addLeds<WS2812, STRIP_PIN, GRB>(leds, LEDS_AM); FastLED.setBrightness(255); // настройки анализатора звука sound.setVolK(15); // снизим фильтрацию громкости (макс. 31) sound.setVolMax(255); // выход громкости 0-255 sound.setPulseMax(200); // сигнал пульса sound.setPulseMin(150); // перезагрузка пульса } void loop() { if (sound.tick()) { // если анализ звука завершён (~10мс) // перематываем массив светодиодов на P_SPEED вправо for (int k = 0; k < P_SPEED; k++) { for (int i = LEDS_AM - 1; i > 0; i--) leds[i] = leds[i - 1]; } // резкий звук - меняем цвет if (sound.pulse()) curColor += COLOR_STEP; // берём текущий цвет с яркостью по громкости (0-255) CRGB color = CHSV(curColor, 255, sound.getVol()); // красим P_SPEED первых светодиодов for (int i = 0; i < P_SPEED; i++) leds[i] = color; // выводим FastLED.show(); } }
Вместо яркости-от-громкости будем варьировать все три параметра в пространстве HSV: цвет, насыщенность и яркость. Чем выше громкость – тем больше цвет (например от красного к жёлтому), чем выше громкость – тем меньше насыщенность (от цвета к белому), и наконец чем выше громкость, тем ярче. В FastLED это можно организовать так:
color = CHSV(START_HUE + vol / 5, 255 - vol / 2, vol);
где START_HUE
задаёт стартовый цвет (0.. 255), мы его смещаем на громкость / 5
. Насыщенность берём как 255 - громкость / 2
, и яркость по громкости как раньше. Полный код примера:
// бегущие частицы 1D, версия с FastLED, огненная палитра #define STRIP_PIN 2 // пин ленты #define SOUND_PIN A0 // пин звука #define LEDS_AM 300 // количество светодиодов #define P_SPEED 2 // скорость движения #define START_HUE 0 // цвет огня (0.. 255). 0 красный, 150 синий, 200 розовый #include <FastLED.h> CRGB leds[LEDS_AM]; #include "VolAnalyzer.h" VolAnalyzer sound(SOUND_PIN); byte curColor = 0; // текущий цвет void setup() { FastLED.addLeds<WS2812, STRIP_PIN, GRB>(leds, LEDS_AM); FastLED.setBrightness(255); // настройки анализатора звука sound.setVolK(15); // снизим фильтрацию громкости (макс. 31) sound.setVolMax(255); // выход громкости 0-255 sound.setPulseMax(200); // сигнал пульса sound.setPulseMin(150); // перезагрузка пульса } void loop() { if (sound.tick()) { // если анализ звука завершён (~10мс) // перематываем массив светодиодов на P_SPEED вправо for (int k = 0; k < P_SPEED; k++) { for (int i = LEDS_AM - 1; i > 0; i--) leds[i] = leds[i - 1]; } // цвет по огненной палитре int vol = sound.getVol(); CRGB color = CHSV(START_HUE + vol / 5, 255 - vol / 2, vol); // красим P_SPEED первых светодиодов for (int i = 0; i < P_SPEED; i++) leds[i] = color; // выводим FastLED.show(); } }
// бегущие частицы 1D, версия с MicroLED #define STRIP_PIN 2 // пин ленты #define SOUND_PIN A0 // пин звука #define COLOR_STEP 151 // шаг цвета, интересные 151, 129 #define LEDS_AM 300 // количество светодиодов #define P_SPEED 2 // скорость движения #define COLOR_DEBTH 2 #include <microLED.h> microLED < LEDS_AM, STRIP_PIN, -1, LED_WS2812, ORDER_GRB, CLI_AVER > strip; #include "VolAnalyzer.h" VolAnalyzer sound(SOUND_PIN); byte curColor = 0; // текущий цвет void setup() { strip.setBrightness(255); // настройки анализатора звука sound.setVolK(15); // снизим фильтрацию громкости (макс. 31) sound.setVolMax(255); // выход громкости 0-255 sound.setPulseMax(200); // сигнал пульса sound.setPulseMin(150); // перезагрузка пульса } void loop() { if (sound.tick()) { // если анализ звука завершён (~10мс) // перематываем массив светодиодов на P_SPEED вправо for (int k = 0; k < P_SPEED; k++) { for (int i = LEDS_AM - 1; i > 0; i--) { strip.leds[i] = strip.leds[i - 1]; } } // резкий звук - меняем цвет if (sound.pulse()) curColor += COLOR_STEP; // берём текущий цвет с яркостью по громкости (0-255) mData color = mWheel8(curColor, sound.getVol()); // красим P_SPEED первых светодиодов for (int i = 0; i < P_SPEED; i++) strip.set(i, color); // выводим strip.show(); } }
// бегущие частицы 1D, версия с MicroLED и асинхронным АЦП #define STRIP_PIN 2 // пин ленты #define SOUND_PIN 0 // аналоговый пин звука #define COLOR_STEP 151 // шаг цвета, интересные 151, 129 #define LEDS_AM 300 // количество светодиодов #define P_SPEED 2 // скорость движения #define COLOR_DEBTH 2 #include <microLED.h> microLED < LEDS_AM, STRIP_PIN, -1, LED_WS2812, ORDER_GRB, CLI_AVER > strip; #include "VolAnalyzer.h" VolAnalyzer sound; byte curColor = 0; // текущий цвет volatile int mins = 1023, maxs = 0; void setup() { strip.setBrightness(255); // настройки анализатора звука sound.setVolK(15); // снизим фильтрацию громкости (макс. 31) sound.setVolMax(255); // выход громкости 0-255 sound.setPulseMax(200); // сигнал пульса sound.setPulseMin(150); // перезагрузка пульса sound.setDt(0); // период 0 (ручной режим tick) sound.setWindow(2); // окно на 2 измерения, мы передаём их вручную // настройки АЦП ADMUX = DEFAULT << 6 | SOUND_PIN; // reference + пин ADCSRA = 1 << ADEN | 1 << ADATE | 1 << ADIE | 0b111; // запускаем асинх. режим на мин. скорости ADCSRA |= 1 << ADSC; // поехали } // прерывание АЦП по завершению измерения ISR(ADC_vect) { // ищем минимум и максимум if (mins > ADC) mins = ADC; if (maxs < ADC) maxs = ADC; } void loop() { // забираем значения noInterrupts(); // отключаем прерывания // читаем минимумы и максимумы int cmin = mins; int cmax = maxs; // сбрасываем для анализа в прерывании mins = 1023; maxs = 0; interrupts(); // включаем прерывания обратно // передаём в тик минимум и максимум sound.tick(cmin); sound.tick(cmax); // перематываем массив светодиодов на P_SPEED вправо for (int k = 0; k < P_SPEED; k++) { for (int i = LEDS_AM - 1; i > 0; i--) { strip.leds[i] = strip.leds[i - 1]; } } // резкий звук - меняем цвет if (sound.pulse()) curColor += COLOR_STEP; // берём текущий цвет с яркостью по громкости (0-255) mData color = mWheel8(curColor, sound.getVol()); // красим P_SPEED первых светодиодов for (int i = 0; i < P_SPEED; i++) strip.set(i, color); // выводим strip.show(); }
Более сложный вариант, позволяет выводить на ленту неограниченной длины. Позиции частиц хранятся в массиве, расчёт и отрисовка “хвостов” происходит при каждом выводе. Реакция только на “резкие звуки”.
// бегущие частицы 1D, потоковая версия #define STRIP_PIN 2 // пин ленты #define SOUND_PIN A0 // пин звука #define COLOR_STEP 10 // шаг цвета, интересные 151, 129 #define PART_SP 4 // скорость движения частиц #define LEDS_AM 500 // количество светодиодов #define PART_AM 50 // макс. количество частиц #define FADE_VAL 100 // скорость затухания /* // микролед #include <microLED.h> microLED < 0, STRIP_PIN, -1, LED_WS2812, ORDER_GRB, CLI_AVER > strip; */ // тинилед (чуть быстрее работает) #define TLED_PORT PORTD #define TLED_DDR DDRD #include "tinyLED.h" tinyLED<STRIP_PIN> strip; #include "VolAnalyzer.h" VolAnalyzer sound(SOUND_PIN); int partsPos[PART_AM]; // позиции частиц byte partsCol[PART_AM]; // цвет частиц byte curColor = 0; // текущий цвет int partsEx = 0; // количество активных частиц int tailSize = 0; // размер хвоста-градиента void setup() { //strip.setBrightness(255); // для тинилед не нужно, яркость и так максимум // настройки анализатор звука sound.setPulseMin(40); // громкость перезагрузки пульса sound.setPulseMax(60); // громкость активации пульса sound.setAmpliK(20); // плавность амплитуды sound.setVolK(10); // плавность громкости // определяем длину хвоста при текущих настройках tailSize = 1; byte color = 255; while (color > 0) { // пока яркость выше 0 color = fade8(color, FADE_VAL); // гасим tailSize++; // хвост растёт } } void loop() { if (sound.tick()) { // если анализ звука завершён (~10мс) // двигаем активные частицы for (int i = 0; i < partsEx; i++) partsPos[i] += PART_SP; // самая дальняя частица вышла за ленту if (partsPos[0] >= LEDS_AM) { // перематываем массив на ячейку влево for (int i = 0; i < PART_AM - 1; i++) { partsPos[i] = partsPos[i + 1]; partsCol[i] = partsCol[i + 1]; } partsEx--; // уменьшаем количество активных частиц } if (sound.pulse()) { // зарегистрирован скачок if (partsEx + 1 <= PART_AM) { // есть место в буфере partsEx++; // добавляем частицу partsPos[partsEx - 1] = 0; // её позиция partsCol[partsEx - 1] = curColor; // и цвет curColor += COLOR_STEP; // мотаем цвет } } // начинаем потоковую отправку strip.begin(); mData color = 0; // тут храним цвет int lastPart = partsEx - 1; // младшая частица // проходимся по всем светодиодам for (int i = 0; i < LEDS_AM; i++) { // хвост текущей активной частицы попадает на текущий светодиод if (lastPart >= 0 && i > partsPos[lastPart] - tailSize && i <= partsPos[lastPart]) { // личный цвет частицы color = mWheel8(partsCol[lastPart]); // гасим в разницу в позиции раз for (int k = 0; k < partsPos[lastPart] - i; k++) color = getFade(color, FADE_VAL); // позиция частицы совпала со светодиодом - переходим к следующей if (i == partsPos[lastPart]) lastPart--; } else color = 0; // иначе чёрный // включаем светодиод if (i < LEDS_AM) strip.send(color); } strip.end(); } }