Ingegneria del suono: costruisci il tuo filtro passa basso con frequenza di taglio regolabile tramite potenziometro digitale

Introduzione

Esplora il mondo dell’audio realizzando un filtro passa basso con frequenza di taglio regolabile.

Introduciti nell’entusiasmante mondo dell’ingegneria del suono, dove il controllo totale della tua esperienza audio diventa una realtà! In questo articolo, ti guiderò attraverso la costruzione di un filtro passa basso personalizzato, un dispositivo potente e versatile che consente di plasmare la qualità del suono secondo i tuoi desideri. La caratteristica distintiva di questo progetto è la presenza di due potenziometri digitali, controllati da un ESP8266, che consentono di regolare la frequenza di taglio del filtro.

Il filtro passa basso è implementato con un operazionale in configurazione invertente, una scelta che semplifica al massimo il circuito. Ma la vera magia inizia con i potenziometri digitali, il cuore intelligente di questo sistema. Grazie alla loro connessione alla ESP8266, hai il potere di regolare la frequenza di taglio del filtro in tempo reale. Immagina di poter adattare il tuo ambiente sonoro con un semplice tocco, personalizzando ogni aspetto dell’esperienza audio in base ai tuoi gusti e alle tue esigenze.

Questo non è solo un progetto per gli audiofili, ma apre le porte a nuove possibilità nel mondo della sintesi digitale. Con un elemento di controllo così potente, puoi integrare facilmente questo filtro passa basso nei tuoi sintetizzatori digitali, consentendo una personalizzazione avanzata del tuo repertorio sonoro. Con questa guida dettagliata, non solo costruirai il tuo filtro passa basso, ma imparerai anche a sfruttare appieno il potenziale della tecnologia digitale per plasmare il suono a tuo piacimento. Lasciati coinvolgere dall’arte dell’audio engineering e inizia il tuo viaggio nella creazione di un ambiente sonoro su misura.

Cosa è un amplificatore operazionale?

L’amplificatore operazionale (detto anche opamp) è un componente elettronico analogico a stato solido capace, come dice anche il suo nome, di amplificare un segnale elettrico. È definito operazionale perché è in grado di svolgere alcune operazioni matematiche in maniera totalmente analogica su segnali elettrici. Le operazioni generalmente possibili sono la somma, la sottrazione, la derivazione e l’integrazione, il logaritmo etc etc.
È anche parte fondamentale dei filtri attivi passa-basso, passa-alto, passa-banda, notch etc etc.

È dotato di due ingressi, uno invertente (indicato col segno -) e uno non invertente (indicato col segno +), e di una uscita. Viene alimentato, in genere, con una tensione duale (di solito indicata coi segni V+ e V).

Nel caso il segnale entri attraverso l’ingresso non invertente, il segnale di uscita si trova con la stessa fase del segnale di ingresso. Se invece il segnale entra nell’ingresso invertente, il segnale di uscita sarà in opposizione di fase (sfasato cioè di 180 gradi) rispetto al segnale in ingresso.

Un amplificatore invertente è, quindi, un amplifcatore operazionale il cui ingresso non invertente è collegato a massa mentre quello invertente è collegato alla sorgente di ingresso VINPUT.

Schema elettrico di un amplificatore operazionale in configurazione invertente
Schema elettrico di un amplificatore operazionale in configurazione invertente

Questo circuito prende il segnale al morsetto INPUT e lo ripropone in OUTPUT amplificato (cioè moltiplicato per un valore chiamato guadagno che indicheremo col simbolo G).

Quindi, in formula, abbiamo che:

VOUTPUT = G * VINPUT

Abbiamo detto che questo amplificatore inverte la fase del segnale di uscita rispetto alla fase del segnale in ingresso (i due segnali sono sfasati di 180°). Questo significa che il valore di G è negativo. Se per esempio il segnale in ingresso fosse amplificato di 10 volte, avremmo che G = -10 [V/V] e quindi:

VOUTPUT = -10 * VINPUT

Se, per esempio, il segnale VINPUT = 1V avremmo che VOUTPUT = -10 V mentre con VINPUT = -1 V avremmo VOUTPUT = 10 V.

In pratica il segnale in uscita è dato dal segnale di ingresso ingrandito di 10 volte e ribaltato.

NOTA: il guadagno, essendo il rapporto tra due tensioni

G = VOUTPUT / VINPUT

è espresso da un numero puro. Possiamo comunque esprimerlo, alternativamente, con la notazione [V/V] (che è sempre un numero puro).

Come si imposta il guadagno dell’amplificatore invertente?

Il guadagno è facilmente determinato a partire dai valori delle due resistenze R1 e R2. In particolare abbiamo che il guadagno, in valore assoluto, è dato da:

|G| = R2 / R1

Quindi, se avessimo R1 = 1 kΩ e R2 = 10 KΩ, avremmo che |G| = 10.

Considerando lo sfasamento di 180° abbiamo che

G = -R2 / R1

Con i valori dell’esempio precedente abbiamo che G = -10 [V/V].

Detto in altre parole:

VOUTPUT = -(R2 / R1) * VINPUT

cioè:

VOUTPUT = -10 * VINPUT

Il guadagno può essere anche espresso in dB (decibel) secondo la relazione:

GdB = 20 * log(|G|) = 20 * log(|-R2 / R1|) = 20 * log (R2 / R1)

Per esempio, se R1 e R2 fossero da 1 kΩ e 10 kΩ rispettivamente, si avrebbe G = -10 [V/V] e GdB = 20 dB. Se R1 e R2 fossero da 2 kΩ e 15 kΩ rispettivamente, si avrebbe G = -7.5 [V/V] e GdB = 17.5 dB.

Come funziona un potenziometro classico?

Un potenziometro è un dispositivo a 3 terminali usato come resistore variabile. E’ dotato di un contatto rotante comandato dalla manopola e viene usato come partitore di tensione.

Un classico potenziometro
Un classico potenziometro

Il suo simbolo elettrico è quello di una classica resistenza ma con il contatto intermedio comandato dalla manopola:

Il simbolo elettrico del potenziometro
Il simbolo elettrico del potenziometro

Come funziona un partitore di tensione?

Un partitore di tensione è un semplice circuito in cui la tensione in uscita è una frazione della tensione in ingresso. Si possono usare due resistori fissi oppure un potenziometro.

