Controller MIDI con Arduino Micro

Quando il mondo si è fermato per la pandemia di COVID-19, fare cover di discutibilmente popolari canzoni dance italiane non è stata l’unica cosa che ho fatto. In effetti, proprio nel bel mezzo di quel progetto mi sono reso conto che mi mancava qualcosa: l’abilità di controllare in maniera tangibile svariati parametri dei VSTi che stavo usando. In parole semplici: mi mancava un banco di cursori da spingere su e giù per controllare cose tipo dinamiche ed espressività dei miei quattro prodi musicisti. Volevo una cosa semplice, letteralmente quattro cursori che potessero inviare, a scelta, uno qualunque dei 128 comandi di controllo MIDI. Una rapida ricerca mi ha convinto però che qualunque prodotto in commercio fosse o troppo limitato o troppo esagerato per le mie necessità. In ogni caso più costoso di quanto fossi disposto a spendere.

Così mi sono detto: non avrò mica fatto l’ITIS per niente, no? Me lo faccio io! Era da un po’ che avevo voglia di giocare con un Arduino, e questo mi sembrava proprio il progetto perfetto per cominciare. Quanto difficile potrà mai essere mettere insieme quattro potenziometri e quattro… coppie di bottoni… con una manciata di display a sette segmenti… più un po’ di logica di controllo… e un multiplexer…

Russell Crowe in "A Beautiful Mind" davanti ad una lavagna piena di conti complicati.
È stato lì che Russell è venuto a fermarmi, ma era troppo tardi.

Fase 1: scegliere l’Arduino

Uno normale partirebbe col progettare il sistema: quanti controlli? Di che tipo? Che funzioni? Tutte queste cose normali, no? Io invece sono partito dritto con troppe idee ma confuse a scegliere l’Arduino, perché viene fuori che gli Arduini non sono mica tutti uguali! Incidentalmente, scegliere l’Arduino mi ha aiutato a riflettere sul sistema, quindi ok.

  1. Quattro potenziometri (lineari) hanno bisogno di quattro ingressi analogici. Ne basterebbe uno e un multiplexer ma a) praticamente tutti gli Arduini hanno almeno quattro ingressi analogici, e b) un multiplexer complica circuito e codice.
  2. Ciascun potenziometro è associato ad un CC da 0 a 127, quindi mi servono dei bottoni per mandare su e giù il numero. Certo, scorrere 128 numeri su e giù schiacciando bottoni è una rottura, quindi via di encoder rotativi incrementali che sono una cosa bella che uno li gira e li gira e li gira e questi dicono “hey, sto girando in senso orario!” o “hey, sto girando in senso anti-orario!” e io devo solo aggiungere o togliere al numero a seconda della direzione [1]. Gli encoder rotativi di questo tipo necessitano di due input digitali ciascuno: la maggior parte degli Arduini ne ha almeno 14, quindi siamo a posto. Certo, con un po’ di sudore e multiplexer si può ridursi a soli quattro, due per selezionare quale encoder leggere, e due per leggerlo, ma non facciamo i pidocchiosi.
  3. A questo punto dovrò pure sapere a che CC è associato ciascun cursore, quindi mi servono dei display a sette segmenti, tre per ciascun cursore, che però potrebbero ridursi a due visto che la cifra più significativa è sempre e solo 0 o 1, e quindi a quel punto potrei usare un LED, o anche uno dei puntini di cui spesso questi display sono dotati. Una rapida visita al castello della memoria, ala ITIS, e tutto l’orrore di circuiteria necessario ad usare questi display, fatto di porte logiche, multiplexer, e altre bizantinerie varie, mi ha assalito. Sono virato violentemente verso un LCD a matrice. Lo svantaggio di questo genere di display è che hanno bisogno di molti output digitali, ma per fortuna esiste una cosa chiamata I2C che è essenzialmente magia nera e ci fa risparmiare pin.
  4. Lo scatolotto deve poter comunicare MIDI-over-USB che se uno conosce un po’ il MIDI è una cosa un po’ complicata, ma praticamente ogni strumento moderno ormai ha una porta USB e quasi nessuno ha più le classiche porte MIDI tonde a cinque pin. Il protocollo MIDI è un protocollo seriale a 31250 baud. Praticamente ogni Arduino ha una porta seriale, quindi usare le classiche porte MIDI sarebbe banale, ma poi sarei stato costretto ad usare un adattatore per collegarlo al computer. Purtroppo non tutti gli Arduini hanno un chipset USB nativo che permette loro di apparire ad un host USB come periferica MIDI. Ci sono modi di girare attorno a questa limitazione ma è complicato, e visto che gli Arduini a base di ATMega32U4 e fratellini (notasi la U) ce l’hanno, tanto vale usare quelli.

