산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

FHT 오디오 스펙트럼 비주얼라이저 - 멋진 LED 오디오 디스플레이 만들기

필요한 부품 및 소모품

Arduino Nano R3 1개
WS2812B 8x32 RGB LED 매트릭스 1개
저항 4.75k 옴 3개
저항 100k 옴 2개
저항 10k 옴 2개
저항 390 옴 (스루홀) 1개
1N4007 다이오드 (고전압/대전류) - 1N4004 대체 가능 1개
커패시터 100 nF 2개
커패시터 1000 µF 1개
푸시버튼 스위치 12mm (패널 버튼 2개로 대체 가능) 2개
4x6 cm 범용 프로토타입 보드 1개
PCB 스테레오 RCA 암 플러그 1개

필요한 도구 및 기계

프로젝트 소개

Arduino FHT 라이브러리

FHT(Fast Hartley Transform)는 더 잘 알려진 FFT(Fast Fourier Transform)와 동일한 역할을 하지만, FFT가 복소수 데이터를 사용하는 반면 FHT는 실수 데이터만 사용하므로 처리 능력과 메모리를 절반만 소모합니다.

이 라이브러리는 FFT 라이브러리처럼 실수 값과 허수 값용 두 개 배열 대신 단일 배열로 데이터 샘플링을 수행하며, 복잡한 부동소수점 로그 계산을 루프로 돌리는 대신 수치 테이블을 사용하여 계산을 단순화합니다.

단점은 샘플링에 사용된 배열 크기의 절반만 값을 반환하므로 오디오 범위 양 끝에서 일정 해상도와 정밀도 손실이 발생합니다. 하지만 측정 기기가 아닌 단순한 가젯을 만드는 것이 목적이라면 극도의 정밀도는 필요 없고, 음악 리듬에 맞춰 LED를 켜는 정도면 충분합니다.

FHT 라이브러리는 매우 빨라서 깜빡임을 줄이기 위해 코드에 지연을 추가해야 할 정도입니다. FFT 라이브러리 버전은 음악을 따라가는 듯 느려 보였던 반면, 이 버전은 훨씬 빠르고 반응성이 뛰어납니다. 시스템 리소스 사용이 적어 추가 기능 구현도 가능했습니다.

새 버전의 추가 기능

이전 버전과 하드웨어는 동일하므로, 기존 제작자는 버튼 하나와 10k 저항만 추가하고 코드만 업데이트하면 됩니다.

FHT 라이브러리 적용 외에 다음과 같은 기능이 추가되었습니다:

코드

FHT 코드 버전 (Arduino)
/* Copyright (c) 2020 Janux
   MIT License - 상업적 이용, 수정, 배포 가능 */

#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;
byte 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; // ADC 프리러닝 모드, 프리스케일러 32
  ADMUX = 0b00000000;  // A0 핀, 외부 전압 레퍼런스

  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); // FHT가 너무 빨라 깜빡임 방지용 지연
  }
}

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] = peaks[i] - 1;
    if (yvalue > peaks[i]) peaks[i] = yvalue;
    yvalue = peaks[i];
    setColumn(i, yvalue);
  }
  pixel.show();
}

void setColumn(byte x, byte y) {
  int led, i;
  for (i = 0; i < yres; i++) {
    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 < sizeof(CONFIGURATION); i++) {
      *((char*)&CONFIGURATION + i) = EEPROM.read(CONFIG_START + i);
    }
    return 1;
  }
  return 0;
}

void saveConfig() {
  CONFIGURATION.displaycolor = displaycolor;
  CONFIGURATION.brightness = brightness;
  for (unsigned int i = 0; i < sizeof(CONFIGURATION); i++)
    EEPROM.write(CONFIG_START + i, *((char*)&CONFIGURATION + i));
}

void showSettings(byte num, bool show) {
  if (show) {
    pixel.clear();
    if (num == 1 || num == 3) {
      drawChar(0, 0);
      drawChar(displaycolor + 1, 5);
    }
    if (num == 2 || num == 3) {
      drawChar(9, xres - 9);
      drawChar(brightness + 1, xres - 4);
    }
    delay(1000);
    pixel.clear();
  }
}

void drawChar(byte val, byte pos) {
  for (int x = 4; x >= 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();
      }
    }
  }
}

회로도

회로도 이미지 2장 참조

제조공정

  1. 32밴드 아두이노 오디오 스펙트럼 아날라이저 – 리니어 & 유사 로그 디스플레이
  2. 스마트 식물 갈증 알림기 – DIY 토양 수분 센서로 최적의 수분 관리
  3. 이탈리안 워드 클럭(Word Clock) 제작: 아두이노 나노 + 네오픽셀 + LCD + RTC
  4. 아두이노 MKR Fox 1200으로 신뢰성 높은 시그폭스 kWh 미터 만들기
  5. 블루투스 온도 모니터: 아두이노 나노와 NTC 서미스터로 구현
  6. 터치프리 제스처 잠금장치: APDS-9960과 아두이노로 보안 액세스 구현
  7. 아두이노 나노 컴패니언 키트 – DIY 전자 작업을 위한 필수 부품 및 도구
  8. 아두이노 마이크로/레오나르도용 USB MIDI 어댑터 키트 – 전체 부품 리스트
  9. 아두이노용 절연 아날로그 입력 회로 만들기
  10. 아두이노 우노로 정밀 반응 속도 테스터 만들기