Mini vu meter con arduino nano e ws2812

ciao a tutti
ho un programma che tra le altre cose volevo arricchire con un mini vu meter con soli 4 led
ho implementato il codice e funziona, ma ho notato che tutti i led (quando sono accesi) flickerano!
non capisco il problema e chiedo aiuto per sapere se c'è modo di sitemarlo
inoltre mi piacerebbe avere un effetto dei colori dinamico come i vu meter che si vedono in giro (ma che non sono riuscito a replicare sul mio programma! :frowning:
ringrazio tutti in anticipo
ciao

void Vmeter(){
  int micValue = analogRead(MIC_PIN);

  if (micValue > 0) {
    int ledLevel = map(micValue, 395, 440, 0, NUM_LEDS);
    for (int i = 1; i < NUM_LEDS; i++) {
      if (i < ledLevel) {
        strip.setPixelColor(i, strip.Color(0, 255, 0));  // Accendi il LED
        //delay(30); 
      } else {
        strip.setPixelColor(i, strip.Color(0, 0, 0)); // Spegni il LED
         
      }
    }
    strip.show();
  }
}

Il segnale che viene dal microfono deve essere raddrizzato e livellato, altrimenti analogRead leggerà qualunque valore tra zero e il valore di picco per la semionda positiva e zero per tutta la semionda negativa! Guarda lo schema della "Spilla elettronica da discoteca":

per prima cosa di ringrazio per avermi risposto.

in merito al seggerimento che mi hai dato, faccio presente che il sistema funziona bene dato che l'effetto vu meter c'è e rispetta la musica applicata.
credo che il problema del flicker (che forse ho nominato in modo errato) è di fatto un lampeggio velocissimo (costante) dei led che credo sia figlio del ciclo for.

in merito al discorso dei valori negativi (che ho letto hai suggerito anche ad altri su altri messaggi su questo forum e che non conoscevo - motivo per cui ti ringrazio) io sto usando l'uscita DAC_L del DFplayer mini che collego tramite resistenza da 560ohm (più per una mia paranoia) al pin A0 del arduino nano.
da quello che ho letto nelle specifiche e anche in vari commenti in rete, questo dispositivo non tira fuori un segnale che va al di sopra e sotto lo 0, ma è tutto positivo. in tal senso ho visto altre persone (in video su youtube) che hanno usato lo stesso sistema senza dover interporre nulla.
comunque come detto sopra, il sistema funziona.

sto cercando di adattare altri codici di progetti di vu meter trovati in rete, ma sono tutti con decine e decine di led e quindi quando li adatto a 4 led, l'effetto non si nota neanche a meno di non capire bene il codice e modificarlo al bisogno.

grazie ancora

Mah...

  1. Non so come possano funzionare i VUmeter con un segnale che fa continuamente su e giù: secondo l'istante in cui l'ADC fa la conversione, il livello istantaneo del segnale può assumere qualsiasi valore fra il minimo e il massimo.

  2. Di tutta l'escursione del segnale in ingresso, il programma prende solo i valori fra 395 e 440 e li mappa da 0 al numero di LED.

  3. I VUmeter, data l'ampia dinamica dei segnali audio per la curva logaritmica della sensibilità dell'orecchio, devono avere una scala logaritmica, ovvero lineare (o modellata secondo necessità) in dB, per esempio -40, -30, -20, -10, -8, -6, -4, -3, -2, -1, 0dB, +1, +2, +3 dove, se 0dB è 1V, -40dB è 10mV e +3dB è 1,4V...

  4. Se il problema sta veramente nella riscrittura continua su tutti i LED, devi tenere in una variabile costantemente aggiornata il numero di LED accesi e poi, nel for, pilotare solo i LED che assumono uno stato diverso dal precedente.

Esistono integrati appositi, come l'LM3915 e l'LM3916, ma anche altri più piccoli per pilotare solo 5 o 6 LED. Per esempio:

Nota: quegli integrati possono fare sia la barra che il punto, ma se non ci metti il raddrizzatore e il condensatore di livellamento la barra viene sfumata e il punto non può funzionare!

Se stai usando la libreria Adafruit, è un difetto che ho riscontrato spesso anche io.
Forse non hai selezionato il tipo di led corretto rispetto a quelli in tuo possesso.

Potresti provare anche con la libreria FastLed eventualmente

@Datman

ciao e grazie ancora per aver risposto

ti rispondo punto per punto:

  1. come puoi vedere dal video che ho realizzato e che ti allego, il tutto funziona o meglio si muove a ritmo di musica che è quello che mi serve. forse aver detto vu meter è stato sbagliato fin dal titolo -> link del video WeTransfer - Send Large Files & Share Photos Online - Up to 2GB Free (se viola le regole del forum mi scuso e chiedo al Mod di cancellarlo)
    coumnque ovviamente è come dici tu, il segnale fluttua in continuazione ma credo che vinca il volume del suono che alla fine consente di avere degli incrementi nei led accesi.
  2. il range 395 440 l'ho impostato io facendo le prove con le letture da monitor seriale per capire quale fosse il picco massimo e minimo del segnale che applicavo e per il quale ho bisogno di avere tele effetto "vu meter"
  3. sicuramente i VERI vu meter sono degli strumenti di misura e, come dici tu, hanno dei Decibel ben noti che servono a ben altro che un effetto prettamente visivo.
  4. per questo punto non ho ben chiaro il suggerimento che i stai dando, ma io penso che vengano aaccesi e rispenti ad ogni ciclo - ma non sono sicuro, quindi forse il suggerimento è la strada giusta (se per favore me lo puoi spiegare meglio)

per gli appositi circuiti credo che siano eccessivi per avere banalmente dei led che si accendano a ritmo di musica ma ti ringrazio infinitamente

grazie ancora

@cotestatnt

si, la libreria che uso è proprio quella, ma non credo sia la causa del problema dato che uso, nello stesso programma, altri giochi di luce senza problemi :wink:

grazie mille per la risposta

ciao a tutti

mi sono accorto che il collegamento come era fatto era una sorta di antenna e avvicinando la mano ai fili del pin A0 alterava i valori
allora ho fatto come suggerito fin dall'inizio da @datman e ho messo un partitore resistivo con 2 resistenze da 22K tra VCC e GND
ovviamente sul lato che va al pin DFplayer mini DAC_L ci ho messo un condensatore per bloccare la continua.

la cosa ha risolto il problema "antenna" dato che pure toccando i fili resta stabile ma il flickerio dei led è rimasto.

ora sto provando a cambiare il ciclo for
grazie ancora a tutti

Questo è interessante:

https://danyk.cz/avr_iv2.html

Il codice, purtroppo, è scritto in Assembly:
https://danyk.cz/avr_iv2.asm
Ho provato a convertirlo con

ma sono stato costretto a dividerlo in blocchi e ne è uscito questo:

#include <avr/io.h>

#define REG 16		// docasny registr
#define REG2 17		// docasny registr 2
#define VYS_L 18		// vysledek AD prevodu dolni
#define VYS_H 19		// vysledek AD prevodu horni
#define DELREG 20		// registr deleni frekvence
#define DELREG2 21	// registr deleni frekvence 2
#define KANAL 22		// prepinani kanalu
#define MULTREG 23	// registr si pamatuje stav multiplexu 
#define LK 24			// levy kanal
#define PK 25			// pravy kanal
#define LKD 26		// levy kanal indikace maxima
#define PKD 27		// pravy kanal indikace maxima
#define LKZ 28		// levy kanal zpozdeni maxima
#define PKZ 29		// pravy kanal zpozdeni maxima

#define SMER DDRD
#define PORT PORTD
#define SMER2 DDRC
#define PORT2 PORTC
#define VSTUPY PINC
#define SMER3 DDRB
#define PORT3 PORTB

void init() {
    // nastaví port D
    DDRD = 0b11111111;
    PORTD = 0b00000000;
    
    // nastaví port C
    DDRC = 0b000000;
    PORTC = 0b111100;
    
    // nastaví port B
    DDRB = 0b00111111;
    PORTB = 0b11111100;
    
    // nastaveni casovace a jeho preruseni
    TCCR2 = 0b00001001;
}

int main() {
    init();
    
    while (1) {
        // TODO: implement main logic here
    }
    
    return 0;
}

// ---------------------------- Secondo blocco: ----------------------------

#include <avr/io.h>

void setup_timer() {
    TCCR2 = 0b00001001; // CTC mode, no output compare
    OCR2 = 249; // set compare value
    TIMSK = 0b10000000; // enable CTC interrupt
}

void setup_adc() {
    MCUCR = 0b10010000; // enable sleep mode
    ACSR = 0b10000000; // disable analog comparator
    ADCSRA = 0b10000011; // ADC prescaler f/8, enable ADC
}

int main() {
    setup_timer();
    setup_adc();
    sei(); // enable global interrupt

    while (1) {
        sleep();
    }
}

void mult() {
    PORT = 0b00000000;
    PORT3 = 0b11111100;
    PORT2 = 0b111100;

    // fast
    if (LK < MULTREG) {
        PORT3 &= ~(1 << 2);
    }
    if (LK - 246 < MULTREG) {
        PORT3 &= ~(1 << 3);
    }
    if (PK < MULTREG) {
        PORT3 &= ~(1 << 4);
    }
    if (PK - 246 < MULTREG) {
        PORT3 &= ~(1 << 5);
    }

    // slow
    if (VSTUPY & (1 << 2)) {
        if (LKD != MULTREG) {
            PORT3 &= ~(1 << 2);
        }
        if (LKD - 246 != MULTREG) {
            PORT3 &= ~(1 << 3);
        }
        if (PKD != MULTREG) {
            PORT3 &= ~(1 << 4);
        }
        if (PKD - 246 != MULTREG) {
            PORT3 &= ~(1 << 5);
        }
    }
}

// ---------------------------- Terzo blocco: ----------------------------

#define MAX_VYP \
    do { \
        REG--; \
        ZL = LOW(SKOK); \
        ZH = HIGH(SKOK); \
        ZL += REG; \
        REG = 0; \
        ZH += REG; \
        goto SKOK; \
    } while (0)

#define SKOK \
    do { \
        goto MULT1; \
        goto MULT2; \
        goto MULT3; \
        goto MULT4; \
        goto MULT5; \
        goto MULT6; \
        goto MULT7; \
        goto MULT8; \
        goto MULT9; \
        goto MULT10; \
    } while (0)

#define MULT1 \
    do { \
        PORT |= (1 << 0); \
        return; \
    } while (0)

#define MULT2 \
    do { \
        PORT |= (1 << 1); \
        return; \
    } while (0)

#define MULT3 \
    do { \
        PORT |= (1 << 2); \
        return; \
    } while (0)

#define MULT4 \
    do { \
        PORT |= (1 << 3); \
        return; \
    } while (0)

#define MULT5 \
    do { \
        PORT |= (1 << 4); \
        return; \
    } while (0)

#define MULT6 \
    do { \
        PORT |= (1 << 5); \
        return; \
    } while (0)

#define MULT7 \
    do { \
        PORT |= (1 << 6); \
        return; \
    } while (0)

#define MULT8 \
    do { \
        PORT |= (1 << 7); \
        return; \
    } while (0)

#define MULT9 \
    do { \
        PORT3 |= (1 << 0); \
        return; \
    } while (0)

#define MULT10 \
    do { \
        PORT3 |= (1 << 1); \
        return; \
    } while (0)

#define PADEJ \
    do { \
        if (LK == 0) { \
            goto LEVY_0; \
        } \
        LK--; \
        LEVY_0: \
        if (PK == 0) { \
            goto PRAVY_0; \
        } \
        PK--; \
        PRAVY_0: \
        if (LKZ == 0) { \
            goto LEVYZ_0; \
        } \
        LKZ--; \
        goto LEVYZ_NE0; \
        LEVYZ_0: \
        LKD = LK; \
        LEVYZ_NE0: \
        if (PKZ == 0) { \
            goto PRAVYZ_0; \
        } \
        PKZ--; \
        goto PRAVYZ_NE0; \
        PRAVYZ_0: \
        PKD = PK; \
        PRAVYZ_NE0: \
        return; \
    } while (0)

// ---------------------------- Quarto blocco: ----------------------------

#include <avr/io.h>

void CASOVANI() {
    uint8_t VYS_L, VYS_H, REG, REG2;

    VYS_L = ADCL;
    VYS_H = ADCH;
    REG = KANAL;
    REG &= 0b00000001;
    REG |= 0b11000000;
    ADMUX = REG;
    ADCSRA |= (1 << ADSC);
    REG = 0;
    REG2 = HIGH(11);
    if (VYS_L < LOW(11) || (VYS_L == LOW(11) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(14);
    if (VYS_L < LOW(14) || (VYS_L == LOW(14) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(18);
    if (VYS_L < LOW(18) || (VYS_L == LOW(18) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(22);
    if (VYS_L < LOW(22) || (VYS_L == LOW(22) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(28);
    if (VYS_L < LOW(28) || (VYS_L == LOW(28) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(36);
    if (VYS_L < LOW(36) || (VYS_L == LOW(36) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(45);
    if (VYS_L < LOW(45) || (VYS_L == LOW(45) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(56);
    if (VYS_L < LOW(56) || (VYS_L == LOW(56) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(71);
    if (VYS_L < LOW(71) || (VYS_L == LOW(71) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(90);
    if (VYS_L < LOW(90) || (VYS_L == LOW(90) && VYS_H < REG2)) {
        REG++;
    }
    RJMP POPOSKOC;
MENSI:
    RJMP MENSI2;
POPOSKOC:
    REG2 = HIGH(113);
    if (VYS_L < LOW(113) || (VYS_L == LOW(113) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(142);
    if (VYS_L < LOW(142) || (VYS_L == LOW(142) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(179);
    if (VYS_L < LOW(179) || (VYS_L == LOW(179) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(225);
    if (VYS_L < LOW(225) || (VYS_L == LOW(225) && VYS_H < REG2)) {
        REG++;
    }
    REG2 = HIGH(283);
    if (VYS_L < LOW(283) || (VYS_L == LOW(283) && VYS_H < REG2)) {
        REG++;
    }
MENSI2:
    REG++;
}

// ---------------------------- Quinto blocco: ----------------------------

#include <stdint.h>

uint8_t REG = 0;
uint8_t REG2 = 0;
uint8_t VYS_L = 0;
uint8_t VYS_H = 0;
uint8_t KANAL = 0;
uint8_t LK = 0;
uint8_t LKD = 0;
uint8_t LKZ = 0;
uint8_t PK = 0;
uint8_t PKD = 0;
uint8_t PKZ = 0;
uint8_t DELREG = 0;
uint8_t MULTREG = 0;
uint8_t DELREG2 = 0;

void MENSI() {
    INC(KANAL);
    if (KANAL & 0x01) {
        RJMP(LEVY_NE);
    }
    if (REG >= LK) {
        BRSH(L_NENI_VYSSI);
    }
    MOV(LK, REG);
    L_NENI_VYSSI:
    if (REG < LKD) {
        BRLO(LD_JE_NIZSI);
    }
    LDI(LKZ, 67);
    MOV(LKD, REG);
    LD_JE_NIZSI:
    RJMP(PRAVY_NE);
}

void PRAVY_NE() {
    if (REG >= PK) {
        BRSH(P_NENI_VYSSI);
    }
    MOV(PK, REG);
    P_NENI_VYSSI:
    if (REG < PKD) {
        BRLO(PD_JE_NIZSI);
    }
    LDI(PKZ, 67);
    MOV(PKD, REG);
    PD_JE_NIZSI:
    DEC(DELREG);
    if (DELREG != 0) {
        BRNE(HOP);
    }
    LDI(DELREG, 32);
    RCALL(MULT);
    DEC(MULTREG);
    if (MULTREG != 0) {
        BRNE(HOP);
    }
    LDI(MULTREG, 10);
    DEC(DELREG2);
    if (DELREG2 != 0) {
        BRNE(HOP);
    }
    LDI(DELREG2, 3);
    RCALL(PADEJ);
    HOP:
    RETI();
}

int main() {
    LDI(REG2, HIGH(356));
    CPI(VYS_L, LOW(356));
    CPC(VYS_H, REG2);
    BRLO(MENSI);
    INC(REG);
    LDI(REG2, HIGH(449));
    CPI(VYS_L, LOW(449));
    CPC(VYS_H, REG2);
    BRLO(MENSI);
    INC(REG);
    LDI(REG2, HIGH(565));
    CPI(VYS_L, LOW(565));
    CPC(VYS_H, REG2);
    BRLO(MENSI);
    INC(REG);
    LDI(REG2, HIGH(711));
    CPI(VYS_L, LOW(711));
    CPC(VYS_H, REG2);
    BRLO(MENSI);
    INC(REG);
    LDI(REG2, HIGH(895));
    CPI(VYS_L, LOW(895));
    CPC(VYS_H, REG2);
    BRLO(MENSI);
    INC(REG);
    MENSI();
    return 0;

il risultato del tizio è molto bello
peccato che il codice non sia alla mia portata !!!
ma ovviamente ti ringrazio moltissimo per averlo convertito

io per ora sono riuscito a togliere il flickerio togliendo il FOR come mostrato sotto.
unica cosa (strana per me) è che con il delay (che poi ho commentato) invece di lasciare i led accesi in quel modo fino alla successiva chiamata della funzione vumeter da parte della void loop, li tiene spenti (per quel tempo) e poi alla successiva chiamata li riaccende un attimo - e la cosa si ripete così.
in pratica stanno sempre spenti tranne un flash!
ma non capisco: se è stato fatto strip.show, non dovrebbero restare in quello stato fino al prossimo strip.show?
perchè si spengono?

void Vmeter(){   //nuova versione
  int micValue = analogRead(MIC_PIN);
  if (micValue > 0) {
    int ledLevel = map(micValue, 510, 545, 1, NUM_LEDS);      //cavo e partitore resistivo e condensatore
      strip.fill(002000, 1, ledLevel);
      strip.fill(000000, ledLevel+1, NUM_LEDS);
      strip.show();
      //delay(100);
     }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.