Audio SmartLedBars

Generalidades-

Este proyecto es el fruto de una idea que empecé a desarrollarla hace un año con una primera expectativa de practicar con FastLed y hacerme mi propio vu-meter. Pero la cosa se fue complicando positivamente en mi afan de encontrar un resultado sencillamente satisfactorio..

Con este hardware que doy por terminado,comienza asi mismo buenas posibilidades de practicar con el software del Ide Arduíno, a modo de entrenamiento o campo de prácticas como el que expongo yo,que por supuesto es libre de ser copiado y transformado, con el agradecimiento quien le pueda interesar,que me mantenga informado para ponerlo yo tambien en práctica..

Al principio comence utilizando lo que encontraba por Git-Hub, pero finalmente, a excepción de lo basico, el resultado final es bastante personal.

¿Por donde empiezo?….

Bueno, visionando el video ya esta casi todo dicho:

Como idea general lo que hago es separar el audio en dos salidas que representan las freq mas bajas y las freq mas altas a modo de bajos y altos,a traves de un proceso de audio que explicaré y que será entregado a un modulo ESP32 “H&L”, con otro modulo ESP32 que denomino,CTROL, se encargara de controlar automaticamente la ganancia de los convertidores de nivel antes de su entrega al H&L.

El entendimiento entre ambos módulos se hace tambien a traves de las salidas DAC (25,26) de H&L conectadas a dos entradas analógicas del modulo CTROL a modo de “feedback”. Con la combinación de ambos y un software apropiado haremos que H&L controle el ritmo de la música y CTROL que supervise la altura de los bajos y los altos sincronizandose entre ellos. Así, el unico ajuste que hay que hacer es vigilar el volumen utilizado.

Recordemos que no vamos a construir un simple vumeter, se trata de un cojunto armonioso audio/visual entre los bajos y los altos que no haga mas divertida nuestra habitación junto a nuestro equipo de música.

La caja de control dispondrá de una pantalla oled para el ajuste y la puesta a punto ,ademas de tres potenciometros de uso a la carta,necesario para la práctica del software.

Formar dos columnas cilindricas de 1 metro de altura con cinco perfiles de aluminio que encierra cinco tiras led de 144 elementos sobre una base fabricada con pla en una impresora 3D;

Las dos barras mas adelantadas estan alimentadas por la salida que representa el HPF (filtro de paso alto) y las tres mas atrasadas representan al LPF(filtro de paso bajo).

Tratamiento y proceso de audio

-Proceso de audio: la entrada de audio puede ser interna a traves de un módulo receptor BT VHM-314 o externa procedente de una salida de auriculares de otro dispositivo. La conmutación de uno a otro se hace con un simple interruptor.Al mismo tiempo en paralelo esta la salida de audio para un amplificador externo. De una u otra fuente, la señal stereo se convierte en mono a traves de una red de dos resistencias de 10K cuyo punto central con respecto al comun o tierra se dirigiran simultáneamente a dos amplificadores como se puede ver en este bloquede abajo.

Las señales de audio se conducen a dos redes pasivas de filtros HPF y LPF constituidas por un transformador de aislamiento 600:600 ohmios ,un potenciometro de ajuste multivueltas y un condensador Cx/Cy cuyos valores elegidos son; C47 uF para el LPF y C4.7 nF para el HPF. De ahi pasaremos a los convertidores de nivel (0-3.2V) . La ganancia/amplificación de estos convertidores esta controlada por las salidas DAC del ESP32 de control (CTROL). Asi evitamos potenciometros manuales de ajuste. Será nuestra programacion la que se encarge de su control.

Aqui vemos la parte trasera de la VuBOX, entrada y salida de audio, interruptor de selección BT o externa,interruptor on/off,salida/entrada usb power, y conexión con las tiras Led

En este diagrama de bloques general podemos continuar con el flujo de señal a la salida de los convertidores que no es mas que un nivel de tensión que se conecta dos entradas analógicas del ESP32 H&L

Esquema de bloques completo con todas las relaciones entre H&L y CTROL. El software asociado no desarrolla todas sus posibilidades como el uso de una bateria externa y su monitorización en la pantalla oled.

Filtros HPF y LPF:

