Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

FHT Аудио-спектр: Создаем потрясающий LED-дисплей для музыки

Компоненты и материалы

Arduino Nano R3 ×1
WS2812B 8×32 RGB LED Matrix (256 светодиодов) ×1
Резистор 4,75 кОм ×3
Резистор 100 кОм ×2
Резистор 10 кОм ×2
Резистор 390 Ом (просверленный) ×1
Диод 1N4007 (подойдет 1N4004) ×1
Конденсатор 100 нФ ×2
Конденсатор 1000 мкФ ×1
Тактильная кнопка 12 мм (или панельные для коробка) ×2
Монтажная плата 4×6 см ×1
RCA-разъем (стерео, мама) для PCB ×1

Необходимые инструменты

О проекте

Библиотека Arduino FHT

FHT (Fast Hartley Transform) выполняет ту же задачу, что и FFT (Fast Fourier Transform), но работает только с вещественными данными, в то время как FFT оперирует комплексными числами. Поэтому FHT требует в 2 раза меньше процессорного времени и памяти.

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

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

Подробнее о работе FHT см. на Open Music Labs — там документация, описание функций и примеры для Arduino.

FHT настолько быстра, что в код пришлось добавить задержку для уменьшения мерцания. Результат — дисплей реагирует на музыку мгновенно, без заметного лага, характерного для FFT-версий. Освобожденные ресурсы позволили добавить новые функции.

Новая версия

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

Добавленные функции:

Код

FHT Code Version (Arduino)

/* Copyright (c) 2020 Janux. MIT License.
Based on MAX72xx LED matrix + FFT lib by Shajeeb.
Config section based on work of Ragnar Ranøyen Homb (Norwegian Creations). */

#define LIN_OUT 1
#define FHT_N 128
#include <FHT.h>

#define xres 32
#define yres 8
#define ledPIN 6
#define NUM_LEDS (xres * yres)
#include <Adafruit_NeoPixel.h>

#define colorPIN 5
#define brightnessPIN 10
byte displaycolor = 0;
byte brightness = 1;

#include <EEPROM.h>
#define CONFIG_START 32
#define CONFIG_VERSION "VER01"
typedef struct {
  char version[6];
  byte displaycolor;
  byte brightness;
} configuration_type;
configuration_type CONFIGURATION = { CONFIG_VERSION, displaycolor, brightness };

byte yvalue;
int peaks[xres];
byte state = HIGH, previousState = LOW;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 100;
byte data_avgs[xres];

Adafruit_NeoPixel pixel = Adafruit_NeoPixel(NUM_LEDS, ledPIN, NEO_GRB + NEO_KHZ800);

byte eq[32] = {
  60, 65, 70, 75, 80, 85, 90, 95, 100, 100, 100, 100, 100, 100, 100, 100,
  100, 100, 100, 100, 100, 100, 100, 100, 115, 125, 140, 160, 185, 200, 200, 200
};
bool EQ_ON = true;

byte colors[][8] = {
  {170, 160, 150, 140, 130, 120, 1, 1},
  {1, 5, 10, 15, 20, 25, 90, 90},
  {90, 85, 80, 75, 70, 65, 1, 1},
  {90, 90, 90, 30, 30, 30, 1, 1},
  {170, 160, 150, 140, 130, 120, 110, 0}
};

byte charBitmap[] = {
  0x1C, 0x10, 0x10, 0x10, 0x10, 0x1C,
  0x08, 0x18, 0x08, 0x08, 0x08, 0x1C,
  0x0C, 0x12, 0x04, 0x08, 0x10, 0x1E,
  0x0C, 0x12, 0x02, 0x06, 0x12, 0x0C,
  0x10, 0x10, 0x10, 0x14, 0x1E, 0x04,
  0x1E, 0x10, 0x1E, 0x02, 0x12, 0x0C,
  0x1E, 0x10, 0x10, 0x1E, 0x12, 0x1E,
  0x1E, 0x02, 0x04, 0x08, 0x08, 0x08,
  0x0C, 0x12, 0x0C, 0x12, 0x12, 0x0C,
  0x1C, 0x12, 0x1C, 0x12, 0x12, 0x1C
};

void setup() {
  pixel.begin();
  ADCSRA = 0b11100101; // free running, prescaler 32
  ADMUX  = 0b00000000; // A0, external reference
  if (loadConfig()) {
    displaycolor = CONFIGURATION.displaycolor;
    brightness = CONFIGURATION.brightness;
  }
  pixel.setBrightness(brightness * 24 + 8);
  showSettings(3, true);
}

void loop() {
  while (1) {
    Sampling();
    RearrangeFHT();
    SendToDisplay();
    colorChange();
    brightnessChange();
    delay(10);
  }
}

void Sampling() {
  for (int i = 0; i < FHT_N; i++) {
    while (!(ADCSRA & 0x10));
    ADCSRA = 0b11110101;
    byte m = ADCL;
    byte j = ADCH;
    int value = (j << 8) | m;
    value -= 0x0200;
    value <<= 6;
    fht_input[i] = value / 8;
  }
  fht_window();
  fht_reorder();
  fht_run();
  fht_mag_lin();
}