Vediamone lo schema e la formula:

Un semplice esempio di partitore di tensione realizzato con un potenziometro
Un semplice esempio di partitore di tensione realizzato con un potenziometro

Il potenziometro è indicato con la lettera P mentre R1 e R2 sono le due sezioni della resistenza totale che variano a seconda della posizione del cursore (che è comandato dalla manopola).

Come puoi osservare la tensione Vo di uscita è una frazione di quella di ingresso Vi. La Vo varia tra due estremi:

  • Vo = 0 quando R2 = 0 (la manopola è ruotata tutta verso il terminale collegato al polo negativo della batteria)
  • Vo = Vi quando R1 = 0 (la manopola è ruotata tutta verso il terminale collegato al polo positivo della batteria)

I potenziometri digitali

Questi esempi si basano su potenziometri meccanici ma esistono in commercio anche dei tipi di potenziometri che sono completamente elettronici e regolabili tramite un opportuno segnale in ingresso. Sono i cosiddetti potenziometri digitali.

Oggi vogliamo testarne uno comandandolo tramite ESP8266, in particolare il modello MCP41010, che è un singolo potenziometro. Il suo valore massimo è di 10 kΩ mentre il valore minimo è di circa 100Ω.

Un potenziometro digitale MCP41010
Un potenziometro digitale MCP41010

Vediamo ora il pinout di questo componente:

Pinout di un MCP41010
Pinout di un MCP41010

Il nome del dispositivo dipende dal massimo valore di resistenza del singolo potenziometro digitale e dal numero di potenziometri presenti al suo interno. Per esempio:

  • MCP41010: singolo potenziometro, 10 kΩ
  • MCP41050: singolo potenziometro, 50 kΩ
  • MCP41100: singolo potenziometro, 100 kΩ
  • MCP42010: due potenziometri indipendenti, 10 kΩ
  • MCP42050: due potenziometri indipendenti, 50 kΩ
  • MCP42100: due potenziometri indipendenti, 100 kΩ

Comanderemo il nostro potenziometro digitale attraverso la sua porta SPI opportunamente pilotatata dall’ ESP8266.

Osservando il datasheet del potenziometro digitale si può vedere come, per comandare questo chip, sia necessario mandargli prima un “command byte” (per dire al chip cosa deve fare) e poi un “data byte” (per dire al chip quale valore di resistenza settare, da 0 a 255).

Per esempio, per settare una resistenza di 10 kΩ, dobbiamo mandare un data byte pari a 11111111 (corrispondente a 255), per settare una resistenza di 5 kΩ dobbiamo mandare un data byte pari a 10000000 (corrispondente a 128) e così via.

Perché il comando venga eseguito, per prima cosa il terminale CS deve essere messo a 0 (valore LOW) poi bisogna mandare il command byte seguito dal data byte (per un totale di 16 bit). Infine bisogna riportare il terminale CS al valore 1 (valore HIGH). Solo allora il comando verrà eseguito (dal datasheet:”Executing any command is accomplished by setting CS low and then clocking-in a command byte followed by a data byte into the 16-bit shift register. The command is executed when CS is raised.”)

Abbiamo già affrontato l’uso di uno di questi potenziometri digitali negli articoli Come controllare un potenziometro digitale con Arduino UNO e Come controllare un amplificatore operazionale invertente usando Arduino UNO e un potenziometro digitale di cui ti consiglio la lettura.

Partiamo con un po’ di teoria

I Sistemi Lineari Tempo Invarianti (SLTI)

Lo studio dei sistemi lineari tempo invarianti (SLTI) è fondamentale nell’ambito dell’ingegneria elettronica e del controllo, poiché fornisce un approccio sistemico all’analisi e al design di sistemi dinamici. Un sistema è considerato LTI se soddisfa due proprietà fondamentali: linearità e invarianza nel tempo. La linearità implica che il sistema rispetti il principio di sovrapposizione degli effetti, mentre l’invarianza nel tempo indica che la risposta del sistema non cambia nel tempo, purché l’ingresso rimanga invariato. Due rappresentazioni comuni di tali sistemi sono la risposta all’impulso e la funzione di trasferimento.

Risposta all’Impulso

La risposta all’impulso è una caratteristica fondamentale di un sistema dinamico LTI ed è definita come la risposta del sistema a un impulso unitario, noto anche come delta di Dirac (indicato con δ(t)). Quindi, se in ingresso ad un sistema SLTI pongo un impulso di Dirac, ottengo la risposta del sistema a quell’impulso, che indicherò con la funzione h(t):

blank

Se h(t) è la risposta all’impulso di un sistema LTI, allora la risposta y(t) del sistema a un qualsiasi ingresso x(t) può essere ottenuta calcolando la convoluzione fra l’ingresso e la risposta all’impulso:

blank

Quindi, tornando agli schemi a blocchi, avremo che:

blank

dove l’uscita y(t) è, come detto prima, la convoluzione tra l’ingresso x(t) e la risposta all’impulso h(t) (calcolata con l’integrale di sopra).

L’operazione di convoluzione si indica col simbolo * quindi possiamo scrivere che y(t) = x(t) * h(t).

In sostanza, la risposta all’impulso h(t) di un SLTI è la funzione che caratterizza il comportamento del sistema nel dominio del tempo.

Funzione di Trasferimento

La funzione di trasferimento, spesso indicata con H(s) nel dominio di Laplace, è un modo alternativo di descrivere un sistema LTI. Essa rappresenta il rapporto tra la trasformata di Laplace della risposta Y(s) e quella dell’ingresso X(s). In pratica è la trasformata di Laplace della risposta all’impulso h(t):

blank

Graficamente rappresenteremo il nostro sistema in questo modo:

blank

La funzione di trasferimento è utile perché semplifica l’analisi dei sistemi in frequenza. Giusto per fare un esempio, la convoluzione (operazione non semplicissima) si trasforma in una semplice moltiplicazione. Quindi, nel mondo di Laplace avremo che la relazione precedente si trasforma in

Y(s) = X(s) H(s).

I poli (radici del denominatore) e gli zeri (radici del numeratore) della funzione di trasferimento forniscono informazioni cruciali sulla stabilità e la risposta in frequenza del sistema.