Con un occhio sia al budget che alle dimensioni, ho scelto l’Arduino Micro [2]. Ho preso la versione con pin saldati perché era più comodo da usare con una breadboard per costruire un prototipo prima di andare di saldatore. Non è la scelta più economica in termini di volume, né di prezzo, ma certo la più flessibile, anche perché metti che un giorno smetta di funzionare e vada sostituito, o voglia farne una versione stabile su un unico PCB con versione custom dell’Arduino, permettendomi così di riutilizzare il Micro per qualche altro giocattolo.

Fase 2: progettare il circuito

Con tutte le considerazioni del caso, progettare il circuito è stato più o meno questione di attaccare i fili giusti ai piedini giusti.

Schema elettrico del controller.

I potenziometri a slitta (Rs1-4) hanno normalmente tre piedini: uno va a massa, uno va alla tensione di riferimento, e uno va all’input analogico dell’Arduino. In condizioni normali uno attaccherebbe il secondo piedino direttamente ai 5 V dell’alimentazione, e così avevo fatto anch’io, ma non riuscivo mai a leggere il valore massimo dall’ingresso analogico. Poi ho scoperto che l’Arduino ha un pin che fornisce la tensione di riferimento su cui confronta gli input analogici, che potrebbe essere più bassa di 5 V per qualsiasi motivo, e quella è la scelta giusta.

Gli encoder rotativi (SW1-4) hanno a loro volta tre piedini: due vanno collegati a un input digitale ciascuno, e uno va collegato a massa. In questa configurazione, bisogna istruire l’Arduino ad usare le resistenze di pull-up sugli input digitali. Il modo in cui funzionano questi encoder è di collegare ciclicamente a massa i due piedini per formare quattro configurazioni di uscita che ci dicono la direzione in cui sta girando l’encoder. Logicamente, quando i piedini non sono collegati a massa, non sono collegati a niente, e quindi l’Arduino deve poter leggere un chiaro segnale diverso da 0 V. Quando diciamo all’Arduino di usare la resistenza di pull-up integrata, questa porta l’ingresso a ~5 V quando il pin non è collegato a niente, come in questo caso. Magia nera. Funziona. Devo dire che inizialmente avevo inserito dei condensatori di debouncing, ma si può fare anche via software e la libreria che sono finito ad usare lo fa, quindi meno componenti, più felicità. Ovviamente questo ruba qualche ciclo di esecuzione al resto del codice, ma non è un furto drammatico.

Infine, il display abbisogna di connessione ai pin I2C, e il tutto si completa collegando chi di dovere all’alimentazione. Normalmente un Arduino si collega ad un piccolo alimentatore a 5 o 3.3 V, o ad una batteria, ma in questo caso lo scatolotto va usato via USB, che è perfettamente in grado di fornire 5 V e massimo 500 mA [3], che sono del tutto sufficienti a far funzionare tutto quanto. Non ho misurato, ma funziona. In ogni caso, l’unico altro componente a richiedere alimentazione è il display, quindi è sufficiente collegarlo ai corrispondenti pin 5V e GND dell’Arduino.

Fase 3a: realizzare il circuito

A questo punto non resta che fare il prototipo su breadboard, scrivere il codice, e infine realizzare il circuito stampato.

Primo prototipo del controller su breadboard, con display che mostra già l'interfaccia finale.
Il prototipo.

Io avrei anche fatto il circuito stampato. Ai tempi dell’ITIS avevo costruito un bromografo per incidere basette di rame fotosensibili a doppio lato, ma poi ho scoperto che le uniche basette fotosensibili che avevo in casa risalivano anche loro ai tempi dell’ITIS e quindi il photoresist se ne era un po’ andato in pensione. Le uniche alternative plausibili a quel punto erano disegnare il circuito a mano col pennarello indelebile o andare di millefori. Il mio odio per la millefori mi ha quasi fatto considerare il pennarello indelebile, ma non è una soluzione molto pratica quando si ha a che fare con componenti i cui piedini distano gli uni dagli altri 2.54 millimetri.

Realizzazione del controller su basetta millefori.
La millefori.

Fase 3b: scrivere il codice

