duminică, 26 ianuarie 2014

Miniorga de lumini cu Arduino

   Am gasit un material foarte bun despre construirea unei miniorgi de lumini cu 3 canale pe LED-uri pe blog-ul Arduino Guay folosind un Arduino si cateva componente.
   Articolul de baza a fost Arduino light organ (II)
   Schema de conectare originala este:
    Deoarece m-am hotarat sa fac si eu asta, nu am avut la indemana un 741 bun, am incercat si cu un operational dintr-un 324, dupa care am apelat la modulul de (pre)amplificator cu LM386, care poate fi folosit intr-o aplicatia atipica cu amplificare foarte mare 74dB (aproape 5000 de ori), pe care am mai folosit-o ca amplificator de microfon cu electret si ascultare sunete dintr-o camera (pentru fie'mea cand era bebelus), vedeti articolul Intercom / interfon - sistem de comunicare la distanta (schema originala este de la http://www.intio.or.jp/jf10zl/LM386.htm)
   Deoarece am avut doar LED-uri multicolore, le-am folosit pe ecastea + un LED alb pentru un viitor canal de pauza/negativ, cum ammai facut si cu electronica clasica, vedeti articolele Miniorga de lumini cu... LED-uri (partea intai) si Miniorga de lumini cu... LED-uri (partea a doua):
 
   Montajul meu de test arata asa:
 
   Pentru primele teste am folosit sketch-ul original (care-l gasiti in articolul mentionat la inceput, Arduino light organ (II)) si am gacut si un filmulet, numit Arduino light organ (1):
   Ulterior, am modificat sketch-ul sa am si canalul negativ, LED-ul alb inseriat cu o rezistenta de 220 ohmi fiind conectat la pinul D6:
   Sketch-ul meu:
/*
*  Organo de luces psicodelicas 
*  Organo de luces de tres canales , utilizando la FFT   
*  Autor: Jose Daniel Herrera
*  Fecha: 05/09/2012
*  http://arduino-guay.blogspot.com.es
*/
// niq_ro (Nicu FLORICA) adapted this sketch to add a negative channel
// http://nicuflorica.blogspot.ro/


#include <fix_fft.h>
#define MUESTRAS 128           // Numero de muestras para el cálculo de la FFT
#define LOGM 7                 // Logaritmo en base 2 del número de muestras

#define BAJOS_MEDIOS 7         // Nº de banda para el corte entre Bajos y Medios
#define MEDIOS_AGUDOS 35       // Nº de banda para el corte entre Medios y Agudos

#define BPIN  9                // Pin de salida Bajos
#define MPIN  10               // Pin de salida Medios 
#define APIN  11               // Pin de salida Agudos
#define NPIN  6                // Pin for negative channel

#define MAX_PASADAS 10         // Nº de pasadas para el cálculo de los límites

char data[MUESTRAS];           // Array con los valores muestreados (parte real)
char im[MUESTRAS];             // Array con los valores muestreados (parte imaginaria)

unsigned char salida[MUESTRAS/2];  // Valores obtenidos de la FFT (espectro de 64 bandas)
unsigned char bajos,medios,agudos; // Valores calculados para cada canal 

byte  pasada,                            // nº de pasada para el cáculo de los límites
      acumBajos,acumMedios,acumAgudos,   // acumuladores de veces que se supera el límite
      limBajos,limMedios,limAgudos;      // límites calculados para cada canal


/*
* Funcion que aplica una ventana de Hann a los datos muestreados para reducir el 
* efecto de las discontinuidades en los extremos
*/
void aplicaVentana (char *vData) {
    double muestrasMenosUno = (double(MUESTRAS) - 1.0);
 // Como la ventana es simétrica , se calcula para la mitad y se aplica el factor por los dos extremos
    for (uint8_t i = 0; i < MUESTRAS/2 ; i++) {
        double indiceMenosUno = double(i);
        double ratio = (indiceMenosUno / muestrasMenosUno);
        double factorPeso = 0.5 * (1.0 - cos(6.28 * ratio));
 vData[i] *= factorPeso;
 vData[MUESTRAS - (i + 1)] *= factorPeso;
    }
}

void setup() { 
  //  Serial.begin(9600);  
    // Configuramos el prescaler a 32 -> 16Mhz/32 = 500 KHz
    // como cada conversion son 13 ciclos 500/13 ~ 38.4KHz
    // Es decir podemos medir en teoria hasta unos 19KHz, 
    // que para este proyecto sobra.
    bitWrite(ADCSRA,ADPS2,1);
    bitWrite(ADCSRA,ADPS1,0);
    bitWrite(ADCSRA,ADPS0,1); 

    // Como la señal es muy baja,utilizamos la referencia interna 
    // de 1.1 V en vez de la de defecto de 5 V.
    analogReference(INTERNAL);   
    
    // Salidas para los canales de Bajos,Medios y Agudos
    pinMode(BPIN,OUTPUT);
    pinMode(MPIN,OUTPUT);
    pinMode(APIN,OUTPUT);
    pinMode(NPIN,OUTPUT);
    
    // Variables para el cálculo de los límites
    pasada = 0;
    acumBajos = acumMedios = acumAgudos = 0;
    limBajos = limMedios = limAgudos = 50;
}

void loop() {

    // Realizamos el muestreo 
    for( int i=0; i < MUESTRAS; i++) {
       data[i] = analogRead(0)/4 -128;  //Convertimos de 0..1024 a -128..127                                 
       im[i] = 0;                       // parte imaginaria = 0                          
    }
    
    // Aplicamos la ventana de Hann
    aplicaVentana (data);
 
    // Calculamos la FFT 
    fix_fft(data,im,LOGM,0);
    
    // Sólo nos interesan los valores absolutos, no las fases, asi que 
    // calculamos el módulos de los vectores re*re + im*im.
    // Dado que los valores son pequeños utilizamos el cuadrado 
    for (int i=0; i < MUESTRAS/2; i++){
       salida[i] = data[i] * data[i] + im[i] * im[i];
    }
   
    // Ahora repartimos las bandas entre las 3 salidas
    // En vez de sacar la media, utilizo sólo el valor máximo de
    // una banda 
    bajos = 0;
    for (int i=2; i < BAJOS_MEDIOS; i++){
      bajos += salida[i];
    }
    bajos = bajos/2;
    
    medios = 0;
    for (int i=BAJOS_MEDIOS ; i < MEDIOS_AGUDOS; i++){
      medios += salida[i];
    }
    medios = medios/2;
    
    agudos = 0;
    for (int i=MEDIOS_AGUDOS; i < MUESTRAS/2; i++){
      agudos += salida[i];
    }
    agudos = agudos/2;
    
   // Calculamos si el canal correspondiente 
   // supera le límite para encenderlo 
   int siBajos  =  bajos  > limBajos;
   int siMedios =  medios > limMedios;
   int siAgudos =  agudos > limAgudos;
   int negativ = 255 - (bajos + medios + agudos)*6;
   // int negativ = 255-bajos*10;
   if (negativ <0) negativ=0;
   
/*   
   Serial.print(bajos);
   Serial.print("....");
   Serial.print(negativ);
   Serial.println("----");
*/     
   digitalWrite(BPIN,siBajos ? HIGH : LOW);
   digitalWrite(MPIN,siMedios? HIGH : LOW);
   digitalWrite(APIN,siAgudos? HIGH : LOW);
   analogWrite(NPIN,negativ);
  
      
   // Utilizamos las veces que se supera para
   // recalcular el límite y evitar que con los
   // cambios de volumen los canales se saturen
   // o no funcionen.
   acumBajos  += siBajos;
   acumMedios += siMedios;
   acumAgudos += siAgudos;
   
   if ( ++pasada > MAX_PASADAS ) {
      pasada = 0;
      limBajos  = 20 + acumBajos*5;
      limMedios = 20 + acumMedios*5;
      limAgudos = 20 + acumAgudos*5;
      acumBajos  = 0;
      acumMedios = 0;
      acumAgudos = 0;
   }
 }
   Am facut si un filmulet pentru varianta cu canal negativ, numit Arduino light organ (2)
   Am modificat sketch-ul sa am 4 canale, in loc de pauza: unul pentru sunete grave adica foarte joase (subbas/subwoofer) si a rezultat filmuletul Arduino light organ (3)
   Sketch-ul folosit este:
/*
*  Organo de luces psicodelicas 
*  Organo de luces de tres canales , utilizando la FFT   
*  Autor: Jose Daniel Herrera
*  Fecha: 05/09/2012
*  http://arduino-guay.blogspot.com.es
*/
// niq_ro (Nicu FLORICA) adapted this sketch to add a subwoofer/subbass channel
// http://nicuflorica.blogspot.ro/


#include <fix_fft.h>

#define MUESTRAS 128           // Numero de muestras para el cálculo de la FFT
#define LOGM 7                 // Logaritmo en base 2 del número de muestras

#define SUBBAS 2                // for subwoffer
#define BAJOS_MEDIOS 7         // Nº de banda para el corte entre Bajos y Medios
#define MEDIOS_AGUDOS 35       // Nº de banda para el corte entre Medios y Agudos

#define BPIN  9                // Pin de salida Bajos
#define MPIN  10               // Pin de salida Medios 
#define APIN  11               // Pin de salida Agudos
#define NPIN  6                // Pin for subwoofer channel

#define MAX_PASADAS 10         // Nº de pasadas para el cálculo de los límites

char data[MUESTRAS];           // Array con los valores muestreados (parte real)
char im[MUESTRAS];             // Array con los valores muestreados (parte imaginaria)

unsigned char salida[MUESTRAS/2];  // Valores obtenidos de la FFT (espectro de 64 bandas)
unsigned char bajos,medios,agudos,subbas; // Valores calculados para cada canal 

byte  pasada,                            // nº de pasada para el cáculo de los límites
      acumBajos,acumMedios,acumAgudos, acumSubbas,  // acumuladores de veces que se supera el límite
      limBajos,limMedios,limAgudos, limSubbas;      // límites calculados para cada canal
/*
* Funcion que aplica una ventana de Hann a los datos muestreados para reducir el 
* efecto de las discontinuidades en los extremos
*/
void aplicaVentana (char *vData) {
    double muestrasMenosUno = (double(MUESTRAS) - 1.0);
 // Como la ventana es simétrica , se calcula para la mitad y se aplica el factor por los dos extremos
    for (uint8_t i = 0; i < MUESTRAS/2 ; i++) {
        double indiceMenosUno = double(i);
        double ratio = (indiceMenosUno / muestrasMenosUno);
        double factorPeso = 0.5 * (1.0 - cos(6.28 * ratio));
 vData[i] *= factorPeso;
 vData[MUESTRAS - (i + 1)] *= factorPeso;
    }
}

void setup() { 
  //  Serial.begin(9600);  
    // Configuramos el prescaler a 32 -> 16Mhz/32 = 500 KHz
    // como cada conversion son 13 ciclos 500/13 ~ 38.4KHz
    // Es decir podemos medir en teoria hasta unos 19KHz, 
    // que para este proyecto sobra.
    bitWrite(ADCSRA,ADPS2,1);
    bitWrite(ADCSRA,ADPS1,0);
    bitWrite(ADCSRA,ADPS0,1); 

    // Como la señal es muy baja,utilizamos la referencia interna 
    // de 1.1 V en vez de la de defecto de 5 V.
    analogReference(INTERNAL);   
    
    // Salidas para los canales de Bajos,Medios y Agudos
    pinMode(BPIN,OUTPUT);
    pinMode(MPIN,OUTPUT);
    pinMode(APIN,OUTPUT);
    pinMode(NPIN,OUTPUT);
    
    // Variables para el cálculo de los límites
    pasada = 0;
    acumBajos = acumMedios = acumAgudos = 0;
    limBajos = limMedios = limAgudos = 50;
}

void loop() {

    // Realizamos el muestreo 
    for( int i=0; i < MUESTRAS; i++) {
       data[i] = analogRead(0)/4 -128;  //Convertimos de 0..1024 a -128..127                                 
       im[i] = 0;                       // parte imaginaria = 0                          
    }
    
    // Aplicamos la ventana de Hann
    aplicaVentana (data);
 
    // Calculamos la FFT 
    fix_fft(data,im,LOGM,0);
    
    // Sólo nos interesan los valores absolutos, no las fases, asi que 
    // calculamos el módulos de los vectores re*re + im*im.
    // Dado que los valores son pequeños utilizamos el cuadrado 
    for (int i=0; i < MUESTRAS/2; i++){
       salida[i] = data[i] * data[i] + im[i] * im[i];
    }
   
    // Ahora repartimos las bandas entre las 3 salidas
    // En vez de sacar la media, utilizo sólo el valor máximo de
    // una banda 
    subbas = 0;
    for (int i=0; i < SUBBAS; i++){
      subbas += salida[i];
    }
    subbas = subbas/2;
    
    
    bajos = 0;
    for (int i=SUBBAS; i < BAJOS_MEDIOS; i++){
      bajos += salida[i];
    }
    bajos = bajos/2;
    
    medios = 0;
    for (int i=BAJOS_MEDIOS ; i < MEDIOS_AGUDOS; i++){
      medios += salida[i];
    }
    medios = medios/2;
    
    agudos = 0;
    for (int i=MEDIOS_AGUDOS; i < MUESTRAS/2; i++){
      agudos += salida[i];
    }
    agudos = agudos/2;
    
   // Calculamos si el canal correspondiente 
   // supera le límite para encenderlo 
   int siBajos  = bajos  > limBajos;
   int siMedios = medios > limMedios;
   int siAgudos = agudos > limAgudos;
   int siSubbas = subbas > limSubbas;
  
   digitalWrite(BPIN,siBajos ? HIGH : LOW);
   digitalWrite(MPIN,siMedios? HIGH : LOW);
   digitalWrite(APIN,siAgudos? HIGH : LOW);
   digitalWrite(NPIN,siSubbas ? HIGH : LOW);

  
      
   // Utilizamos las veces que se supera para
   // recalcular el límite y evitar que con los
   // cambios de volumen los canales se saturen
   // o no funcionen.
   acumBajos  += siBajos;
   acumMedios += siMedios;
   acumAgudos += siAgudos;
   acumSubbas += siSubbas;
   
   if ( ++pasada > MAX_PASADAS ) {
      pasada = 0;
      limBajos  = 20 + acumBajos*5;
      limMedios = 20 + acumMedios*5;
      limAgudos = 20 + acumAgudos*5;
      limSubbas = 20 + acumSubbas*5;
      acumBajos  = 0;
      acumMedios = 0;
      acumAgudos = 0;
      acumSubbas = 0;
   }
 }
27.01.2014    Schema de conectare folosita de mine este:
   Am mai pus un filmulet numit Arduino light organ (4)

   Am inlocuit partea de amplificare a sunetului de la microfonul electret cu un clasic LM741, cum era si in articolul original, doar ca am schimbat unele valori ale componentelor.
   Efectul "psihedelic" este similar, doar ca miniorga de lumini e "cam tare de ureche" fata de solutia anterioara cu LM386, dupa cum se vede in filmuletul Arduino light organ (5)
03.02.2014
   Am inlocuit placa Arduino Uno cu una mai mica, Arduino Pro Mini, care functioneaza la 5V si frecventa de 16MHz:
   Am incarcat sketch-ul folosindu-ma de adaptorul USBT-FTDI cu CP2102, pe care l-am prezentat in articolul Arduino de casa cu interfata USB CP2102, alegand placa corespunzator:
   Montajul arata astfel:
 iar in timp ce functioneaza:
   Filmuletul Arduino light organ (6) prezinta cum functioneaza acest Arduino Pro Mini ca miniorga de lumini:
la fel si:
10.02.2013

9 comentarii:

  1. As avea o intrebare in legatura cu sketch-ul, cand il verific imi zice ca nu a fost declarat (bitWrite(ADCSRA,ADPS2,1).
    trebuie sa declar sau... :)

    RăspundețiȘtergere
  2. ce librarie ai folosit side undepot so o am.
    Ceversiune de arduino aimfolosit
    Multumesc

    RăspundețiȘtergere
    Răspunsuri
    1. daca te uiti in sketch se vede ca folosesc libraria .. l ainceputul articolului este mentionat articolul de unde am luat informatiile de baza (pe scurt e http://arduino-cool.blogspot.ro/2012/09/arduino-light-organ-ii.html ) si acolo e link la librarie 9mura-n gura: https://drive.google.com/file/d/0B9XPtn5QO-OzWF9ZQnVIcDV6THc/view?pli=1 )
      am folosit Arduino IDE 1.0.4, dupa cum se vede in poze...
      sper ca macar click poti da pe link-uri... :(((

      Ștergere
  3. Buna ziua, la circuitul cu 741, daca as vrea ca in loc de 1 led sa adaug 3 de exemplu ce ar trebui sa fac pentru am functiona corespunzator circuitul??

    RăspundețiȘtergere
    Răspunsuri
    1. iesirea scoate maxim 30-40mA daca depasiti "bau".. se pun tranzistoare, cum e la miniorga clasica..

      Ștergere
  4. Cum se poate ca in locul ledurilor sa conectez optocuplare pentru comanda benzilor led normale de 12 V?

    RăspundețiȘtergere
    Răspunsuri
    1. simplu ...pui optocuploare in locul ledurilor. la iesire ataci un tranzistor de putere ce iti poate tine banda led adica sa aiba o tensiune de lucru peste 12 volti si puterea sa fie de vreo doua ori mai mare decat a benzii led si eu i-as pune si un radiator....asa as face eu....cat priveste intrarea optocuplorului este in esenta un led tu trebuie sa iti alegi tipul de optoculpor deci implicit si tensiunea de alimentare a intrarii si sa calculezi rezistentele de limitare... cam asa s-ar face

      Ștergere
    2. multumesc. am rezolvat si merge perfect. Vreau sa folosesc in loc de arduino pro mini arduino nano. Are pin Ref si cu un divizor rezistiv pot regla sensibilitatea pe intrare astfel incat sa nu mai fie nevoie de preamplificator

      Ștergere