La funzione H(s) è una funzione di variabile complessa. Infatti la variabile s, detta variabile di Laplace, è una variabile complessa (dotata cioè di parte reale e parte immaginaria) e si rappresenta così: s = α + jω (con α e ω reali). Per α = 0 si ha s = jω. In questo caso la funzione di trasferimento dipende quindi dalla sola pulsazione ω, si ha cioè H(ω) dove la pulsazione è legata alla frequenza dalla relazione ω = 2πf. Il modulo e la fase di H(ω) saranno funzioni di ω. Essa caratterizza quindi il comportamento del sistema nel dominio della frequenza.

Risposta in frequenza di un SLTI

Un aspetto chiave dello studio dei sistemi LTI è l’analisi della loro risposta in frequenza. Questa analisi coinvolge la valutazione di come il sistema risponde a segnali sinusoidali a diverse frequenze. Il modulo e la fase della funzione di trasferimento sono usualmente rappresentati graficamente tramite il diagramma di Bode, fornendo una chiara visione delle caratteristiche di attenuazione e fase del sistema in funzione della frequenza.

Breve teoria del filtro passa basso

Un filtro passa basso del primo ordine è un semplice circuito elettronico o, più in generale, un sistema dinamico caratterizzato da una frequenza di taglio che rappresenta il punto in cui il segnale inizia ad essere attenuato. La funzione di trasferimento H(s) di un filtro passa basso del primo ordine nel dominio di Laplace è espressa come:

blank

dove:

  • K rappresenta il guadagno statico del filtro, ovvero il valore del guadagno quando la frequenza tende a zero
  • s è la variabile complessa della trasformata di Laplace
  • T è la costante di tempo del sistema

La costante di tempo T è inversamente proporzionale alla frequenza di taglio fc​, e la relazione tra loro è data da:

blank

Il filtro passa basso del primo ordine agisce come un circuito RC, dove R è la resistenza e C è la capacità. La funzione di trasferimento può anche essere scritta nel dominio del tempo come risposta impulsiva del sistema h(t) (cioè calcolando l’antitrasformata di Laplace della funzione di trasferimento H(s)):

blank

dove:

  • t è il tempo
  • e è la base del logaritmo naturale

La risposta in frequenza del filtro passa basso del primo ordine indica che attenua gradualmente le frequenze superiori alla frequenza di taglio. Questo comportamento può essere visualizzato nel dominio della frequenza attraverso il diagramma di Bode, dove il modulo della funzione di trasferimento decresce con una pendenza di -20 dB/decade, mentre la fase varia da 0° a -90°.

In generale, il filtro passa basso del primo ordine trova ampio utilizzo in diverse applicazioni, inclusi circuiti audio, sistemi di controllo, elettronica di potenza e altro ancora. La sua semplicità lo rende una scelta comune per il filtraggio di segnali con esigenze di attenuazione relativamente basse alle frequenze più elevate.

Il filtro passa basso realizzato con amplificatore operazionale

Come già visto, un filtro passa basso è un circuito elettronico progettato per consentire il passaggio di segnali a bassa frequenza mentre attenua quelli a frequenza più elevata. Questo tipo di filtro è fondamentale in diverse applicazioni audio e elettroniche, in quanto consente di isolare e concentrarsi sulle componenti di bassa frequenza di un segnale. Nel contesto dell’ingegneria del suono, i filtri passa basso vengono spesso utilizzati per eliminare o ridurre le frequenze indesiderate e per concentrarsi sulla riproduzione dei toni più profondi e risonanti di un suono. Sono essenziali anche in sistemi di comunicazione, dove la separazione delle frequenze è cruciale per garantire una trasmissione chiara e priva di interferenze. Grazie alla loro versatilità, i filtri passa basso trovano impiego in una vasta gamma di dispositivi, dalla progettazione di altoparlanti e amplificatori audio a quella di circuiti di elaborazione del segnale elettronico.

Ovviamente, oltre ai filtri passa basso esistono anche i filtri passa alto, i filtri passa banda e i filtri elimina banda.

La frequenza di taglio in un filtro passa basso è il punto critico in cui il filtro inizia ad attenuare le frequenze del segnale. Si tratta della frequenza alla quale il segnale inizia a essere ridotto, e al di sotto di questa frequenza il filtro consente il passaggio quasi completo delle componenti di bassa frequenza. In un filtro passa basso ideale, al di sopra della frequenza di taglio, il segnale viene attenuato gradualmente, raggiungendo infine un punto in cui le frequenze più elevate vengono drasticamente ridotte.

La frequenza di taglio è un parametro chiave nella progettazione di filtri passa basso, poiché determina la gamma di frequenze che verranno mantenute o attenuate. Regolando la frequenza di taglio, è possibile adattare il filtro alle specifiche esigenze dell’applicazione, consentendo un controllo preciso sulla riproduzione delle frequenze nel segnale audio o elettronico. La corretta impostazione della frequenza di taglio è fondamentale per ottenere il risultato desiderato nel filtraggio del segnale.


Un filtro passa basso del primo ordine può essere realizzato utilizzando un amplificatore operazionale in configurazione invertente insieme a un componente reattivo, come un condensatore. Questo tipo di filtro è noto anche come filtro RC a un polo, poiché coinvolge un solo componente reattivo. La configurazione tipica di un filtro passa basso del primo ordine con un amplificatore operazionale è chiamata filtro RC passa basso.

Nel filtro RC passa basso, il condensatore è collegato tra l’ingresso invertente dell’amplificatore operazionale e la sua uscita. La resistenza è collegata all’ingresso invertente e al terminale di uscita dell’amplificatore (in parallelo al condensatore). La frequenza di taglio del filtro è determinata dalla relazione tra la resistenza (R) e la capacità (C) secondo la formula:

fc = 1/(2πRC)

Dove:

  • fc è la frequenza di taglio,
  • R è la resistenza,
  • C è la capacità.

Qui sotto vediamo un esempio di filtro passa basso del primo ordine realizzato con un amplificatore operazionale in configurazione invertente:

Schema di base di un filtro passa basso del primo ordine realizzato con un amplificatore operazionale in configurazione invertente
Schema di base di un filtro passa basso del primo ordine realizzato con un amplificatore operazionale in configurazione invertente