Il codice è tutto disponibile su GitHub. Ci sono circa una riga di commenti ogni cinque di codice, quindi spero che il codice sia abbastanza chiaro da navigare. È piuttosto elementare. Le uniche cose degne di nota forse sono le librerie che ho usato.

  • USB-MIDI: ci sono svariate librerie MIDI e MIDI-over-USB per Arduino. Ho scelto questa perché l’API mi piaceva più delle altre. Nessun altro motivo. Potrei cambiarla in futuro al bisogno. Sono tutte più o meno buone uguali.
  • Rotary: una libreria zerosbatta per leggere lo stato degli encoder. Ha debouncing software gratis, configura i pull-up automaticamente, e ha qualche feature extra che potrebbe servire come no.
  • LiquidCrystal_I2C: ci sono svariate librerie simili, bisogna un po’ provarle perché non tutti i display sono uguali e non tutti i backpack I2C sono uguali. Questa funziona nel mio caso, è sufficientemente documentata, e l’API è abbastanza semplice, quindi l’ho scelta.
  • IoAbstraction: questa è una libreria con svariate utility. Io ne uso il task manager che altro non è che un cosino che permette di programmare dei task da eseguirsi ad intervalli di tempo regolari, o anche one-shot dopo un certo tempo. Si può fare a mano ma questa è comoda ed efficiente, quindi perché far fatica?

Normalmente l’Arduino ha una routine chiamata loop() che esegue continuamente, più veloce che può, più volte che può, fino a che non gli si toglie l’alimentazione. Dentro a questa funzione succedono le cose importanti tipo leggere i sensori, scrivere gli output, e via così.

Io la uso per leggere, trasformare, e memorizzare i valori dei cursori, e controllare se gli encoder si stanno muovendo — e, in caso, per aggiornare i relativi CC [4]. L’invio dei messaggi MIDI e l’aggiornamento del display non devono succedere ogni volta che loop() viene eseguita. I messaggi MIDI vanno inviati normalmente solo quando ce n’è bisogno, cioè solo quando i valori cambiano, e il display si può aggiornare lentamente perché tanto è lento di suo, non è un’informazione cruciale, e tanto i nostri occhi non riescono a distinguere cambiamenti che avvengono più velocemente di circa 40 ms, quindi io lo aggiorno ogni 50 ms, ma il prototipo iniziale lo aggiornava anche ogni 200 ms. Questo mi permette di non sovraccaricare l’Arduino di lavoro e lasciare spazio a cose più importanti.

La routine che si occupa di inviare i messaggi MIDI viene eseguita ogni 5 ms circa. Il funzionamento è semplice: per ciascun controller, verifica se il valore attuale è cambiato dall’ultimo valore registrato. Se è così, invia il corrispondente messaggio. Se non è così, passa oltre. Inviare un messaggio MIDI costa cicli macchina, quindi meno messaggi mandiamo, meglio è, e poi CC sta per Control Change, quindi sono messaggi che non ha senso mandare se il valore non è cambiato.

Fase 4: collaudare il circuito

Ovviamente al primo collaudo sembrava non funzionare niente. Ho passato in rassegna tutte le saldature, inclusa l’orribile massa di stagno che collegava tutti i fili a massa — e adesso avete capito perché si chiama massa. Ho quindi scollegato l’Arduino dalla millefori, e proceduto a ricollegare tutti i componenti alla schedina via millefori, per verificare se ci fossero problemi invisibili alle saldature.

Debugging selvaggio.
Questa la situazione intorno a mezzanotte.

Niente da fare. Ad ormai tarda ora, ho avuto la bella idea di collegare il display direttamente all’Arduino, non via millefori, ed è in quel momento che mi si è palesata la tremenda realtà dei fatti:

Dettaglio di contatto mancante su millefori, Vicenza 2020.
Colonna sonora del momento.

La tremenda realtà dei fatti è che tutto funzionava dal principio ma io mi ero dimenticato di collegare il display all’Arduino e quindi sembrava che non funzionasse niente. Ci ho fatto pure una storia su Instagram, per dire il livello.

Corollario divertente: non mettete in corto roba a caso per sbaglio mentre il vostro Arduino è collegato al computer. Al computer non piace. Se siete fortunati e il vostro computer è furbo, si protegge e vi avverte, ma non tutti i computer sono furbi, e non sempre siamo fortunati e ci troviamo senza porte USB. Per fortuna il mio è più furbo di me.

Intermezzo riflessivo

