Controller radio per effetti sonori

Salve a tutti, in quest’articolo riporto il risultato di un lavoro che mi è stato commissionato da un giovane prestigiatore il quale, visto il mio precedente articolo, mi ha contattato dicendo che desiderava poter riprodurre, per mezzo dell'impianto audio dei posti in cui si esibiva, degli effetti sonori a comando attraverso dei pulsanti nascosti negli abiti (ad esempio all'interno di una giacca).

In questo video potete vedere un esempio di ciò: video

Dialogando per email, sono venuto a sapere che egli disponeva di un impianto audio utilizzato come diffusore musicale di sottofondo per i suoi spettacoli a cui attaccava un computer portatile che riproduceva le playlist adeguate.
Dunque, la soluzione finale che ho proposto è stata quella di costruire un dispositivo trasmettitore nascosto nella giacca del prestigiatore e un dispositivo ricevitore che inviasse via cavo al PC di cui sopra i comandi ricevuti. A bordo del PC, un software scritto appositamente provvede all'interpretazione del comando e alla riproduzione del file audio corrispondente.
Nel prossimo capitolo tratterò brevemente i vantaggi di questo sistema.

Il numero di suoni riproducibili in questo modo è stato fissato a dieci.

E' anche bene chiarire che numerose scelte progettuali sono scaturite dal basso budget disponibile come, ad esempio, la scelta dei moduli radio economici sprovvisti di un sistema di codifica/decodifica interno.


Il progetto si divide dunque in tre parti:

  • Modulo radio-trasmettitore
  • Modulo radio-ricevitore
  • Software lato PC

Questa immagine chiarisce bene il concetto:

Il fatto di utilizzare un PC come interprete e riproduttore dei file audio presenta svariati vantaggi rispetto all'uso di un modulo audio come quello del mio precedente progetto:

  • Essendo il computer già disponibile, non vi sono costi aggiuntivi;
  • I file audio riproducibili possono essere cambiati dall'utente in modo semplice e intuitivo (piuttosto che richiedere una riprogrammazione del modulo audio);
  • Il circuito non si complica eccessivamente, pertanto vi sono meno componenti a rischio di rottura;
  • Nè il numero dei file audio riproducibili nè la loro qualità (vedi bitrate) risentono della memoria ristretta che contraddistingue un modulo audio;
  • Considerando i 500 km che separano casa mia da casa del commissionario, un'eventuale modifica potrebbe essere effettuata semplicemente variando il software lato PC ed inviandolo per email, mentre una riprogrammazione di un eventuale microcontrollore non sarebbe così semplice;

Ovviamente, tutti questi vantaggi derivano dal fatto di avere un PC già disponibile ma non giustificherebbero economicamente l'acquisto dello stesso.

Analizzerò i tre "blocchi" (trasmettitore, ricevitore, software PC) singolarmente nei prossimi capitoli.


Il dispositivo trasmettitore è incaricato della gestione dell'input dell'utente (la pressione dei tasti), della codifica e dell'invio al ricevitore del dato corrispondente.

Trattandosi di un dispositivo "particolare", differente da un normale telecomando, deve rispondere ai seguenti requisiti:

  • Piccole dimensioni, non deve essere ingombrante e deve nascondersi facilmente in un abito;
  • Deve avere pulsanti in varie zone del corpo, collegati al dispositivo con dei cavi;
  • I pulsanti devono essere facilmente sostituibili in caso di rottura;
  • La batteria deve essere semplice da sostituire ed avere una discreta durata;
  • La trasmissione deve essere veloce (va usato in real-time durante gli spettacoli), ma anche affidabile (pensate se un comando venisse frainteso o non inviato!).

Componenti:

I componenti che ho selezionato per questo dispositivo sono principalmente:

  • Un LM7805 per la stabilizzazione della tensione della batteria a 5V;
  • Un PIC16F628A, microcontrollore a 18 pin preposto alla lettura dell'input dell'utente (matrice di tasti) e alla codifica del dato da inviare;
  • Radio trasmettitore Qam-tx1 operante a 433.92 MHz (QAM indica il produttore Quasar e il tipo di modulazione utilizzato AM, non la modulazione di ampiezza e fase QAM);
  • 10 pulsanti collegati al radiocomando attraverso spezzoni di cavo di lunghezza da poco meno di 50cm a quasi 1m.
    Sono connessi secondo una matrice di 5 linee e 2 colonne (usando solo 7 pin del PIC per fare il polling);
  • 20 morsettiere a molla per connettere i cavi dei pulsanti. Le ho scelte perché sono resistenti, anti-strappo e facili da manipolare.

Alimentazione

Il circuito è alloggiato in un contenitore di plastica nera comprato su eBay e provvisto di un comodo scomparto per l'inserimento di una pila da 9V a cui si collega un LM7805 provvisto dei soliti condensatori di corredo. La tensione di alimentazione del circuito è, quindi, pari a 5V.

L’LM7805 ed i suoi circuiti sono ormai conosciuti da tutti, ma per completezza riporto qui sotto lo schema della sezione di alimentazione:

(clicca sull'immagine per vederla più grande)

Un interruttore a pulsante interrompe il positivo della batteria da 9V. Quando questo è chiuso, il 7805 stabilizza la tensione in uscita a 5V. I condensatori da 330nF e 100nF in ingresso ed in uscita all’IC sono necessari per una buona stabilità della tensione, mentre il condensatore da 100uF è un condensatore di filtro che assicura una buona stabilità della linea di alimentazione all'intera circuiteria.

Microcontrollore

Come già anticipato, il microcontrollore utilizzato è il PIC16F628A. Si tratta di un PIC a 18 pin molto diffuso, ed è proprio il PIC giusto per questo compito: uno più piccolo non basterebbe, uno più grande sarebbe sprecato ed ingombrante.

Lo schema delle connessioni è molto semplice ed è riportato qua di seguito:

(clicca sull'immagine per vederla più grande)

In alto a destra è visibile il modulo radio, il quale richiede solo le due connessioni di alimentazione, un ingresso dati e un pin per l'antenna. Dell'utilizzo di questo modulo parlerò nel prossimo capitolo.
L'antenna del trasmettitore è uno spezzone di filo non rigido (non sottoposto quindi a rischio di rottura), dimensionato ad un quarto della lunghezza d'onda della frequenza a cui lavora il modulo (circa 17cm per 433.92 MHz).

Nonostante la disponibilità in questa MCU di un clock interno, ne ho utilizzato uno esterno proveniente da un quarzo a 4 MHz con due condensatori da 22 pF per avere la massima stabilità possibile (maggior stabilità del clock = maggior precisione ed affidabilità del segnale radio).

L'uscita modulata inviata al modulo radio è presente sul pin A2 mentre sul pin A1 troviamo un led che ho utilizzato molto in fase di debug ma che non trova applicazioni pratiche nel normale funzionamento, tranne il lampeggiare due volte all'avvio del PIC (per eventuale debug futuro).

Infine, i pin della porta B sono utilizzati per il polling della tastiera (tranne B7), due come colonne d’uscita e cinque come righe d’ingresso.

Lo schema è tutto qua, come è possibile notare è abbastanza elementare. Nel prossimo capitolo parlerò di come avviene la trasmissione e di quale protocollo ho utilizzato.

Un ulteriore dettaglio è la disposizione dei connettori per i pulsanti. Come ho già detto, ho utilizzato delle morsettiere a molla. I pulsanti sono 10, vi sono quindi 10 coppie di cavi. Di conseguenza ho utilizzato due file di morsettiere a 10 poli connesse ai pin del PIC secondo questo schema:

(clicca sull'immagine per vederla più grande)

Per chiarezza ecco una foto dell'interno del trasmettitore in cui sono visibili le morsettiere ed i terminatori dei cavi che vanno ai pulsanti.
Mentre il cavo dei pulsanti in sé deve essere molto flessibile (per correre comodamente all’interno degli abiti) le morsettiere a molla gradiscono molto di più un cavo monofilare rigido. Ho risolto la situazione saldando dei puntali terminali ai singoli cavi in modo da garantirgli contemporaneamente un’alta flessibilità ed un solido bloccaggio meccanico.
Ecco anche una foto dei pulsanti coi cavi connessi (ho protetto i contatti dei pulsanti con le coperture dei jack audio da 3.5 mm):

(clicca sulle immagini per vederle più grandi)

Nella foto precedente potete inoltre notare la disposizione dei componenti di cui abbiamo appena parlato, anche se questo vi riuscirà sicuramente meglio in quest'altra foto:

(clicca sull'immagine per vederla più grande)

Da notare il ridotto spazio a disposizione per il montaggio del circuito.

Ecco infine uno schema che mette insieme tutti i singoli pezzi visti sinora:

(clicca sull'immagine per vederla più grande)

Bene, l'ultima cosa che manca per comprendere appieno questo dispositivo è il codice sorgente caricato sul PIC e una spiegazione del protocollo di trasmissione utilizzato. Il codice sorgente lo trovate qui di seguito (insieme al file precompilato), la spiegazione del protocollo è nel prossimo capitolo.


Codice Sorgente

Il codice sorgente è scritto in assembly per avere il più stretto controllo possibile sulle tempistiche di trasmissione attraverso l'utilizzo di routine di ritardo calibrate al microsecondo. Per la scrittura e la compilazione ho utilizzato Pic Simulator IDE, dotato di editor, compilatore e tool di simulazione.

Per permettere il corretto funzionamento del programma, i fuses vanno impostati nel modo seguente:

Brown-out reset: abilitato
Flash program memory code protection: disabilitato
Data EEPROM memory code protection: disabilitato
Power-up timer: abilitato
Watchdog timer: disabilitato
Low voltage in-circuit serial programming: disabilitato
RA5/MCLR pin function select: digital input
Oscillator selection: XT

Il codice è relativamente semplice da capire: esegue continuamente la lettura in polling della matrice di pulsanti, in caso uno sia premuto genera il segnale codificato (vedi prossimo capitolo) e lo invia al trasmettitore.


Come ho anticipato nell'introduzione, molte scelte progettuali sono state condizionate in modo decisivo dal basso budget disponibile. Per questo motivo, la comunicazione fra trasmettitore e ricevitore avviene utilizzando dei moduli radio che non includono alcuna forma di codifica/decodifica del segnale. Occorre progettare tutto manualmente.

Per fortuna ciò non mi ha trovato impreparato (vedere il mio precedente articolo in cui ho avuto lo stesso problema), perciò ho riadattato lo stesso protocollo che avevo già accuratamente sviluppato.

Per una spiegazione molto dettagliata del protocollo, vi rimando a quell'articolo. Se non potete accedervi, inviatemi un'email.
Ad ogni modo, rispiegherò brevemente la questione e le modifiche apportate appositamente per questo progetto.

Innanzitutto, i moduli radio utilizzati lavorano alla frequenza di 433.92 MHz, frequenza in banda libera e quindi molto utilizzata da apparecchi di tutti i generi. Questo rende necessario un protocollo robusto in grado di individuare e scartare gli errori.

A causa della tipologia dello stadio d'ingresso e del metodo di funzionamento di questi moduli, è come se l'ingresso fosse accoppiato in alternata (per intenderci, avesse un condensatore in serie che filtrasse il valor medio/tensione continua del segnale d'ingresso). Per questo motivo non si può trasmettere un livello logico alto per tempi lunghi ed è quindi impossibile l'utilizzo di un protocollo standard tipo RS232. Occorre un protocollo in cui vi sia una componente continua minima ma un buon numero di transizioni di livello.

In genere quando sussiste questa condizione, viene utilizzato il protocollo Manchester, ma nel nostro caso i moduli sono talmente "economici" che durante il processo di trasmissione la larghezza degli impulsi del segnale viene alterata e vengono aggiunti alcuni picchi, rendendo molto difficile una sincronizzazione corretta.

La soluzione da me adottata consiste nell'utilizzare un segnale TTL modulato in OOK (Fh=3kHz): il livello logico alto è così rappresentato dall'invio di un'onda quadra a frequenza 3kHz (massimo consentito, riportato nel datasheet del modulo) per un tempo Tbit, il livello logico basso è rappresentato non inviando nulla per un tempo Tbit.
In questo modo, abbiamo componente continua minima, numerose transizioni, una decodifica semplice ed una facile creazione del segnale codificato (compito svolto direttamente dal PIC trasmittente). Inoltre, un segnale di questo tipo è inviato senza subire un'eccessiva distorsione (fatto osservato sperimentalmente)

Per riuscire ad individuare errori generati da eventuali interferenze, ho incluso un rudimentale ma efficace controllo dell'errore: dopo aver inviato i bit di comando (Es: 1100) si invia anche il loro complemento (Es:0011).Si tratta di un codice d’individuazione degli errori molto ridondante (raddoppia quasi il numero di bit da inviare, aggiungendone di non necessari all’informazione) e non permette la correzione dell'errore, ma è praticamente infallibile ed insensibile alle interferenze anche forti e ripetitive.

Per esempio, in presenza di una interferenza continua che mantenga l'uscita del ricevitore alta per molto tempo, i bit del dato saranno tutti a 1 e, quando il ricevitore vedrà che anche i bit di controllo sono a 1, scarterà il comando classificandolo come disturbo.

La durata di un bit (Tbit) è stata scelta come compromesso: aumentandola si ha più affidabilità nella decodifica, ma aumentano i tempi di trasmissione. Il valore scelto è di 10 ms (inferiore rispetto al mio precedente progetto).
Il numero di bit inviati deve permettere di distinguere fra 10 pulsanti, quindi (è intuitivo, ma...) n = log2(10) = 3.32 -> ci servono 4 bit.
I comandi da inviare sono numerati da 1 a 10.

Il formato di invio dei dati è:

  • 1 bit di start a livello logico alto;
  • 4 bit del codice comando dall'MSB all'LSB;
  • 4 bit del codice controllo dall'MSB all'LSB;

Da cui si ricava che il tempo totale di invio di un comando è (1+4+4)*Tbit = 9*Tbit = 90 ms, sufficientemente veloce da sembrare istantaneo, il che è molto importante per il tipo di applicazione a cui è destinato questo progetto.

Facciamo un esempio: supponiamo di dover inviare il comando numero 5, il cui codice binario è 0101. Il codice di controllo è quindi 1010. Questo significa che in uscita dal pin A2 del PIC trasmettitore troveremo un segnale ottenuto modulando in OOK (F=3kHz, Tbit=10ms) il numero 101011010 (il primo è il bit di start).
Troveremo quindi per 10 ms un'onda quadra di 3 kHz, 10 ms di segnale a livello basso, un'onda quadra a 3 kHz per 10 ms, 10 ms di livello basso, 20 ms di onda quadra, ecc... come nell'immagine seguente:

Questo segnale è lo stesso (idealmente) che avremo all'uscita del modulo ricevitore. Vedremo quindi che all'uscita del modulo ricevitore troveremo una rete circuitale predisposta alla trasformazione di questo segnale in qualcosa di simile al segnale binario TTL di partenza (il nostro 101011010). Parlerò di questo nel capitolo seguente.


Per la decodifica del segnale radio (sul ricevitore) ho utilizzato il modulo ricevitore 'gemello' a quello trasmittente, il modulo Qam-rx2. Come la maggior parte di questi moduli, si tratta di un circuito super-rigenerativo. Oltre ai pin di alimentazione e al pin dell'antenna, vi sono non uno ma due pin di uscita. Per quale ragione?

Il secondo pin di uscita, quello ufficiale a livello logico TTL, è l'uscita di un comparatore integrato nel modulo il quale compara il segnale rigenerato con una soglia fissa. In pratica, quando il segnale rigenerato oltrepassa una certa soglia, l'uscita va alta per indicare che si sta ricevendo qualcosa.

Pur funzionando bene, questo meccanismo può non essere il massimo quando il segnale giunge debole al ricevitore, non riuscendo a portare alta l'uscita e causando la perdita dei dati inviati. Nel nostro caso, siccome non utilizziamo il modulo ricevitore ad una distanza di pochi metri dal trasmettitore, dobbiamo tenere conto di questo problema.

Ho condotto varie prove sperimentali su questi parametri, scoprendo che ad una distanza di una trentina di metri (o attraverso un muro) il segnale viene degenerato fino a cadere in questo triste caso.

Ecco che viene in aiuto l'altro pin di uscita: su quel pin è presente il segnale analogico rigenerato. Si tratta in pratica dell'ingresso non invertente del comparatore di uscita.
Ho prelevato l'uscita da questo pin e la ho mandata in un comparatore esterno, su cui ho fissato una soglia modificabile con un trimmer, tarandola in modo da garantire una sensibilità più alta ma non eccessiva. Ho così ottenuto la portata più alta possibile (circa una cinquantina di metri o una trentina con un muro in mezzo).

Il segnale presente su questo pin ha una forma "strana": quando non si riceve niente, è un livello più o meno continuo (qualche centinaio di millivolt), mentre quando si riceve qualcosa i segnali ricevuti si sommano a questo offset. In pratica, quando trasmettiamo un'onda quadra a 3 kHz, abbiamo su questo pin un segnale composto da una componente continua di qualche centinaio di millivolt più un'onda quadra leggermente distorta di ampiezza pari anch'essa a qualche centinaio di millivolt.
La figura seguente è una ricostruzione di questo segnale, in questo caso l'offset vale 0.4 V e l'onda quadra ha ampiezza 0.3V:

(clicca sull'immagine per vederla più grande)

Com'è evidente, la soglia in questo caso dovrebbe essere impostata ad un valore maggiore di 0.4V e minore di 0.7V. In realtà, la presenza di rumore in alta frequenza è molto incisiva, perciò la soglia va posizionata più in "alto" rispetto al valore della tensione continua a riposo.
Incuriosito dall'origine di questo rumore (il dispositivo, vedremo, è alimentato dalla presa USB) ho impostato l'oscilloscopio in modalità AC (blocca la componente continua permettendomi di vedere solo le componenti a frequenza maggiore), il trigger in modalità LF reject (ignora il trigger alle basse frequenze), il canale MATH su FFT e posto una sonda sulla linea di alimentazione. Quello che ho scoperto è un rumore consistente alla frequenza di 48 MHz, un altro picco più basso a 96 MHz, ecc..., casualmente la frequenza di lavoro dell'USB 2.0 (e multipli). Ecco una ricostruzione (le frequenze componenti il disturbo NON sono in scala con i disturbi realmente osservati, ma le ampiezze sì) dell'onda disturbata:

(clicca sull'immagine per vederla più grande)

Provando a collegare il dispositivo ad un PC portatile (prima era collegato ad un PC fisso) questo rumore si riduceva drasticamente. Probabilmente la differenza risiede nel tipo di alimentazione: un PC fisso ha un alimentatore switching (un dispositivo elettricamente molto rumoroso, che dovrebbe essere ben schermato ma non lo è sempre effettivamente) vicino al controllore usb, mentre il portatile dispone di un alimentatore meno potente e più lontano.

Ho comunque effettuato la taratura della soglia nel primo caso, quello più rumoroso, in modo da garantire un corretto funzionamento anche nel caso il dispositivo venisse collegato ad un PC fisso.

Tornando alla decodifica, secondo quanto detto finora, trasmettendo un'onda quadra a 3 kHz ci aspettiamo di vedere uscire da questo pin un segnale come quello visto poco fa. Mandando questo segnale in un comparatore (un LM311 in questo caso) e comparandolo con una soglia impostata secondo il modo appena visto, otteniamo in uscita l'onda quadra di partenza.
Questa onda quadra, la cui presenza (ricordiamolo) significa un uno logico e la cui assenza significa uno zero logico, viene inviata direttamente al microcontrollore ricevente che controlla il valore più volte per ogni Tbit (10 ms), circa 130 volte ad intervalli regolari. Ogni volta che questo valore è alto, viene incrementato un contatore. Per ogni bit, finiti i 130 controlli, se il totale degli 1 supera una soglia minima allora il bit è considerato un 1, altrimenti uno 0. Questo valore minimo è circa 30, cioè se su 130 letture almeno 30 danno un "1", allora il bit è considerato alto.

Questo numero (il 30) deve essere ben calibrato, perchè se fosse troppo basso non scarterebbe le interferenze più pesanti, se fosse molto alto rischierebbe di scartare dei bit validi. Questo 30 è un valore ottenuto sperimentalmente attraverso varie prove e misure.

Tutto questo riguarda i bit del dato e i bit del codice di controllo, per quanto riguarda il bit di start il discorso è differente: quando s’inizia a ricevere un segnale, per verificare il bit di start si fanno ben 350 letture ad intervalli regolari a distanza di qualche microsecondo. Invece di contare il numero finale delle letture a "1” quando si sono letti un certo numero di zeri (120 nell'ultima versione del firmware) s’ignora il bit e lo si considera come un’interferenza, tornando immediatamente al ciclo principale.

Ho preso quest’accorgimento per evitare che ogni interferenza faccia perdere 10ms (Tbit) al ricevitore, durante i quali potrebbe per esempio arrivare un vero bit di start che andrebbe quindi perso.

Il segnale da decodificare (OOK), per semplificare il lavoro di lettura e decodifica al PIC, viene prima mandato in una semplice rete chiamata "rivelatore di inviluppo", una rete comunemente usata nella modulazione di ampiezza. Se cercate informazioni in rete su questa rete troverete molta documentazione e delle formule canoniche, in questo caso le formule non servono particolarmente e vi spiego come ho calcolato i valori dei componenti mostrandovi innanzitutto lo schema (semplicissimo):

Quando in ingresso si ha il livello alto dell'onda quadra, questo attraverso il diodo carica il condensatore in un tempo teoricamente pari a zero (nessuna resistenza di carica). Quando il livello in ingresso è basso, a causa della presenza del diodo il condensatore può scaricarsi solo attraverso la resistenza. Se i valori di R e C sono ben proporzionati rispetto alla frequenza del segnale da demodulare (considerando anche il duty cycle), si avrà in uscita una tensione continua (con un certo ripple, eliminabile con un filtro passa-basso in quanto il ripple è una componente in frequenza più alta) quando in ingresso è presente un'onda quadra, mentre quando in ingresso non abbiamo niente il condensatore si scarica completamente sulla sua resistenza portando l'uscita bassa.

Come dicevo, per calcolare i valori di R e C si può utilizzare la formula canonica reperibile in rete o ragionare di testa propria. Io ho scelto quest'ultima opzione e dimensionato i componenti in questo modo:

Consultando il datasheet del PIC che ho usato nel ricevitore, ho appreso che l'ingresso TTL considera "alto" un valore superiore ai 2 V, basso un segnale inferiore agli 0.8V e non è definito il comportamento nella fascia intermedia. Ciò significa che la costante di tempo R*C dovrà essere dimensionata in modo che nel tempo in cui l'onda quadra è a livello basso, la tensione sul condensatore non scenda sotto i 2V.

Basterebbe impostare una costante di tempo enorme, ma questo impedirebbe la scarica del condensatore e porterebbe il PIC ad interpretare tutti i bit a 0 come bit a 1, con conseguenze catastrofiche. Occorre dunque scegliere la costante di tempo più bassa possibile (per non incorrere in questo problema) che non permetta alla tensione sul condensatore di scendere sotto i due volt nel tempo di Off dell'onda in ingresso.

Procediamo: la frequenza del segnale OOK è 3 kHz, quindi il periodo vale T = 1/F = 333 uS. Il tempo di off sarà quindi 333/2 = 167 uS.
La formula che definisce la tensione ai capi di un condensatore è:

Che, isolando il termine esponenziale, diventa:

Sottoponendo a logaritmo naturale entrambi i membri, otteniamo:

Isoliamo l'incognita RC moltiplicando ambo i membri per RC e dividendo ambo i membri per il logaritmo naturale col suo contenuto:

Secondo quanto detto, la tensione ai capi del condensatore non deve scendere sotto i 2 volt in 167 uS, quindi troviamo la costante di tempo RC minima ponendo Vc=2 V e T=167 uS. Inoltre la scarica parte da una tensione di 5 V per terminare a 0 V, abbiamo quindi Vi=5V e Vf=0V. Sostituendo e risolvendo otteniamo:

Ed ecco finalmente il valore cercato:

Conciliando il risultato di questo calcolo con la mia disponibilità di componenti, ho utilizzato un condensatore da 19pF e una resistenza da 10MOhm ottenendo una costante di tempo RC = 190 uS.

Il valore è molto vicino a quello teorico ed il margine è ristretto, ma come già detto è così che deve essere per garantire una buona ricezione anche nei bit a livello basso (più la costante di tempo è bassa, prima si scaricherà il condensatore e prima il livello logico in uscita tornerà basso). Se anche il PIC dovesse leggere un valore basso invece di uno alto per qualche microsecondo, troverebbe comunque un software implementato in modo da garantire la giusta tolleranza.
Il valore del condensatore potrebbe sembrare molto piccolo, ma in questo modo riesco ad ottenere una carica in un tempo ridottissimo ottenendo una maggiore velocità di risposta ai fronti di salita del segnale OOK.

Grazie a questa rete, il PIC riesce a distinguere meglio tra bit bassi e bit alti perchè deve leggere sostanzialmente un livello logico piuttosto che determinare l'assenza o la presenza di una frequenza. La figura seguente mostra come il segnale (sempre quello di prima, per inviare il comando 5) venga trasformato da questa rete:

(clicca sull'immagine per vederla più grande)

Riassumendo il tutto, l'intero processo di decodifica è schematizzabile in questo modo:

  • Quando un comando viene trasmesso, il segnale rigenerato internamente al modulo ricevitore assume la forma mostrata in precedenza (un 1 logico perchè il bit di start è sempre un uno);
  • Questo segnale viene inviato nel comparatore esterno che, opportunamente tarato, dà in uscita l'onda quadra trasmessa originariamente risquadrata e con ampiezza 0-5 V;
  • Quest'onda quadra entra nel rivelatore di inviluppo, venendo trasformata in tensione approssimativamente continua;
  • Il tutto dura per 10 ms (un Tbit, il bit di start);
  • Il microcontrollore ricevente legge questo segnale e interpreta il bit come bit di start;
  • Il microcontrollore allora legge i 4 bit seguenti (quindi legge 4 volte per 10 ms) per ottenere il codice del comando;
  • Il microcontrollore legge i 4 bit rimanenti (ancora 4 volte per 10 ms) per ottenere il codice di controllo;
  • A questo punto, il microcontrollore compara il codice del comando col codice di controllo negato; se i due corrispondono, il comando è valido e viene inviato via USB al computer.

Il tutto sarà più chiaro vedendo nel dettaglio la struttura nel ricevitore presentata nel prossimo capitolo.


Una volta costruito il trasmettitore, occorre costruire il ricevitore che decodifichi il segnale radio e inoltri il comando al PC. Per prima cosa, dovendosi collegare al PC, bisogna scegliere quale porta usare per connettere il dispositivo:

  • Scartiamo subito la parallela LPT a 25 pin perchè neanche i computer più vecchi in circolazione la hanno ancora;
  • La più diffusa per questo genere di cose è la porta seriale a 9 pin, ma è vecchia anche lei, e a parte il fatto che i computer non la hanno più e occorrerebbe un (costoso) convertitore USB-seriale, non mi piace fare le cose obsolete sul nascere;
  • La porta USB sembra perfetta! E' molto veloce, presente ovunque, fornisce anche alimentazione eliminando la necessità di trasformatori o batterie. L'unico problema è la difficoltà di utilizzo, ma basta documentarsi un po' e fare un po' di pratica senza scoraggiarsi.

Il ricevitore è inserito in una piccola scatola di plastica nera reperita su ebay al cui interno sono alloggiati:

  • Un PIC18F2550, microcontrollore a 28 pin dotato di modulo USB, il "cervello" dell'intero circuito;
  • Radio ricevitore Qam-rx2 operante a 433.92 MHz (notare che anche in questo caso la sigla Qam è la sigla del produttore Quasar seguita dal tipo di modulazione AM, non indica il tipo di modulazione di ampiezza/fase QAM);
  • Un comparatore rapido LM311 utilizzato nel modo descritto in precedenza per la ricezione del segnale radio assieme al rivelatore a diodo.

Schema elettrico

Prima di spiegarvi uno per uno i componenti di questo circuito ritengo opportuno fornirvi lo schema elettrico completo:

(clicca sull'immagine per vederla più grande)

(Errata corrige: non posso modificare subito lo schema, per cui correggo testualmente: al rivelatore d'inviluppo non va il pin 7 del 311 ma il pin 6, il pin 7 va in realtà a massa).

Innanzitutto, si nota la presenza del connettore USB in basso, i cui pin di dati D+ e D- sono connessi al PIC e da cui preleviamo anche l'alimentazione (ho predisposto un jumper per poter eventualmente utilizzare un alimentatore esterno).

Sul PIC non c'è molto da dire, il clock viene fornito da un quarzo da 12 MHz (il PLL 4x interno lo porta a 48 MHz, necessari per la porta USB) con i due condensatori da 22 pF annessi. Il condensatore sul pin denominato Vusb è indispensabile per il corretto funzionamento del modulo USB, anche se il valore non è critico.

In alto a destra è presente il ricevitore radio, con i due pin di uscita TEST e OUT. TEST è il pin di output digitale (uscita del comparatore) di cui abbiamo già parlato, mentre OUT è il pin di uscita del segnale rigenerato. Da questo pin (ignorate il jumper, era lì per dei test che ho condotto sul modulo) preleviamo il segnale che mandiamo all'ingresso del comparatore LM311 (la resistenza R6 è stata inserita perchè ho sperimentalmente osservato che veniva utile).
Il 311 compara questo segnale con una soglia fissa impostabile sul trimmer da 10kOhm. Sullo stesso pin è presente un condensatore per stabilizzare questo valore di tensione, ciò è necessario perchè senza quel condensatore la tensione sul pin invertente fa dei brutti scherzi (picchi vari a seconda del segnale in ingresso).

Vi rimando al capitolo precedente per una spiegazione approfondita del processo di demodulazione e decodifica.

La resistenza R3 in uscita è necessaria come pull-up perchè l'uscita del 311 è open collector. All'uscita del comparatore è presente il rivelatore di inviluppo di cui abbiamo già parlato, dopo cui troviamo finalmente l'ingresso nel PIC.

Un ultimo dettaglio da notare è la presenza di due led bicolore rosso-verde a tre terminali denominati STATUS e DATA led. Il primo è rosso appena il dispositivo è acceso e diventa verde quando è collegato al software sul PC. Il secondo viene gestito durante la ricezione di dati per poter verificare se il PIC sta ricevendo i comandi correttamente o sta ricevendo interferenze.


Codice Sorgente

Il codice sorgente è scritto in C (ambiente mikroC), il che semplifica notevolmente la scrittura del programma soprattutto data la presenza delle librerie per l'impostazione della porta USB e di esempi a riguardo.
Inoltre, pur rimanendo indiscussa la superiorità dell'assembly, i PIC della serie 18F sono ottimizzati per eseguire codice compilato dal C.

Per permettere il corretto funzionamento del programma, i fuses vanno impostati nel modo seguente:

(clicca sull'immagine per vederla più grande)

E tutti i vari code-protect vanno disabilitati (o abilitati, come preferite, è indifferente).

Il codice non è eccessivamente semplice, è costruito utilizzando il modello proposto negli esempi delle librerie di mikroC aggiungendovi (molto) codice personalizzato e alcune funzioni utili.

Innanzitutto, la comunicazione USB avviene scambiandosi pacchetti di n byte, dove n è un numero deciso al momento della compilazione. Io ho optato per il numero minimo che è 64, tanto i byte da inviare sono pochi. Ciò significa che ogni volta che il PC o il PIC riceveranno dei dati, avranno ricevuto un pacchetto di 64 byte.

Per fare sì che il ricevitore e il programma per PC sapessero se quello che gli arrivava era un messaggio testuale, un codice ricevuto o un comando di sistema (tipo "disconnetti") ho optato per un sistema in cui il primo byte di ogni pacchetto inviato definisce il tipo di dato ricevuto. Questa tabella fa chiarezza su questo sistema:

(clicca sull'immagine per vederla più grande)

Dalla tabella si capisce che quando il PIC vuole inviare un messaggio testuale al PC, imposta il primo byte a 12 e i seguenti con i caratteri del messaggio. Quando il PC riceverà il pacchetto, leggerà il primo byte e capirà che quello che segue è un messaggio.

Per comunicare al PC di aver ricevuto un comando, il primo byte va impostato a 15 e i seguenti tre col codice del comando. Perchè tre byte con lo stesso codice? Perchè, nel caso il dispositivo venisse posizionato lontano dal PC per avere una migliore ricezione del segnale (ad esempio con una prolunga USB da 5 metri), la distanza elevata potrebbe fare sì che un byte venisse letto male perchè degenerato durante la trasmissione. In questo modo, se il PC legge tre byte uguali non c'è problema. Se legge due byte uguali e uno diverso, applica il codice di correzione d'errore a maggioranza, interpretando il comando corretto come quello presente due volte. Nel caso leggesse tre byte uguali, purtroppo non ci sarebbe nulla da fare, ma questo è praticamente impossibile.

I tre codici rimanenti sono utilizzati come "comandi di sistema" soprattutto per la procedura di autenticazione. Questa procedura è necessaria alla connessione del dispositivo. Se questa procedura non viene effettuata, i comandi ricevuti vengono ignorati. Potrebbe sembrare una complicazione inutile, ma è necessaria per evitare che il dispositivo, danneggiandosi per qualche motivo, invii codici errati al PC. Richiedendo l'invio di codici ben determinati in una sequenza ben determinata, si può essere certi del corretto funzionamento sia del PIC che del PC.

Questa procedura è comunque semplice, quando il dispositivo viene attaccato al PC si preme il tasto "connetti" sul programma lato PC. Premendo questo tasto, il PC invia il codice 11-12 per richiedere la connessione. Se il PIC riceve ed è ben funzionante, risponde con il codice 10 così che entrambi i dispositivi validino la connessione. Il comando 25-28 può essere inviato dal PC al PIC in qualsiasi momento premendo il tasto "disconnetti", ma viene anche inviato automaticamente all'accensione del programma sul PC. Questo perchè nel caso il programma venisse spento e riacceso senza che il dispositivo venisse scollegato, quest'ultimo conterebbe ancora la connessione come valida ma per il PC non sarebbe lo stesso. Inviando il codice di disconnessione all'avvio, si è certi che il dispositivo sarà sempre in uno stato noto.

Tornando al codice sorgente per il PIC, all'inizio vi è la solita impostazione delle porte e dei registri interni. Poi vi è l'inizializzazione del modulo usb, chè è una routine bloccante (il codice non prosegue finchè il PC non riconosce con successo il dispositivo). A questo punto viene gestita la procedura di autenticazione, attendendo il codice 11-12 e inviando il codice 10 una volta che si è ricevuto.

Terminata la procedura di autenticazione, vi è il ciclo principale in cui si controlla in polling lo stato del pin di input (B0) e, nel caso sia alto, si inizia a leggere e decodificare il segnale secondo le modalità descritte in precedenza. Terminata la lettura del segnale, il codice ricevuto viene inviato al PC nel modo che abbiamo appena visto.

All'interno di questo ciclo principale viene anche controllata l'eventuale ricezione di un comando di disconnessione. Nel caso venga ricevuto, si torna con un goto (non me ne vogliano i buoni conoscitori del linguaggio C, non piace neanche a me ma in questo caso ci voleva...) all'inizio della procedura di autenticazione.

Per quanto riguarda il ricevitore è tutto. Qua di seguito trovate i link per scaricare il codice sorgente e il file hex già compilato. Potrei aver dimenticato qualcosa, nel caso potete mandarmi una email per richiedere eventuali altre spiegazioni.


Termino questo capitolo con alcune foto del dispositivo pronto:

Vista del circuito interno:

(clicca sulle immagini per vederle più grandi)

Vista esterna e dispositivo chiuso:

(clicca sulle immagini per vederle più grandi)


L'ultima parte del progetto che rimane è il software scritto per il PC. Come già detto, questo software deve ricevere dati via USB dal PIC e riprodurre i file collegati al comando ricevuto.

Se programmare un PIC per la connessione USB non è stato facile ed ha richiesto svariati tentativi, scrivere un software che gestisca autonomamente una porta USB per un PC è, al livello delle mie conoscenze attuali, pura follia. Per fortuna su questa pagina ho trovato una libreria perfetta e facile da usare scritta per Visual Basic .NET, che vi consiglio nel caso dobbiate sviluppare qualcosa che utilizzi la porta USB.

A questo punto il linguaggio da utilizzare è diventato obbligatoriamente il Visual Basic .NET che, sebbene non sia il mio preferito, è comunque facile da usare e sufficientemente prestante per questo progetto.

Una volta installato il template (vi consiglio di farlo per capire bene questo codice sorgente) ho modificato secondo le mie esigenze le varie routine, inserendo una gestione della routine di autenticazione, un sistema di ricezione e parsing dei comandi (secondo la tabella vista in precedenza) ed un sistema di verifica dei suoni da riprodurre.

Un breve approfondimento su quest'ultimo aspetto: i comandi ricevibili sono numerati dall'1 al 10. Questo programma richiede che i file utilizzati siano in formato WAV (per essere riprodotti da windows senza l'utilizzo di librerie esterne) e abbiano come nome il codice del comando a cui sono associati. Avremo quindi i file 1.wav, 2.wav, 3.wav... ecc fino a 10.wav. Questi file sono posizionati in una cartella che è definibile dall'utente (di default è una cartella chiamata "suoni" che viene creata sul desktop).

Ho implementato ancora altre routine secondarie per ottimizzare il funzionamento del programma, come la routine di controllo della presenza dei file wav, la routine di controllo dell'alimentazione (per evitare che si scarichi la batteria in mezzo ad uno spettacolo, ad esempio), alcune routine di abbellimento grafico.

Non scendo eccessivamente nel dettaglio per quanto riguarda questo software, chi necessitasse di ulteriori informazioni può scrivermi una mail e sarò ben felice di fornire le spiegazioni desiderate. Penso che commentare qua riga per riga il sorgente sia inutile e pesante.

Detto questo, vi lascio il link per il download del progetto in VB .NET e alcuni screenshot del funzionamento del programma:

Codice Sorgente


Screenshots

Le immagini sono a bassissima qualità per velocizzare la vostra navigazione.

Dispositivo non ancora collegato al computer:

(clicca sull'immagine per vederla più grande)

Dispositivo collegato al computer ma non ancora connesso al programma:

(clicca sull'immagine per vederla più grande)

Dispositivo connesso al programma:

(clicca sull'immagine per vederla più grande)

Cambio della cartella dei file:

(clicca sull'immagine per vederla più grande)

Il programma si preoccupa della mancanza di file audio:

(clicca sull'immagine per vederla più grande)

Comandi ricevuti ma file mancanti:

(clicca sull'immagine per vederla più grande)

Comandi ricevuti e file presenti:

(clicca sull'immagine per vederla più grande)


Con la connessione dell'uscita cuffie del PC all'impianto diffusore il progetto è concluso.

Ho terminato l'esposizione di questo progetto, spero che vi sia piaciuto e/o che lo abbiate trovato utile.

Nel caso abbia dimenticato qualche dettaglio (o anche qualcosa di più importante di un dettaglio) fatemelo sapere che provvederò il prima possibile. L'articolo non è corto ed è possibile che mi sia sfuggito qualcosa.

Allo stesso modo attendo eventuali critiche e/o consigli che mi sarebbero molto utili per migliorare i miei progetti futuri.

Buon proseguimento a tutti.