Una vez elegí los valores del filtro pasivo, y teniendo en cuenta la dependencia con los niveles de entrada de audio, esto es, el volumen utilizado , hago un pequeño estudio-que omito- tomando un volumen del 80% para al final encontrar los margenes de trabajo donde la salida de cada filtro alcanza sus valores minimo y maximo, valores digitales (0-255) para las salidas DAC (25,26) conectadas a los convertidores de nivel respectivos, y estos fueron;

  • VALORES LPF: 130-146
  • VALORES HPF; 97-132

Quiere decir, que voy a trabajar con los valores dentro de estos márgenes para controlar la ganancia de forma que;

dacWrite(25, gain_lpf);
dacWrite(26, gain_hpf);

Esmuy importante señalar que hice el ajuste tomando un valor de alimentación a la placa entre 4.8 y 4.9 voltios.

El estudio de las frecuencias de corte merecería un tutorial aparte. y prefiero guardarmelo para mi para no pecar de tedioso.

Ide Arduino:

En adelante nombraré como “HL” el modulo asociado al control ws2812b y “CTROL” al módulo asociado al control de ganancia de los convertidores de nivel.

La idea es no tener que hacer ajustes manuales con los potenciometros usados a excepción de la puesta en marcha y su ajuste. Finalmente se usa un potenciometro para hacer los cambios de display de la pantalla oled y dos mas para ayudar a mejorar el software cuando se requiera.

El enlace entre módulos se realiza a través de las salidas DAC de HL que da un un feedback a CTROL.

Básicamente ;

  • HL controla la cadencia ritmica lumínica.
  • CTROL vigila la altura de leds y coordina la secuencia de la FQ altas y FQ bajas

Software para ESP32- CTROL

/*This software is a part of my project "AudioSmartLedBars" owned by *f81@crisalctime.com. corresponding to the ESP32 CTROL module 
 *The working margins of the LPF and HPF, hpf(138-146) and 
 * lpf(97-132), digital values applied to the DAC outputs 25,26 of the module converted 
 * into analog values between 0-3V that will be applied to the converters to control its gain.
 *The analog inputs 36 and 34 where we define the Dlin and Dhin variables are connected 
 to the DAC outputs of the L&H module so that by adding both, they generate the "timing" 
 variable used to balance the LPF gain and automatically adjust the HPF input gain
*/


#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h> 
#include <FastLED.h>
#define Dlin  (analogRead(36))//conectada con output25 H&L
#define Dhin  (analogRead(34))//conectada con output26 H&L
#define Input39 (analogRead(39))//CLOW 
//#define Input14 (analogRead(14))//ctrl tension bateria conectado a un divisor de tension
#define Input32 (analogRead(32)) // screen change
#define Input15  (analogRead(15))// mode change
//#define pOne 13   //13ctrol
//#define one_switch (!digitalRead(pOne)) when requiered

int w,z=1;
int lpf,hpf,nhpf,nlpf,nvbat,vbat,gFH,gFL;
int ctrol_low,ctrol_high,input32,input39,input15;
uint32_t dlin,dhin,vol;
Adafruit_SSD1306 oled_1(128,64, &Wire,-1);
int gain_lpf,gain_hpf,nvol,timing,gL,gH,height_L,height_H;


void setup() {

  Serial.begin(9600);
  oled_1.begin(SSD1306_SWITCHCAPVCC,0x3C);//inicialización de pantalla en direccion 0x3c
  //oled_1.setRotation(2);
 oled_1.setTextColor(WHITE); 
    oled_1.clearDisplay();
  oled_1.display(); 
  //   pinMode(pOne,INPUT_PULLUP);
      analogSetPinAttenuation (34, ADC_11db);
  analogSetPinAttenuation (36, ADC_11db);
    analogSetPinAttenuation (39, ADC_11db);
     //analogSetPinAttenuation (14, ADC_11db);
     analogSetPinAttenuation (32, ADC_11db);
     analogSetPinAttenuation (15, ADC_11db);
   }


   