A questo punto vorrei fare una considerazione. Ricordo piuttosto distintamente le ore di laboratorio dell’ITIS in cui si trasformavano le lunghe ore di teoria in circuiti più o meno utili. Nel laboratorio di elettronica digitale, l’attività principale era tradurre un problema pratico in un ammasso di porte logiche, LED, e occasionalmente qualche altro componente. Il processo è abbastanza semplice e non particolarmente appassionante, specialmente quando il numero di LED da accendere e spegnere arriva all’ordine di svariati display a 7 segmenti. La circuiteria di supporto diventa complicata, e questo spesso è il punto in cui si introducono i microcontrollori e i multiplexer. Ora, alla base di ogni Arduino c’è un microcontrollore. A scuola usavamo il PIC16x84 che è semplice a sufficienza per essere compreso da noi giovani menti in preda agli ormoni, e sufficientemente versatile da poter essere usato per applicazioni interessanti. Questo PIC andava infilato in un circuito programmatore che andava collegato alla porta seriale di un PC. Il chip così programmato, normalmente non prima del terzo tentativo, andava poi trasferito nel prototipo e provato. Era frustrante ma così era. Un progetto del livello di mostrare tre numeri su un display a 7 segmenti poteva benissimo prendere dalle 2 alle 5 ore di lezione, tra progettazione e realizzazione su breadboard, senza neanche passare al disegno del circuito stampato, la fabbricazione della scheda, il montaggio, il collaudo…

Quando ho ricevuto l’Arduino sulla sua bella schedina prefabbricata con la sua simpatica porta micro USB, la prima cosa che ho fatto è stata collegarci il display, collegare l’Arduino al mio fido Macbook Pro, aprire l’IDE, mettere insieme un po’ di righe di codice, schiacciare “upload”, e riempirmi di stupore e meraviglia.

Arduino con LCD che dice "Help I turned into a maker".
Tempo totale: 2 minuti più riuscire ad infilare la USB nel verso giusto da entrambi i lati.

Ora. Arduino non è solo un microcontrollore: è una scheda che include una discreta dose di circuiteria necessaria ed accessoria, e un circuito programmatore integrato così da poter lasciare il microcontrollore al suo posto che poi si piegano i piedini ed è un casino. L’intento principale della scheda è scavalcare la quantità di lavoro iniziale necessaria a rendere il microcontrollore utilizzabile. Tutto questo lavoro va comunque fatto nel momento in cui si volesse trasformare il prototipo in un prodotto finito, indipendentemente dalla tecnologia di base. Però, dal punto di vista di uno che ha un’idea e vuole metterla in pratica velocemente non posso fare a meno di pensare a quanto tempo perdevamo in laboratorio. Perché va bene imparare il processo una volta, va bene ripeterlo la seconda per essere sicuri, ma alla terza è finito l’anno scolastico. Io spero che le cose siano un po’ cambiate, ecco.

Intermezzo WTF

Durante lo sviluppo del codice mi sono scontrato con un fenomeno bizzarro. Collegando il controller al computer, il display veniva aggiornato molto più lentamente di quanto mi aspettassi. Ho subito pensato ad un collo di bottiglia da qualche parte, che è il motivo per cui in giro per il codice ho lasciato delle sezioni commentate marcate con DEBUG con il solo scopo di capire quante volte loop() viene chiamata ogni secondo. Tragicamente, muovendo i cursori questo numero crollava da circa 1500-1900 cicli al secondo giù fino a 0-5. Decisamente non ideale.

Ho dovuto procedere un po’ alla cieca — debuggare software che gira su una scheda esterna senza un debugger hardware è un po’ difficile — ma alla fine ho concluso di non avere colli di bottiglia — però ho ugualmente ottimizzato l’aggiornamento del display, evitandolo quando non ce ne fosse bisogno.

Sempre più confuso, ho aperto GarageBand per vedere quanto male andasse veramente — magari, mi sono detto, i messaggi MIDI vengono comunque inviati a velocità sufficiente — e, come per magia, l’Arduino si è sbloccato. Improvvisamente il display veniva aggiornato alla sua frequenza corretta e i messaggi MIDI venivano spediti con la fluidità che mi aspettavo. Sempre più confuso, ho chiuso GarageBand e, nel giro di pochi secondi, l’Arduino è tornato azzoppato come prima. Aperto Pure Data e configurato l’Arduino come input MIDI, unicorni che scorreggiano arcobaleni. Chiuso Pure Data, Sisifo è tornato a spingere il masso, Ercole a pulire le stalle di Augia, e Frodo a salire faticosamente le pendici del Monte Fato.