void RearrangeFHT() {
  int step = (FHT_N / 2) / xres;
  int c = 0;
  for (int i = 0; i < (FHT_N / 2); i += step) {
    data_avgs[c] = 0;
    for (int k = 0; k < step; k++)
      data_avgs[c] += fht_lin_out[i + k];
    data_avgs[c] /= step;
    c++;
  }
}

void SendToDisplay() {
  for (int i = 0; i < xres; i++) {
    if (EQ_ON) data_avgs[i] = data_avgs[i] * (float)eq[i] / 100;
    data_avgs[i] = constrain(data_avgs[i], 0, 80);
    data_avgs[i] = map(data_avgs[i], 0, 80, 0, yres);
    yvalue = data_avgs[i];
    peaks[i]--;
    if (yvalue > peaks[i]) peaks[i] = yvalue;
    yvalue = peaks[i];
    setColumn(i, yvalue);
  }
  pixel.show();
}

void setColumn(byte x, byte y) {
  for (byte i = 0; i < yres; i++) {
    int led = GetLedFromMatrix(x, i);
    if (peaks[x] > i) {
      switch (displaycolor) {
        case 4:
          if (colors[displaycolor][i] == 0)
            pixel.setPixelColor(led, 255, 255, 255);
          else
            pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
          break;
        case 5:
          pixel.setPixelColor(led, Wheel(x * 16));
          break;
        case 6:
          pixel.setPixelColor(led, Wheel(i * y * 3));
          break;
        case 7:
          if (i < yres - 2) {
            if (x & 0x01) pixel.setPixelColor(led, 0, 0, 255);
            else pixel.setPixelColor(led, 255, 0, 0);
          } else pixel.setPixelColor(led, 255, 255, 255);
          break;
        default:
          pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
      }
    } else {
      pixel.setPixelColor(led, pixel.Color(0, 0, 0));
    }
  }
}

int GetLedFromMatrix(byte x, byte y) {
  int led;
  x = xres - x - 1;
  if (x & 0x01) led = ((x + 1) * yres - y - 1);
  else led = ((x + 1) * yres - yres + y);
  return constrain(led, 0, NUM_LEDS);
}

void colorChange() {
  int reading = digitalRead(colorPIN);
  if (reading == HIGH && previousState == LOW && millis() - lastDebounceTime > debounceDelay) {
    displaycolor++; if (displaycolor > 7) displaycolor = 0;
    showSettings(1, true); saveConfig(); lastDebounceTime = millis();
  }
  previousState = reading;
}

void brightnessChange() {
  int reading = digitalRead(brightnessPIN);
  if (reading == HIGH && previousState == LOW && millis() - lastDebounceTime > debounceDelay) {
    brightness++; if (brightness > 7) brightness = 0;
    pixel.setBrightness(brightness * 24 + 8);
    showSettings(2, true); saveConfig(); lastDebounceTime = millis();
  }
  previousState = reading;
}

unsigned long Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85) return pixel.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  if (WheelPos < 170) { WheelPos -= 85; return pixel.Color(0, WheelPos * 3, 255 - WheelPos * 3); }
  WheelPos -= 170; return pixel.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

int loadConfig() {
  if (EEPROM.read(CONFIG_START+0)==CONFIG_VERSION[0] &&
      EEPROM.read(CONFIG_START+1)==CONFIG_VERSION[1] &&
      EEPROM.read(CONFIG_START+2)==CONFIG_VERSION[2] &&
      EEPROM.read(CONFIG_START+3)==CONFIG_VERSION[3] &&
      EEPROM.read(CONFIG_START+4)==CONFIG_VERSION[4]) {
    for (unsigned int i=0; i=0; x--) {
    for (int y=5; y>=0; y--) {
      if ((charBitmap[val*6+5-y] >> x) & 0x01) {
        pixel.setPixelColor(GetLedFromMatrix(4-x+pos, y+1), Wheel((pos>10)*170));
        pixel.show();
      }
    }
  }
}

Схемы

FHT Аудио-спектр: Создаем потрясающий LED-дисплей для музыки
FHT Аудио-спектр: Создаем потрясающий LED-дисплей для музыки

Производственный процесс

  1. 32-полосный Arduino аудио-спектральный анализатор — линейный и псевдологарфмический дисплей
  2. Умный датчик полива «Thirst Alert» на Arduino — DIY сигнализация засухи для растений
  3. Итальянские словесные часы на Arduino Nano, NeoPixel, LCD и RTC
  4. Надежный счетчик кВт·ч на Sigfox с Arduino MKR Fox 1200
  5. Bluetooth-монитор температуры на Arduino Nano с NTC-термистором
  6. Бесконтактный жестовый замок: APDS-9960 + Arduino для безопасного доступа
  7. Набор-компаньон для Arduino Nano — необходимые компоненты и инструменты для DIY-электроники
  8. Набор USB-MIDI адаптера для Arduino Micro / Leonardo — полный список деталей
  9. Гальванически разделённый аналоговый вход для Arduino на LM331 и оптопаре 4N25
  10. Точный тест времени реакции на Arduino UNO