Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio

Componentes y suministros

Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio Arduino Nano R3 × 1
Matriz LED RGB WS2812B 8x32 × 1
Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio Resistencia 4.75k ohm × 3
Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio Resistencia 100k ohm × 2
Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio Resistencia 10k ohm × 2
Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio Resistencia 390 ohm (agujero pasante) × 1
Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio Diodo 1N4007 – Alto voltaje/corriente (incluso 1N4004 sirve) × 1
Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio Capacitor 100 nF × 2
Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio Capacitor 1000 µF × 1
Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio Pulsador 12mm SparkFun (o dos pulsadores de panel si vas a meter el circuito en una caja) × 2
Placa de prototipo genérica 4x6 cm × 1
Conector RCA Estéreo Hembra para PCB × 1

Herramientas y máquinas necesarias

Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio Soldador (genérico)
Cable Jack 3.5mm a RCA Estéreo

Sobre este proyecto

La librería Arduino FHT

La FHT (Transformada Rápida de Hartley) hace el mismo trabajo que la más conocida FFT (Transformada Rápida de Fourier), pero a diferencia de esta, usa solo datos reales, mientras que FFT trabaja con datos complejos. Por tanto, FHT usa la mitad de potencia de procesamiento y la mitad de memoria del sistema.

Esta librería usa un solo array para muestreo en lugar de dos (uno para valores reales y otro para imaginarios, usado por FFT). También simplifica cálculos matemáticos complejos evitando bucles con cálculos logarítmicos de punto flotante complejos, usando en su lugar tablas numéricas.

La desventaja es que siempre devuelve la mitad de valores del tamaño del array usado para muestreo, lo que conlleva cierta pérdida de resolución y precisión en los extremos del rango de audio. Sin embargo, para nuestro caso -no un instrumento de medición sino un gadget simple- no necesitamos precisión extrema, solo encender LEDs al ritmo de la música.

Quien quiera profundizar puede consultar el sitio Open Music Lab con documentación completa, explicación de funciones y ejemplos genéricos para Arduino.

La librería FHT es tan rápida que fue necesario añadir un delay en el código para reducir parpadeo. El resultado es una visualización mucho más rápida y responsiva que la versión con FFT, que a menudo parecía ir retrasada respecto a la música. El menor uso de recursos permitió añadir código para gestionar funciones adicionales.

La nueva versión

El proyecto es idéntico a la versión anterior; quienes ya la construyeron solo necesitan añadir un botón, una resistencia de 10K y actualizar el código.

Además de implementar la librería FHT, esta versión incluye:

Código

Versión código FHT (Arduino)

/* Copyright (c) 2020 Janux
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Basado en proyecto original para matriz MAX72xx y lib FFT de Shajeeb.
Sección de configuración basada en trabajo de Ragnar Ranøyen Homb (Norwegian Creations).
*/

#define LIN_OUT 1      // Salida lineal magnitud FHT
#define FHT_N 128      // Muestras para FHT, debe ser potencia de 2
#include <FHT.h>

#define xres 32        // Columnas totales en display, <= MUESTRAS/2
#define yres 8         // Filas totales en display
#define ledPIN 6       // Pin salida control LEDs
#define NUM_LEDS (xres * yres) // Total LEDs en matriz

#include <Adafruit_NeoPixel.h>

#define colorPIN 5         // Pin cambio color LED
#define brightnessPIN 10   // Pin cambio brillo

byte displaycolor = 0; // Valor color por defecto
byte brightness = 1;   // Nivel brillo por defecto

#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);

// Filtro ecualizador
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; // false para desactivar eq

// 5 sets de colores para LEDs, 0 = color personalizado
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}
};

// Caracteres para mostrar ajustes
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();
  
  // Configuración ADC para modo free-running
  ADCSRA = 0b11100101; // free-running, prescaler 32 (0xe5)
  ADMUX = 0b00000000;  // Pin A0, referencia externa
  
  // Leer config de EEPROM
  if (loadConfig()) {
    displaycolor = CONFIGURATION.displaycolor;
    brightness = CONFIGURATION.brightness;
  }
  
  pixel.setBrightness(brightness * 24 + 8);
  showSettings(3, true); // Cambia a false si no quieres mostrar al inicio
}