MIDI è un protocollo di tipo fire-and-forget in cui un dispositivo invia i suoi messaggi e non si interessa che vengano ricevuti [5]. Non avendo previsto l’uscita MIDI sul classico connettore a 5 poli, l’unica ipotesi che ho potuto fare è che l’USB ci si metta di mezzo. USB è un protocollo a pacchetti basato su interrupt. Non conosco USB a sufficienza, ma ipotizzo che abbia meccanismi di handshaking e acknowledgement per essere sicuro che i pacchetti di dati vengano trasferiti correttamente, e ipotizzo anche che il dispositivo che invia pacchetti USB aspetti di verificare la ricezione prima di inviare nuovi pacchetti — con un timeout che a occhio potrebbe essere intorno ai 2-300 ms. Se dall’altra parte non c’è un’applicazione a ricevere e confermare, si pianta tutto. Ipotizzo perché non sono riuscito a trovare informazioni certe in proposito, quindi se tra voi quattro stronzi che leggete il MorphLog [6] c’è un esperto di USB e MIDI-over-USB, imploro spiegazioni.

Fase 5: inscatolamento

Ovviamente lasciando tutti i componenti per aria è difficile usarli come controller MIDI, quindi mi sono “procurato” una scatola e ho montato tutto insieme. Tralascerò l’ultima passata di saldature perché alcuni fili erano troppo corti — ho già detto che odio le millefori? — e la folle corsa in cerca di queste viti per fissare i cursori che avevano un diametro e un passo poco comuni — quantomeno meno comuni di quello che si può trovare in un garage domestico medio italiano [7].

Lo scatolotto quasi finito.

La versione “finita” è qui sotto, ma finita veramente non è: le manopole degli encoder sono brutte — ma altro non c’era — e quelle dei potenziometri non vanno su correttamente. Un po’ è colpa mia che non ho controllato paranoicamente tutti i dettagli tecnici di tutto, e un po’ è colpa della descrizione sul sito da cui ho acquistato i componenti che faceva sembrare che tutto avrebbe funzionato insieme in perfetta armonia.

Ci pensate che questa roba è passata ai controlli di sicurezza in aeroporto?

Conclusione

Per quelli curiosi, la lista dei componenti è questa:

Più minuteria assortita che avevo già tipo fili unipolari, saldatore, stagno, e via così. Totale circa 60 €, che scende a 40 col buono di Amazon con cui ho comprato l’Arduino. Sessanta euro è in linea con i controller più economici che ho trovato in commercio, che comunque non avevano tutti i requisiti che avevo io, quindi direi che il progetto è stato un successo. Il costo dell’Arduino si abbatte usando quello senza header (normalmente 16 € ma vedo che si trova spesso intorno a 11 €) o anche progettandosi un equivalente custom, e in quel caso l’ATMega32U4 si trova intorno a 3-4 € più il costo di quei due o tre componenti extra tipo un cristallo da 16 MHz e qualche altro accessorio. È il bello dell’hardware open source, baby.

Cose imparate:

  • Arduino è fighissimo. Fare le cose è fighissimo. Sono tornato bambino [8].
  • Ricordarsi sempre di collegare tutte le cose che devono essere collegate, e non collegare tutte le cose che è meglio non siano collegate.
  • Se ti sembra di aver controllato tutto quello che dovevi controllare, non hai controllato abbastanza.
    • Se ti sembra di aver controllato abbastanza ma ancora ciccia, non hai controllato tutto tutto tutto.
      • Se hai controllato tutto tutto tutto, c’è qualcosa che non sapevi di non sapere.
  • I fumi dello stagno hanno un buon profumo ma sono probabilmente nocivi.
  • Le basette millefori sono solo lacrime, dolore, e pentimento: investite in PCB, sia che ve li possiate fare in casa, sia che dobbiate ordinarne 100 pezzi a uno dei molteplici servizi che ve li fabbricano e che ai miei tempi non esistevano. Anche perché probabilmente l’ordine minimo è più tipo 5 che 100.

E ora, via a fare musica.

  1. È un po’ più complicato di così ma questo è un blog di un certo livello, e spero che lo siano anche i miei lettori, quindi forza, tutti a documentarsi.[]
  2. Anche perché avevo un buono da 20 sterline su Amazon e quindi mi è sembrata una buona idea[]
  3. USB 3 arriva a 900 mA, ma l’Arduino non lo sa e usa USB 2.[]
  4. Leggere gli encoder in polling è un altro punto che si può ottimizzare, Arduino supporta gli interrupt che sono un meccanismo molto più appropriato quando si tratta di input digitali che cambiano poco di frequente, ma per questo avrei dovuto rivedere il circuito e magari ci penserò per la prossima versione.[]
  5. È un po’ più complicato di così, ma in questo caso è una buona approssimazione[]
  6. Siete quattro stronzi ma siete i miei quattro stronzi e io vi voglio bene.[]
  7. Sì, ho approfittato di un mese di “lavoro da casa” trascorso a Vicenza per fare tutto ciò. Grazie pandemia![]
  8. Non è vero, lo sono sempre stato.[]

Un commento

Rispondi

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.