In questo particolare caso in cui le resistenze sono uguali, il guadagno in tensione è pari a 1 [V/V] (pari a 0 dB) per le frequenze abbastanza inferiori alla frequenza di taglio fc e, man mano che la frequenza aumenta, diminuisce gradualmente. Questo tipo di filtro offre un’attenuazione di 20 dB/decade per frequenze in ingresso al di sopra della frequenza di taglio.

La configurazione del filtro passa basso del primo ordine con amplificatore operazionale offre una soluzione semplice e efficace per ottenere un’attenuazione graduale delle alte frequenze, ed è ampiamente utilizzata in applicazioni audio e di elaborazione del segnale.

Vediamo ora alcune simulazioni eseguite con LTSpice che ci mostrano il comportamento del filtro sia nel dominio della frequenza che nel dominio del tempo.

Il circuito utilizzato è il seguente:

Schema per la simulazione LTSpice
Schema per la simulazione LTSpice

Il generatore V2 rappresenta la batteria da 9V. I due resistori da R5 e R6 dividono la tensione di alimentazione (9V) in modo da creare una massa virtuale. Quindi la giunzione dei due resistori è la massa del circuito mentre le due alimentazioni duali saranno V+ = 4.5V e V = -4.5V rispetto alla massa virtuale.

In questo circuito le resistenze R3 e R1 sono indicate con {R}. Questo significa che il loro valore varia perché è stato definito da una direttiva Spice cioè .step param R 1k 10k 1k la quale dice che il valore di R parte da 1kΩ e arriva a 10kΩ a step di 1kΩ. Quindi vengono fatte 10 simulazioni, una per ciascun valore di R.

Le resistenze R4 e R2, pari a 10Ω, sono messe per sicurezza in modo che se nel circuito reale le resistenze R1 e R3 dovessero assumere il valore 0Ω, non si creerebbero corto circuiti. Con i valori attuali abbiamo come valori estremi della frequenza di taglio:

  • fc1 = 3388Hz per R = 1kΩ
  • fc2= 338Hz per R = 10kΩ

Il generatore V3 genera un’onda quadra di frequenza pari a 1kHz. Quindi la fc1 è molto al di sopra della frequenza del segnale in ingresso mentre la fc2 è molto al di sotto. Dall’analisi di Fourier si sa che l’onda quadra contiene solo armoniche dispari. Nel primo caso verranno attenuate le sole armoniche oltre i 3388Hz circa mentre nel secondo caso tutte le armoniche, compresa la fondamentale a 1kHz, verranno attenuate.

Prima di vedere come ciò si ripercuote sul segnale in uscita, diamo un’occhiata al diagramma di Bode del filtro (al variare del valore di R):

Diagramma di Bode del filtro (solo modulo)
Diagramma di Bode del filtro (solo modulo)

Come si può osservare, a partire dai circa 300Hz tutte le curve, chi più chi meno, stanno iniziando ad attenuare. In particolare la curva che attenua meno (la verde in alto) è quella corrispondente alla fc1 per R = 1kΩ (alla frequenza di 1kHz la sua attenuazione è praticamente trascurabile) mentre l’ultima in violetto, corrispondente alla fc2 per R = 10kΩ, è quella che attenua di più. Già alla frequenza di 1kHz presenta una attenuazione di circa 10dB.

Vediamo ora come tutto ciò si traduce sulla nostra onda quadra a 1kHz:

Variazione dell'onda quadra al variare della frequenza di taglio del filtro
Variazione dell’onda quadra al variare della frequenza di taglio del filtro

Come puoi osservare, nel semipiano inferiore abbiamo l’onda quadra in ingresso mentre nel semipiano superiore abbiamo l’onda quadra in ingresso elaborata dal filtro al variare della frequenza di taglio.

L’onda verde, quella più somigliante ad una vera onda quadra, è quella corrispondente alla fc1. Nel diagramma di Bode abbiamo visto che c’era una piccolissima attenuazione ad 1 kHz e una attenuazione di 3dB alla frequenza fc1. Questa attenuazione fa sì che l’onda non sia perfettamente quadra perchè ha perso le armoniche superiori ai 3kHz e quindi si presenta con un angolo smussato.

Man mano che la frequenza di taglio si abbassa per tendere a fc2, sempre più armoniche vengono attenuate e il segnale, di conseguenza, si trasforma notevolmente fino all’ultima curva, quella violetta, dove assomiglia ad un’onda triangolare.

Fin qui la teoria che spero non sia stata troppo noiosa!

Passiamo alla parte pratica.

Di che componenti abbiamo bisogno?

La lista dei componenti non è particolarmente lunga:

  • una breadboard per connettere la NodeMCU ESP8266 agli altri componenti
  • alcuni fili DuPont (maschio – maschio, maschio – femmina, femmina – femmina)
  • 2 resistori da 47kΩ
  • 2 resistori da 10Ω
  • 4 condensatori da 47nF
  • un amplificatore operazionale LM833
  • due potenziometri digitali MCP41010
  • un connettore per batteria da 9V
  • una batteria da 9V
  • e, ovviamente, una scheda NodeMCU ESP8266 !
  • OPZIONALE: un modulo amplificatore con LM386
  • OPZIONALE: un altoparlante da 4/8 Ω e almeno 2/3 W

Per generare il segnale ad onda quadra abbiamo bisogno di un generatore di segnali. Un modello semplice è più che sufficiente.

In alternativa, puoi anche utilizzare l’uscita cuffie del pc e usare un qualunque software audio in grado di generare un’onda quadra da 1kHz.

Realizzazione del progetto

Lo schema elettrico

Prima di realizzare il circuito vero e proprio diamo un’occhiata al pinout della board:

Pinout del NodeMCU ESP8266
Pinout del NodeMCU ESP8266

Vediamo ora lo schema elettrico Fritzing del filtro:

Schema elettrico completo
Schema elettrico completo

Ciò che faremo è iniettare in ingresso un segnale ad onda quadra da 500mVpp a 1 kHz e far girare uno sketch di test che, agendo sui potenziometri digitali, abbasserà per step la frequenza di taglio del filtro. Collegando l’uscita ad un oscilloscopio ci ritroveremo un segnale che varia come quello ottenuto a seguito della simulazione.

Opzionalmente possiamo usare (per sentire l’effetto del filtro sul segnale in ingresso) un modulo amplificatore con LM386 e un altoparlante da 4/8 Ω e almeno 2/3 W.