void loop() {
  dlin=map(Dlin,0,4095,0,410);//
   dhin=map(Dhin,0,4095,0,410);
 dacWrite(25, gain_lpf);
 dacWrite(26, gain_hpf);
vol=dlin+dhin;
nvol= map(vol,0,810,0,100);
input32=map(Input32,0,4095,0,100);//screen change
input15=map(Input15,0,4095,0,100);//mode change

  
          ////////////////////////////timing & Gain Ctrol////////////////////////////
         
         w=map(input15,0,100,1,2);


 if (w==1){
    
 /*First we define two new variables, height_L and height_H, with values ​​already experienced as acceptable
Intervening in the gain mappings-In theory as the mapping is from “dlin” and dhin”, these values ​​should be =410 
as defined, but they are truncated on purpose to speed up the passing of the values ​​to gL and gH
Timing will now only provide up to a 400msec delay when vol is maxed out, smoothing the light response endings
with H,L (H=Freq high, L=Freq low).The anomaly that makes it work acceptably is that the lpf gain depends on dhin,
when H is low the gain will be at its maximum, like the gain H, the variables gL and gH are the mapping limit switches 
and are proportional to their respective “dlin, dhin” With the indicated values ​​and that are put into practice with
good results. When H rises, the gain L will take decreasing values ​​and vice versa within the range of values ​​experienced.
*/


 height_L=200;
height_H=350;
timing=map (nvol,0,100,0,400);//nos da el tiempo de balanceo de graves de mayor a menor amplitud    
if(timing<25){timing=0;}
gL=map(dlin,0,400,142,140);
gH=map(dhin,0,400,125,115);
gain_lpf=map(dhin,0,height_L,146,gL);
gain_hpf=map(dhin,0,height_H,130,gH);//
delay(timing);
 }

 if (w==2){
/*These instructions will produce the same effect as above but in a simpler way.
  */
 timing=dlin ;    
 if(timing<25){timing=0;}
 delay(timing);
 gL=map(dlin,0,410,410,dhin);
 if (timing>gL){gain_lpf=139;}else { gain_lpf=144; }
 gH=map(dhin,0,410,130,115);
 gain_hpf=map(timing,0,410,gH,120);
 ///////////////////////////////////// Display///////////////////////

 

 }

 
                           z=map(Input32,0,4095,1,3);  

   
////////////////////////////////////////

if (z==1){
 oled_1.clearDisplay(); oled_1.setTextColor(WHITE); 
     oled_1.setTextSize(2);oled_1.setCursor(1,2); oled_1.print("Timing");
     oled_1.setTextSize(3);
         oled_1.setCursor(1,40);
         oled_1.print(timing);
         oled_1.setCursor(75,40);
      oled_1.print("ms");
      
                   
        oled_1.display(); }
        

if (z==2){oled_1.clearDisplay(); oled_1.setTextColor(WHITE); 
     oled_1.setTextSize(2);oled_1.setCursor(5,2); oled_1.print("LPF GAIN");
     oled_1.setCursor(10,20);oled_1.print("138-146");
             oled_1.setCursor(35,50);
           oled_1.print(gain_lpf);
   // oled_1.setCursor(75,40);
    //oled_1.print("dac");
        oled_1.display(); }


if (z==3){ oled_1.clearDisplay(); oled_1.setTextColor(WHITE); 
     oled_1.setTextSize(2);oled_1.setCursor(5,2); oled_1.print("HPF GAIN");
     oled_1.setCursor(10,20);oled_1.print(" 97-132");
             oled_1.setCursor(35,50);
           oled_1.print(gain_hpf);
    //  oled_1.setCursor(75,40);
  //    oled_1.print("%");
      oled_1.display(); } 
       
  


        

                        
Serial.println(timing);


 }

Software para ESP32 H&L

/*Software for the ESP32 module corresponding to the original project "AudioSmartLedBars" owned by f81@crisalctime.com. 
 * This project is open and you can use it freely.
 * This sketch is responsible for controlling the two ws2812B outputs corresponding to the input of the HPF and LPF filters 
 as well as directing the rhythmic cadence so that it does not look like a simple vumeter to generate a character of its own for
 each audible piece.
 
*/


#include <FastLED.h>
# define LPF_IN_PIN 34             // converter lpf 
# define HPF_IN_PIN 36            // converter hpf 
#define DATA_PIN_LPF    13
#define DATA_PIN_HPF    2
#define N_PIXELS    144
CRGB ledslpf[N_PIXELS];
CRGB ledshpf[N_PIXELS];
#define Inputlpf  (analogRead(LPF_IN_PIN))
#define Inputhpf  (analogRead(HPF_IN_PIN))
#define pOne 19  //19HL 
#define switch (!digitalRead(pOne))//WHEN REQUIRED
#define Input12  (analogRead(12)) //12HL in electronic board