void loop() {
  while (1) {
    Sampling();
    RearrangeFHT();
    SendToDisplay();
    colorChange();
    brightnessChange();
    delay(10); // Reduce parpadeo (FHT es muy rápido)
  }
}

void Sampling() {
  for (int i = 0; i < FHT_N; i++) {
    while (!(ADCSRA & 0x10)); // Esperar fin conversión ADC (bit ADIF)
    ADCSRA = 0b11110101;      // Limpiar bit ADIF (0xf5)
    byte m = ADCL;            // Leer dato ADC bajo
    byte j = ADCH;            // Leer dato ADC alto
    int value = (j << 8) | m; // Formar int de 10 bits (ADLAR=0)
    value -= 0x0200;          // Convertir a signed int
    value <<= 6;              // Formar signed int de 16 bits
    fht_input[i] = value / 8; // Copiar a array entrada FHT comprimiendo
  }
  
  // Proceso FHT
  fht_window();   // Ventana para mejor respuesta frecuencial
  fht_reorder();  // Reordenar antes de FHT
  fht_run();      // Procesar datos en FHT
  fht_mag_lin();  // Magnitud lineal de salida
}

void RearrangeFHT() {
  // FHT devuelve valores reales en un solo array
  // Tras fht_mag_lin(), muestras en primeras FHT_N/2 posiciones de fht_lin_out[]
  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; // Promedio
    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]--; // Decaimiento
    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: // Color personalizado
          if (colors[displaycolor][i] == 0) {
            pixel.setPixelColor(led, 255, 255, 255); // Blanco
          } else {
            pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
          }
          break;
        case 5: // Color por columna
          pixel.setPixelColor(led, Wheel(x * 16));
          break;
        case 6: // Color por fila
          pixel.setPixelColor(led, Wheel(i * y * 3));
          break;
        case 7: // Patrón especial (banderas)
          // Bandera Italia
          // if (x < 11) pixel.setPixelColor(led, 0, 255, 0);
          // if (x > 10 && x < 21) pixel.setPixelColor(led, 255, 255, 255);
          // if (x > 20) pixel.setPixelColor(led, 255, 0, 0);
          // Bandera EE.UU.
          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: // Colores definidos en array
          pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
      }
    } else {
      pixel.setPixelColor(led, pixel.Color(0, 0, 0)); // Apagado
    }
  }
}

// Calcular número LED por coordenadas x,y
// Válido para WS2812B layout serpentinado horizontal, LED 0 en esquina inf. der. (DIN a la derecha)
// x: 0 a xres-1, y: 0 a yres-1
// Retorna: 0 a NUM_LEDS-1
int GetLedFromMatrix(byte x, byte y) {
  int led;
  x = xres - x - 1;
  if (x & 0x01) { // Columnas impares aumentan hacia atrás
    led = ((x + 1) * yres - y - 1);
  } else { // Columnas pares aumentan normalmente
    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;
}

// Utilidad Adafruit Neopixel: Input 0-255 -> valor color
// Transición R - G - B - vuelta a R
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));
}

// 1 = mostrar nivel color, 2 = mostrar nivel brillo, 3 = ambos
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();
      }
    }
  }
}

// by Janux®, Última versión 28/06/2020.

Esquemáticos

Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio Visualizador de espectro de audio FHT – Construye una impresionante pantalla LED de audio

Proceso de manufactura

  1. Analizador de espectro de audio Arduino de 32 bandas – Visualización lineal y pseudo-logarítmica
  2. Alerta de Sed Inteligente para Plantas: Sensor DIY para Riego Óptimo
  3. Reloj de Palabras en Italiano con Arduino Nano, NeoPixel, LCD y RTC
  4. Medidor kWh Sigfox Fiable con Arduino MKR Fox 1200
  5. Monitor de temperatura con Bluetooth usando Arduino Nano
  6. Cerradura por Gestos Sin Contacto: Acceso Seguro con APDS-9960 y Arduino
  7. Kit Compañero Arduino Nano: Componentes y Herramientas Esenciales para Electrónica DIY
  8. Kit Adaptador USB MIDI para Arduino Micro / Leonardo: Lista Completa de Componentes
  9. Circuito de entrada analógica aislada para Arduino
  10. Visualizador Espectro Audio RGB 32 Bandas - Matriz LED Arduino
  11. Test de Precisión de Tiempo de Reacción con Arduino UNO