Vediamo ora il modulo amplificatore con LM386:

Modulo amplificatore con LM386
Modulo amplificatore con LM386

Il connettore superiore è l’ingresso dell’alimentazione e del segnale. Il terminale VDD va connesso al terminale Vin dell’ESP8266 (che è a tensione 5V), i terminali GND al GND del circuito mentre il terminale IN rappresenta l’ingresso del segnale da amplificare (corrispondente al pin 1 dell’ LM833)

Il connettore inferiore viene collegato all’altoparlante: il terminale GND al filo nero (o segnato con “-“) dell’altoparlante, il terminale OUT al filo rosso (o segnato con “+”).

Questo modulo si basa sul circuito integrato LM386, un amplificatore di potenza in bassa frequenza, in grado di erogare un paio di W su un carico di 4/8 Ω.

Operante con alimentazione singola da 4V a 12V, questo amplificatore IC è ideale per progetti audio di piccola scala come radio portatili, interfacce audio e progetti DIY. Alcune caratteristiche chiave includono:

  • Guadagno regolabile: l’ LM386 offre la possibilità di regolare il guadagno attraverso l’uso di componenti esterni. Questa caratteristica consente una personalizzazione del livello di amplificazione in base alle esigenze del progetto.
  • Basso consumo energetico: grazie al suo basso consumo energetico, l’ LM386 è adatto per applicazioni alimentate a batteria, contribuendo all’efficienza energetica dei dispositivi.
  • Design semplice: l’LM386 semplifica il processo di progettazione con un numero limitato di componenti esterni richiesti per il suo funzionamento. Questo aspetto lo rende particolarmente adatto per progetti DIY e per coloro che si avvicinano per la prima volta alla progettazione di circuiti audio.

Il modulo che utilizzeremo in questo progetto contiene un LM386 e un trimmer e costituisce un amplificatore audio pre-assemblato, progettato per semplificare l’integrazione degli amplificatori audio in progetti elettronici. Le caratteristiche includono:

  • LM386 integrato: il modulo monta l’LM386 come componente principale, fornendo un’ampia gamma di possibilità di amplificazione per segnali audio a bassa potenza.
  • Guadagno regolabile: il trimmer presente sul modulo consente la regolazione del guadagno dell’LM386. Questa regolazione può essere adattata alle esigenze specifiche del progetto, consentendo un controllo preciso sulla potenza del segnale amplificato.
  • Facilità d’uso: grazie al modulo pre-assemblato, gli utenti possono integrare facilmente la funzionalità di amplificazione audio nei loro progetti senza dover progettare il circuito da zero.
  • Flessibilità di alimentazione: il modulo è progettato per funzionare con un’ampia gamma di tensioni di alimentazione, contribuendo alla sua versatilità.

Questo modulo è particolarmente adatto per progetti audio DIY, altoparlanti portatili, e altre applicazioni in cui è necessario amplificare segnali audio a bassa potenza.

Vediamo ora lo schema elettrico Fritzing del filtro col modulo amplificatore:

Schema elettrico completo con modulo amplificatore
Schema elettrico completo con modulo amplificatore

Nota che ci sono 4 condensatori da 47nF. Uno è quello proprio del filtro, collegato fra i terminali 1 e 2 dell’LM833. Uno, disegnato sul lato sinistro dell’ESP8266 è collegato fra la linea positiva Vin e la massa per filtrare (ripulire dai disturbi) questa alimentazione. Gli altri due sono collegati da una parte alla massa e dall’altra rispettivamente al polo + e al polo – della batteria da 9 V per filtrare (ripulire dai disturbi) anche questa alimentazione duale.

Lo sketch

Creiamo il progetto PlatformIO

Abbiamo già visto la procedura di creazione di un progetto PlatformIO nell’articolo Come creare un progetto per NodeMCU ESP8266 con PlatformIO.

Non installare le librerie indicate nell’articolo in quanto non useremo alcuna libreria.

Ora modifica il file platformio.ini per aggiungere queste due righe:

monitor_speed = 115200
upload_speed = 921600

in modo che abbia un aspetto del genere:

[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
monitor_speed = 115200
upload_speed = 921600

Ovviamente puoi scaricare il progetto dal link seguente:

Sostituisci il file main.cpp del progetto che hai creato con quello presente nel file zip.

Vediamo ora come funziona lo sketch.

Si inizia con l’inclusione della solita libreria Arduino.h:

#include <Arduino.h>

Vengono poi definiti i GPIO di controllo dei potenziometri digitali:

int CS_signal = D2;                      // Chip Select signal on pin D2 of ESP8266
int CLK_signal = D4;                     // Clock signal on pin D4 of ESP8266
int MOSI_signal = D5;                    // MOSI signal on pin D5 of ESP8266

e il command byte (di cui abbiamo parlato nel paragrafo dedicato ai potenziometri digitali) assieme al valore di inizializzazione:

byte cmd_byte2 = B00010001 ;            // Command byte
int initial_value = 0;                // Setting up the initial value

Vengono poi definiti altri tre parametri:

#define LOOP_DELAY 2000
#define RESISTANCE_STEP 30.5
double CAPACITY = 0.000000047;

Il LOOP_DELAY vale 2 secondi ed è l’intervallo tra uno step e l’altro. In poche parole abbasseremo la frequenza di taglio del filtro ogni due secondi.

Il CAPACITY è banalmente il valore del condensatore di retroazione (quello da 47nF) espresso in Farad (ci serve per il calcolo della frequenza di taglio).

Il parametro RESISTANCE_STEP è un parametro sperimentale. In realtà il valore massimo misurato sui potenziometri digitali MCP41010 si è rivelato diverso da 10kΩ (li ho misurati ponendo il valore di controllo al suo massimo, cioè 255) ma molto più prossimo a 7.8KΩ. Quindi lo step di incremento di resistenza non è dato (almeno nel mio caso) da 10000Ω/256 cioè circa 39Ω ma da 7800Ω/256 cioè circa 30.5Ω

Prova anche tu a misurare il valore massimo della resistenza del potenziometro digitale (mandandogli tramite la porta SPI il valore 255) per vedere se corrisponde ai 10kΩ del datasheet e poi dividi il valore trovato per 256 in modo da avere il vero valore di RESISTANCE_STEP.

NOTA: è ovvio che se tra il cursore del potenziometro digitale e uno degli altri due terminali il valore di resistenza è massimo, tra il cursore e l’altro terminale il valore è nullo (o quasi).

Seguono le funzioni spi_transfer e spi_out che gestiscono la trasmissione attraverso la porta SPI

void spi_transfer(byte working) {
    for(int i = 1; i <= 8; i++) {                                           // Set up a loop of 8 iterations (8 bits in a byte)
     if (working > 127) { 
       digitalWrite (MOSI_signal,HIGH) ;                                    // If the MSB is a 1 then set MOSI high
     } else { 
       digitalWrite (MOSI_signal, LOW) ; }                                  // If the MSB is a 0 then set MOSI low                                           
    
    digitalWrite (CLK_signal,HIGH) ;                                        // Pulse the CLK_signal high
    working = working << 1 ;                                                // Bit-shift the working byte
    digitalWrite(CLK_signal,LOW) ;                                          // Pulse the CLK_signal low
    }
}

void spi_out(int CS, byte cmd_byte, byte data_byte){                        // we need this function to send command byte and data byte to the chip
    
    digitalWrite (CS, LOW);                                                 // to start the transmission, the chip select must be low
    spi_transfer(cmd_byte); // send the COMMAND BYTE
    delay(2);
    spi_transfer(data_byte); // send the DATA BYTE
    delay(2);
    digitalWrite(CS, HIGH);                                                 // to stop the transmission, the chip select must be high
}

la funzione initialize che manda il valore iniziale alla porta SPI:

void initialize() {                     // send the command byte of value 0 (initial value)
    spi_out(CS_signal, cmd_byte2, initial_value);
}

e la funzione commandPotentiometer usata nel loop per impartire i vari valori degli step e calcolare la corrispondente frequenza di taglio impostata effettivamente sul filtro:

void commandPotentiometer(int step) {
    spi_out(CS_signal, cmd_byte2, step); 
    Serial.print("step: ");
    Serial.println(step); 
    Serial.print("approximate resistance value: ");
    Serial.print(10 + (step * RESISTANCE_STEP)); 
    Serial.println(" Ω");
    Serial.print("approximate cutoff frequency value: ");
    Serial.print(1/((10 + (step * RESISTANCE_STEP)) * 3.14 * 2 * CAPACITY)); 
    Serial.println(" Hz");
    Serial.println();
    Serial.println();
}

Abbiamo poi la funzione setup:

void setup() {
    pinMode (CS_signal, OUTPUT);
    pinMode (CLK_signal, OUTPUT);
    pinMode (MOSI_signal, OUTPUT);

    initialize();

    Serial.begin(115200);                                                     // setting the serial speed
    Serial.println("ready!");
}

che inizializza i GPIO della porta SPI e li definisce come OUTPUT, setta il valore iniziale da mandare sul bus SPI e inizializza la porta seriale.

Nella funzione loop si susseguono i vari comandi per settare il valore corrente di resistenza dei due potenziometri digitali aumentandolo in modo da abbassare ad ogni step la frequenza di taglio del filtro:

int i = 2;
commandPotentiometer(i);
delay(LOOP_DELAY);

i = 8;
commandPotentiometer(i);
delay(LOOP_DELAY);

i = 12;
commandPotentiometer(i);
delay(LOOP_DELAY);

i = 30;
commandPotentiometer(i);
delay(LOOP_DELAY);

i = 45;
commandPotentiometer(i);
delay(LOOP_DELAY);


i = 64;
commandPotentiometer(i);
delay(LOOP_DELAY);

i = 96;
commandPotentiometer(i);
delay(LOOP_DELAY);

i = 128;
commandPotentiometer(i);
delay(LOOP_DELAY);

i = 160;
commandPotentiometer(i);
delay(LOOP_DELAY);

i = 192;
commandPotentiometer(i);
delay(LOOP_DELAY);

i = 224;
commandPotentiometer(i);
delay(LOOP_DELAY);

Adesso carica lo sketck, inietta un’onda quadra di 500mVpp a 1 kHz nell’ingresso e collega l’oscilloscopio e/o il modulo amplificatore. Se tutto va bene dovresti ottenere un risultato del tipo mostrato nel seguente video:

Noterai che sull’oscilloscopio sono rappresentate due tracce: quella celeste è l’ingresso (cioè il segnale proveniente dal generatore di segnali ossia un’onda quadra con ampiezza pari a 500 mVpp e frequenza pari a 1 kHz) mentre quella gialla è l’uscita.

Noterai che all’inizio (step 2) questa traccia assomiglia molto ad un’onda quadra e, in particolare a quella di ingresso (con la sola differenza che hanno fase invertita dato che, come ricorderai, l’amplificatore operazionale si trova in configurazione invertente). Si somigliano molto perchè in questo caso, essendo la resistenza dei potenziometri digitali molto bassa, la frequenza di taglio è molto maggiore di 1 kHz, quindi solo poche armoniche vengono bloccate/attenuate dal filtro. Man mano che si va avanti con gli step, la resistenza aumenta, di conseguenza la frequenza di taglio diminuisce e quindi aumenta il numero di armoniche dell’onda quadra bloccate/attenuate dal filtro, con la conseguente modifica della forma d’onda del segnale di uscita che assomiglia sempre di più ad un’onda triangolare. Oltre che visivamente, questo comportamento può essere avvertito anche tramite l’altoparlante dato che il timbro del suono (oltre che la sua ampiezza) cambia.

Aggiunta di un controllo remoto

Abbiamo quindi testato il filtro con lo sketch di test che però non ci lascia molta libertà di controllarlo a piacimento. Possiamo allora sfruttare la capacità dell’ESP8266 di collegarsi al WiFi e di essere comandata tramite delle API REST. Ciò ci consente di inserire il nostro filtro in un sistema più ampio e complesso dove moduli di questo genere vengono controllati e impostati in tempo reale da appositi programmi in modo da generare effetti diversi. Abbiamo già affrontato il discorso sulle API REST nell’articolo Come realizzare un server API REST con la ESP32 quindi ti consiglio di andare a dargli un’occhiata. Anche se lì si parla di ESP32 la base concettuale non cambia.

Come già evidenziato nell’articolo citato, le API REST sono un modo per far comunicare dispositivi diversi fra loro oppure per consentirci di interagirci. Col secondo sketch che useremo in questo articolo creeremo un server web con la ESP8266 in modo che questa esponga un set di API REST. Possiamo utilizzare queste API REST per interagire con la ESP8266 in modo da ricevere dati dal filtro o mandare dati verso il filtro. In particolare manderemo il valore compreso fra 0 e 255 per settare la resistenza dei due potenziometri digitali con una POST e otterremo il valore di resistenza impostato sui potenziometri digitali e il valore della frequenza di taglio tramite una GET.

L’API di tipo GET avrà questo aspetto:

http://IP_ESP8266/getValues

ed invierà come risposta un file Json fatto così:

{
	"cutoffFrequency": "56874",
	"cutoffResistance":  "4850"
}

dove IP_ESP8266 è l’indirizzo IP assegnato alla board dal router, cutoffFrequency rappresenta il valore della frequenza di taglio calcolata mentre cutoffResistance rappresenta il valore della resistenza presentata dai due potenziometri digitali.

L’API di tipo POST ha questo aspetto:

http://IP_ESP8266/setCutoffFrequency

ed invierà alla nostra ESP8266 un file Json fatto così:

{
	"resistanceCode": "156"
}

dove IP_ESP8266 è l’indirizzo IP assegnato alla board dal router e resistanceCode rappresenta il valore compreso fra 0 e 255 per settare la resistenza dei due potenziometri digitali.

Per interagire con la ESP8266 tramite le API REST useremo un programma che si chiama Postman, di cui vedremo l’uso più avanti.

Anche per questo esempio realizzeremo il progetto utilizzando l’IDE PlatformIO.

Crea quindi un nuovo progetto con PlatformIO, scarica il file di progetto seguente:

Decomprimilo e sostituisci il file main.cpp e il file platformio.ini del progetto appena creato con quelli presenti nel progetto scaricato e decompresso.

Il file platformio.ini avrà un aspetto del genere:

[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps = 
	bblanchon/ArduinoJson@^6.21.0
	https://github.com/tzapu/WiFiManager.git

Vediamo ora questo sketch. Esso è basato sul precedente. In più c’è la gestione del collegamento WiFi e delle API REST, quindi non mi dilungherò sulle parti già trattate in precedenza.

Inizialmente vengono incluse le librerire necessarie:

#include <Arduino.h>
#include <ESP8266WebServer.h>
#include <ArduinoJson.h>
#include <WiFiManager.h>

Vengono definite poi le variabili

unsigned long measureDelay = 500;        // 0.5 seconds        
unsigned long lastTimeRan;

che decidono ogni quanto tempo la funzione loop deve comandare i potenziometri digitali.

Successivamente viene instanziato il server in ascolto sulla porta 80, viene definito un documento Json, un buffer da 1024 caratteri e la variabile che conterrà il codice (compreso fra 0 e 255) che imposterà le resistenze dei potenziometri digitali:

// Web server running on port 80
ESP8266WebServer server(80);

StaticJsonDocument<1024> jsonDocument;

char buffer[1024];

int resistanceCode = 0;

Segue la funzione handlePost che gestisce l’ API di tipo POST:

void handlePost() {
  if (server.hasArg("plain") == false) {
    //handle error here
  }
  String body = server.arg("plain");
  deserializeJson(jsonDocument, body);
  
  // Get code for resistance
  resistanceCode = jsonDocument["resistanceCode"];

  // Respond to the client
  server.send(200, "application/json", "{}");
}

e le funzioni createJson, addJsonObject, getValues che gestiscono il documento Json e restituiscono, a seguito di richiesta GET, i valori di cutoffFrequency e cutoffResistance:

void createJson(char *name, float value, char *unit) {  
  jsonDocument.clear();
  jsonDocument["name"] = name;
  jsonDocument["value"] = value;
  jsonDocument["unit"] = unit;
  serializeJson(jsonDocument, buffer);  
}

void addJsonObject(char *name, float value, char *unit) {
  JsonObject obj = jsonDocument.createNestedObject();
  obj["name"] = name;
  obj["value"] = value;
  obj["unit"] = unit;  
}

void getValues() {
  Serial.println("Get all values");
  jsonDocument.clear(); // Clear json buffer
  addJsonObject("cutoffFrequency", cutoffFrequency, "Hz");
  addJsonObject("cutoffResistance", cutoffResistance, "Ω");
  serializeJson(jsonDocument, buffer);
  server.send(200, "application/json", buffer);
}

Segue la funzione setupApi che instrada gli url /getValues e /setCutoffFrequency verso le funzioni appropriate e inizializza il server:

void setupApi() {
  server.on("/getValues", getValues);
  server.on("/setCutoffFrequency", HTTP_POST, handlePost);
 
  // start server
  server.begin();
}

La funzione setup ha in più la parte:

WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
// it is a good practice to make sure your code sets wifi mode how you want it.

//WiFiManager, Local intialization. Once its business is done, there is no need to keep it around
WiFiManager wm;

// reset settings - wipe stored credentials for testing
// these are stored by the esp library
// wm.resetSettings();

// Automatically connect using saved credentials,
// if connection fails, it starts an access point with the specified name ( "AutoConnectAP"),
// if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())
// then goes into a blocking loop awaiting configuration and will return success result

bool res;
// res = wm.autoConnect(); // auto generated AP name from chipid
// res = wm.autoConnect("AutoConnectAP"); // anonymous ap
res = wm.autoConnect("AutoConnectAP","password"); // password protected ap

if(!res) {
    Serial.println("Failed to connect");
    ESP.restart();
} 
else {
    //if you get here you have connected to the WiFi    
    Serial.println("Connected...yeey :)");
}

setupApi();

che si occupa della gestione del collegamento WiFi e chiama la funzione setupApi().

La funzione loop gestisce le chiamate del client da parte del server e, ogni measureDelay secondi, chiama la funzione commandPotentiometer(resistanceCode) che imposta il valore contenuto in resistanceCode (ricevuto dalla API POST):

server.handleClient();

if (millis() > lastTimeRan + measureDelay)  {
commandPotentiometer(resistanceCode);
lastTimeRan = millis();

}

Come connettere la board ad Internet

Dopo aver caricato lo sketch sulla board, apri il Serial Monitor per vedere i messaggi provenienti dal dispositivo.

Per prima cosa la board va in modalità Access Point e ci fornirà un indirizzo IP che useremo a breve. Questa operazione serve per connettere la board ad Internet senza dover inserire nel codice i parametri della rete WiFi (SSID e password).

La board ci fornisce il suo indirizzo IP
La board ci fornisce il suo indirizzo IP

In questo caso l’indirizzo IP è 192.168.4.1.

A questo punto la ESP8266 è in modalità Access Point (con SSID AutoConnectAP) e dobbiamo connettere il nostro computer alla rete AutoConnectAP. Se andiamo nel menu delle reti del nostro computer, dovremmo vedere anche la rete AutoConnectAP nell’elenco delle reti wireless.

Lista delle reti WiFi disponibili
Lista delle reti WiFi disponibili

Connetti il computer alla rete AutoConnectAP. Quindi vai sul tuo browser e inserisci l’IP precedentemente fornito dalla ESP8266 (che in questo esempio è 192.168.4.1)

Vedrai una schermata come questa:

La schermata del browser per scegliere la rete
La schermata del browser per scegliere la rete

Clicca il bottone ConfigureWiFi. Ti mostrerà le reti disponibili:

Lista delle reti disponibili
Lista delle reti disponibili

Scegli la SSID della tua rete:

Scegli la tua rete
Scegli la tua rete

Inserisci la password della tua rete e clicca il bottone save:

Inserisci la password
Inserisci la password

La risposta della board
La risposta della board

Il modulo ESP8266 conserva memorizzati i parametri di accesso anche se lo spegni, li ricorderà al riavvio e si ricollegherà automaticamente senza dover ripetere questa procedura. Solo se lo resetti rimuovendo il commento da questa riga

// wm.resetSettings();

perderà i parametri di connessione.

Nota Bene: il dispositivo può memorizzare solo una rete. Se successivamente lo colleghi a un’altra rete, dimenticherà le impostazioni della rete precedente.

Testiamo il progetto con le API REST

Una volta che la ESP8266 è stata connessa alla rete WiFi ci fornirà tramite il Serial Monitor di PlatformIO il suo indirizzo IP, come visibile nella figura seguente:

Ricaviamo l'IP della board
Ricaviamo l’IP della board

In questo caso l’IP assegnato dal router alla board è 192.168.1.9. Tale IP ci servirà per comporre le API REST.

Per interagire con la board abbiamo bisogno di un software apposito che si chiama Postman. Dopo aver installato il programma, siamo pronti ad usarlo.

Ecco come si presenta la sua schermata iniziale:

Schermata iniziale di Postman
Schermata iniziale di Postman

Nella finestra principale si trova una barra in cui dovrai inserire l’API.

Alla sinistra di questa barra c’è un menù a tendina che consente di scegliere il tipo di API (per esempio GET, POST, PUT…).

Scegli ora il tipo POST e inserisci l’API POST che avrà formato:

192.168.1.8/setCutoffFrequency

NOTA: negli esperimenti successivi è stato assegnato l’IP 192.168.1.8 quindi le immagini seguenti faranno riferimento a questo IP.

Ovviamente tu dovrai mettere l’indirizzo IP assegnato alla tua ESP8266.

Prima di premere il tasto Send, dovrai selezionare la voce Body che sta sotto la barra dell’URL. Poi seleziona la voce raw (sotto Body) e poi, sul menù a tendina sulla destra, seleziona la voce JSON al posto di Text, come mostrato in foto:

Selezione dei parametri per l'API di tipo POST
Selezione dei parametri per l’API di tipo POST

Nella finestra sottostante inserisci il Json:

{
        "resistanceCode": "128"
}

Ecco come dovrebbe apparirti la finestra di Postman:

La finestra di Postman con l'API POST
La finestra di Postman con l’API POST

Premi il tasto Send per mandare il Json con il parametro resistanceCode. Variando il valore del parametro resistanceCode (compreso tra 0 e 255) varierai la resistenza dei potenziometri digitali e, di conseguenza, la frequenza di taglio del filtro. Più il resistanceCode si avvicina a 255, più la frequenza di taglio si abbassa.

Per vedere lo stato attuale del filtro (cioè per vedere che resistenza è stata impostata e, di conseguenza, che frequenza di taglio, scegli ora il tipo GET e inserisci l’API GET che avrà formato:

http://IP_ESP8266/getValues

Per esempio, in questo caso, essendo l’IP assegnato 192.168.1.8, l’URL dell’API sarà:

http://192.168.1.8/getValues

Ovviamente tu dovrai mettere l’indirizzo IP assegnato alla tua ESP8266.

Premi il tasto Send sulla destra.

L’API restituirà un file Json che riporta i valori rilevati cutoffFrequency e cutoffResistance, come mostrato nella immagine seguente:

Risultato dell'API GET
Risultato dell’API GET

Osservazioni finali

Hai potuto vedere come interagire con la ESP8266 tramite le API REST e quindi variare la frequenza di taglio del filtro. Per farlo hai usato il programma Postman. Ma si potrebbe interagire anche in altro modo, come per esempio con uno script Python che interroga la board automaticamente sempre tramite API REST. Quindi disporre di un programma che interagisce, dando comandi o ricevendo dati, in maniera del tutto autonoma e indipendente.

Ma tali comandi possono anche essere impartiti da un altro dispositivo come un’altra ESP8266 (o una ESP32 o qualunque altro dispositivo adatto), realizzando così una vera e propria comunicazione fra dispositivi (senza intervento manuale da parte nostra).

Se sei un chitarrista e vuoi applicare questo filtro, o meglio una versione un po’ più avanzata, ti consiglio di dare una lettura all’articolo Distorsore per chitarra fai da te con ESP8266: costruzione, utilizzo e regolazione per realizzare un disorsore personalizzato.

Newsletter

Se vuoi essere aggiornato sui nuovi articoli, iscriviti alla newsletter. Prima dell’iscrizione alla newsletter leggi la pagina Privacy Policy (UE)

Se ti vuoi disiscrivere dalla newsletter clicca sul link che troverai nella mail della newsletter.

Inserisci il tuo nome
Inserisci la tua email
0 0 votes
Valutazione articolo
guest
0 Commenti
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
Torna in alto