// some of this variables can no be in use
int  ctrl_brigthness;//255;
uint8_t hue,hue2;
uint16_t analog_L,analog_H;
uint8_t inputlpf,inputhpf,D_hpf,D_lpf;
uint8_t lpf,hpf,slow;
uint32_t tempo1,tempo2;
uint32_t tref1,tref2;

///////////////////////rythms ////////////////
int w=1;
int z=2;
uint32_t v1,v2;
uint8_t x1,x2,x3,x4,output25,output26;
uint32_t aut_delay_lpf,aut_delay_hpf,input12;


void setup() {
  Serial.begin(9600);
  pinMode(pOne,INPUT_PULLUP);

   FastLED.addLeds<NEOPIXEL, DATA_PIN_LPF>(ledslpf, N_PIXELS);
   FastLED.addLeds<NEOPIXEL, DATA_PIN_HPF>(ledshpf,N_PIXELS);
   FastLED.clear();
      FastLED.show();
        
  analogSetPinAttenuation (34, ADC_11db);
   analogSetPinAttenuation (36, ADC_11db);
   analogSetPinAttenuation (12,ADC_11db);
}
 

void loop() {
 FastLED.setBrightness(ctrl_brigthness);
 // ctrl_brigthness=map(lpf+hpf,0,2*N_PIXELS,0,200);
  Vumeter();

FastLED.show();
dacWrite(25, output25);
 dacWrite(26, output26);

  }
  void Vumeter(){
    
 inputlpf =map(Inputlpf,0,4095,0,N_PIXELS);
 inputhpf =map(Inputhpf,0,4095,0,N_PIXELS);
          
   
  if (inputlpf>lpf) {lpf+=1;}else if (inputlpf<lpf) {lpf-=1;}
  if (lpf < 1) {  lpf = 1;  } 
  if (lpf>N_PIXELS){lpf=N_PIXELS;}

  if (inputhpf>hpf) {hpf+=1;}else if (inputhpf<hpf) {hpf-=1;}
  if (hpf < 1) {  hpf = 0;  } 
  if (hpf>N_PIXELS){hpf=N_PIXELS;}
  
  if (Inputlpf>analog_H){analog_L++;} else {analog_L--;}
  if (analog_L>N_PIXELS){analog_L=N_PIXELS;}
  if (analog_L < 1) { analog_L = 0;  } 

  if (Inputhpf>analog_H){analog_H++;} else {analog_H--;}
  if (analog_H>N_PIXELS){analog_H=N_PIXELS;}
  if (analog_H < 1) { analog_H = 0;  } 
  
   output25=map(lpf,0,N_PIXELS,0,250);
   output26=map(hpf,0,N_PIXELS,0,250);
   //if (lpf+hpf<2){ ctrl_brigthness=0;}
   if (lpf+hpf>=2&&lpf+hpf<50){ ctrl_brigthness=125;}
    if (lpf+hpf>50&&lpf+hpf<90){ ctrl_brigthness=125;}
     if (lpf+hpf>80){ ctrl_brigthness=150;}
                /////////-----------rythms&delay ctrol ------------/////////


 
 
 if (lpf>1&&lpf<N_PIXELS-2){tempo1=millis()-tref1;} if (hpf>1&&hpf<N_PIXELS-2){tempo2=millis()-tref2;}
 if (lpf<1) {tref1=millis();tempo1=0;}  if (hpf<1) {tref2=millis();tempo2=0;} 
 
  /*if (lpf>=0&&lpf<20) {hue=0;}  if (hpf>=0&&hpf<20) {hue2=0;}
  if (lpf>20&&lpf<35){hue=0;} if (hpf>20&&hpf<35){hue2=0;}
  if (lpf>35&&lpf<50){hue=32;}if (hpf>35&&hpf<50){hue2=32;}
  if (lpf>50&&lpf<70){hue=64;}if (hpf>50&&hpf<70){hue2=64;}
if (lpf>70&&lpf<85){hue=96;}if (hpf>70&&hpf<85){hue2=96;}
if (lpf>85&&lpf<100){hue=128;}if (hpf>85&&hpf<100){hue2=128;}
if (lpf>100&&lpf<N_PIXELS-1){hue=160;}if (hpf>100&&hpf<N_PIXELS-1){hue2=160;}
*/

hue=0;
hue2=160;
int hue3=128;
 int blur_value1=map(output25,0,250,1,100);
  int blur_value2=map(output25,0,250,1,100);
   ledslpf[lpf] = CHSV(hue2, 200, 130);
   ledshpf[hpf] = CHSV(hue3, 200, 130);
  for (int k1 = N_PIXELS; k1>lpf; k1--){ledslpf[k1] = CRGB::Black; }
 for (int k2 = N_PIXELS; k2>hpf; k2--){ledshpf[k2] = CRGB::Black; }
   blur1d(ledslpf, N_PIXELS, blur_value1);
     blur1d(ledshpf, N_PIXELS, blur_value2);   
     if (lpf>0&&lpf<50){  for (int j1 = 0; j1<lpf; j1++){ledslpf[j1] = CHSV(hue, 255, 75);} }
   if (lpf>130){  for (int j1 = 0; j1<lpf; j1++){ledslpf[j1] = CHSV(hue, 255, 75);} }
  if (hpf>0&&hpf<50){for (int j2 = 0; j2<hpf; j2++){ledshpf[j2] = CHSV(hue2, 255, 100);} } 
    if (hpf>130){  for (int j2 = 0; j2<hpf; j2++){ledshpf[j2] = CHSV(hue3, 255, 75);} } 
 
                       
               
 //Serial.println(D_lpf) ; 
                                              ////////////////////////////

/* speed and magic effects possibilities
 * The control of the rhythm is based on a pattern that then has to be completed with the software of the 
 * other control esp32 and it is nothing more than:
                                 "if the bass raises the speed too"
*/
                     int sf=map(lpf,0,N_PIXELS,1,4); 
                     z=map(hpf,0,N_PIXELS,1,4);
  if (lpf>=0&&lpf<(N_PIXELS/4)) {w=map(analog_L,0,N_PIXELS,z,sf);} 
  if (lpf>=(N_PIXELS/4)&&lpf<(N_PIXELS/2)) {w=map(analog_L,0,N_PIXELS,z,sf);} 
  if (lpf>(N_PIXELS/2)&&lpf<N_PIXELS-35){w=map(analog_L,0,N_PIXELS,z,sf);}
  if (lpf>N_PIXELS-35&&lpf<=N_PIXELS){w=map(analog_L,0,N_PIXELS,z,sf);}
 
 //////////////////////////////  



if (w==1){
x1= map(lpf,0,N_PIXELS,18,20);
x2= map(hpf,0,N_PIXELS,15,10);
x3=map(hpf,0,N_PIXELS,25,5);
if (tempo1>x1*x3){tempo1=x1*x3;}
if (tempo2>x2*30){tempo2=x2*30;}
if (lpf<20){D_lpf=15;}else{D_lpf=(tempo1+tempo2)/(sqrt(x1+x2)+lpf+sqrt(hpf));}
delay(D_lpf);
 }
 
if (w==2){
x1= map(lpf,0,N_PIXELS,8,10);
x2= map(hpf,0,N_PIXELS,15,5);
x3=map(hpf,0,N_PIXELS,35,10);
if (tempo1>x1*x3){tempo1=x1*x3;}
if (tempo2>x2*30){tempo2=x2*30;}
if (lpf<20){D_lpf=15;}else{D_lpf=(tempo1+tempo2)/(sqrt(x1+x2)+lpf+sqrt(hpf));}
delay(D_lpf);
 }

if (w==4){
x1= map(lpf,0,N_PIXELS,40,30);
x2= map(lpf,0,N_PIXELS,35,20);
x3=map(hpf,0,N_PIXELS,25,15);
if (tempo1>x1*x3){tempo1=x1*x3;}
if (tempo2>x2*30){tempo2=x2*30;}
if (lpf<20){D_lpf=15;}else{D_lpf=(tempo1+tempo2)/(sqrt(x1+x2)+lpf+sqrt(hpf));}
delay(D_lpf);
}
 if (w==3){
 x1= map(lpf,0,N_PIXELS,35,22);
 x2= map(lpf,0,N_PIXELS,15,7);
 x3=map(lpf,0,N_PIXELS,11,3);
 if (tempo1>x1*x3){tempo1=x1*x3;}
 if (tempo2>x2*30){tempo2=x2*30;}
if (lpf<20){D_lpf=15;}else{D_lpf=(tempo1+tempo2)/(sqrt(x1+x2)+lpf+sqrt(hpf));}
delay(D_lpf);
 }
 
 

}


  
        
   
   

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Translate »