LEZIONE 4

Obiettivo della lezione è capire come costruire un sistema in grado di collezionare dati provenienti dal mondo fisico ed elaborarli in tempo reale per produrre segnale audio reattivo; il tutto mantenendo le dimensioni del sistema il più contenute possibile e senza utilizzare un computer (o per lo meno non un computer inteso nel senso tradizionale del termine).

Proveremo quindi ad interfacciare una scheda Arduino UNO ed un Raspberry Pi rev3 mod B.

Il metodo di interfacciamento tra le schede che vedremo oggi si basa sulla comunicazione seriale tramite protocollo USB. Su Arduino è in esecuzione uno sketch che si occupa di collezionare i dati dai diversi sensori; allo stesso tempo riceve segnali di via libera e stop per la gestione della comunicazione seriale, inviati da un secondo software. Quest’ultimo, in esecuzione su RaspberryPi, riceve ed elabora i dati in arrivo da Arduino, una volta che la comunicazione è stabilita.

Questo secondo software è PureData, ideale per l’elaborazione del segnale audio, funziona in ambiente Raspbian, e può comunicare con Arduino via seriale.

fase 1 - installazione dell’IDE e dei driver per la scheda Arduino

Prima di tutto occorre installare l’IDE Arduino sul proprio RaspberryPi in modo da poter riconoscere e programmare la scheda direttamente. Per farlo apriremo una nuova istanza del terminale e useremo il tool aptitude con il seguente comando:

sudo apt-get install arduino

L’applicativo IDE comparirà nella voce Electronics del menù principale.

fase 2 - installazione di PureData (e dell’oggetto [comport]) su RaspberryPi

A seguire occorre installare PureData su RaspberryPi: aptitute ci torna nuovamente in aiuto:

sudo apt-get install puredata

Basta seguire le eventuali istruzioni a schermo per installare tutti i pacchetti e le dipendenze usate da PureData.

Affinchè PureData sia in grado di scambiare messaggi con Arduino occorre equipaggiarlo con un oggetto denominato [comport]. Nuovamente da terminale si usa il comando:

sudo apt-get install pd-comport

fase 3 - primi test

A questo punto si può pensare di realizzare un semplice circuito e scrivere un corrispondente sketch in Arduino, in modo da registrare informazioni dal mondo fisico e inviarle a PureData.

Ecco il circuito che utilizzeremo:

serial

Lo sketch dovrà gestire:

  • i valori analogici associati al potenziometro;
  • i valori digitali del pushbutton;

e allo stesso tempo accendere o spegnere un LED ascoltando i messaggi in arrivo da PureData.

Lo sketch di Arduino è quello mostrato qui di seguito e scaricabile dal repository del corso:

#define LED 13
#define BUTTON 7
#define POT A0

/* addresses */
#define POT_ADDR 0
#define BUTT_ADDR 1

boolean bSendSerial = false;
boolean bLedStatus = false;
byte lastAddress = 0;

// SETUP ////////////////////////
void setup()
{
  Serial.begin( 9600, SERIAL_8N1 );
  pinMode(LED, OUTPUT);
  pinMode(BUTTON, INPUT);
  digitalWrite(LED, bLedStatus);
}

// LOOP /////////////////////////
void loop()
{  
  int value = -1;

  value = analogRead(POT);
  if(bSendSerial)
  {
    Serial.write(128 + POT_ADDR);
    Serial.write(value>>3); //divisione per 8
  }

  value = digitalRead(BUTTON);
  if(bSendSerial)
  {
    Serial.write(128 + BUTT_ADDR);
    Serial.write(value);
  }

  digitalWrite(LED, bLedStatus);

  delay(100);
}


// SERIAL EVENT /////////////////
void serialEvent()
{
  byte b = Serial.read();

  if(b>127)
    lastAddress = b;
  else
  {
    switch(lastAddress)
    {
      case 255:
        if (b == 1)
          bSendSerial = true;
        else
          bSendSerial = false;
        break;
      case 240:
        if (b == 1)
          bLedStatus = true;
        else
          bLedStatus = false;
        break;
      default:
        // do nothing
        break;
   }
 }   
}

Predisposte le costanti POT, LED e BUTTON per memorizzare i pin delle diverse componenti e la variabile booleana di stato bLedStatus, settiamo le modalità dei pin digitali LED e BUTTON rispettivamente come OUTPUT e INPUT nel blocco setup.

Nel blocco loop ci occupiamo della lettura dei valori elettrici dalle diverse componenti facendo uso delle funzioni analogRead per il potenziometro e digitalRead per il pushbutton. Scriviamo poi lo stato del LED con la funzione digitalWrite e per finire aggiungiamo un piccolo ritardo tra un loop e il successivo con delay.


A questo punto occorre riflettere su “Come comunicare i dati a PureData?” Per questo ritorniamo nel blocco setup ed impostiamo la comunicazione seriale con la funzione Serial.begin(...).

Proseguendo in questa direzione ci serve ora inviare i dati attraverso il cavo USB, un po’ come fatto le scorse lezioni con le funzioni Serial.print o Serial.println. I valori collezionati dalle diverse componenti connesse ad Arduino sono valori numerici. Come comunicarli via seriale? Faremo uso della funzione Serial.write!

Protocollo di comunicazione

L’unità base per la comunicazione seriale è il byte. Sappiamo che con un byte possiamo rappresentare valori numerici compresi tra 0 e 255.

Consideriamo ora due questioni:

  • In situazioni dove si devono ottenere valori numerici da una moltitudine di sensori, come fare a discriminare quale dato corrisponde a quale sensore? Si potrebbe pensare a 2 diverse classi di byte inviati tramite seriale: la prima ad indicare un indirizzo univoco associato al sensore in uso e la seconda per rappresentare il dato vero e proprio.
  • taluni sensori ritornano valori numeri compresi tra 0 e 1023, quantità non rappresentabili con un singolo byte.

Problema 1

Per ovviare al primo problema stabiliamo che tutti gli indirizzi avranno valore superiore a 127. Usando il byte come contenitore sarà sempre possibile indirizzare fino a 128 dispositivi differenti (più che sufficiente). Tutti i valori numerici che viaggiano lungo il cavo USB che invece hanno un valore compreso tra 0 e 127 identificano il dato vero e proprio.

Con Arduino che comunica sempre indirizzo e dato sotto forma di 2 byte consecutivi, sarà semplice per PureData, in ascolto sul canale seriale, associare ogni dato al sensore corrispondente e quindi alle logiche software relative.

Problema 2

Per far fronte al secondo problema invece è sufficiente scalare il valore del dato grezzo per un fattore opportuno tale da costringerlo in un range più limitato:

1024 / 8 = 128

Tornando al blocco loop è bene controllare il traffico di dati tra Arduino e PureData allo scopo di evitare di intasare i buffer o di perdere informazioni importanti. Per questo si è pensato alla variabile booleana bSendSerial come fosse una sorta di semaforo per abilitare e disabilitare l’invio di dati da parte di Arduino.

Resta da capire chi e come imposta tale variabile: l’idea è che sia PureData, una volta avviato, ad informare Arduino e richiedere l’invio dei dati.

Serve quindi implementare la funzione serialEvent. Tale funzione fornita dalla libreria Serial di Arduino è una funzione di callback e viene invocata automaticamente dal sistema quando ci sono nuovi bytes in attesa d’essere processati sul buffer seriale della scheda.

In altri termini ogni volta che PureData invia un byte ad Arduino, Arduino a sua volta esegue il blocco di codice della funzione serialEvent.

Utilizziamo lo stesso tipo di protocollo in entrambi le direzioni della comunicazione. Per questo stabiliamo che PureData usa l’indirizzo 255 per informare Arduino d’essere pronto a ricevere i dati.Gli argomenti 1 o 0 del messaggio settano la variabile bSendSerial in modo corrispondente.

pd and arduino

Si potrebbe ora stabilire un altro indirizzo, ad esempio il 240, con cui PureData possa istruire Arduino riguardo lo stato del LED (gli argomenti sono 1 - acceso e 0 - spento).


Compilato lo sketch e caricato sulla scheda abbiamo ultimato quanto necessario sul versante Arduino. E tempo di passare a…

Pure Data

Perchè PureData sia in grado di sfruttare le porte seriali occorre aggiungere alla patch l’oggetto [comport] e costruire la logica necessaria per stabilire la connessione e provvedere all’invio e alla ricezione dei messaggi.

pd serial

Una volta ricevuti, i messaggi vanno esaminati e separati in base al valore numerico in essi contenuto così da sfruttarne correttamente il significato.

pd moses

A questo punto anche la patch di PureData è pronta per essere usata assieme ad Arduino.

Miglioramenti

No GUI

Si nota che l’interfaccia grafica di PureData richiede un certo quantitativo di lavoror da parte della CPU. Perchè allora non provare a far funzionare la patch senza GUI?

Fortunatamente PureData permette tutto questo come si può facilmente constatare dalla lista degli argomenti che possono essere usati per avviare l’applicazione da terminale:

puredata --help

Tra i comandi più interessanti di sicuro abbiamo nogui per evitare il rendering dell’interfaccia e stderr per mostrare eventuali messaggi d’errore direttamente a console. Come ultimo argomento si passa il nome della patch da eseguire.

Ecco che allora il comando potrebbe essere il seguente:

puredata -nogui -stderr test.pd

pdsend

Resta da capire come poter agire sugli elementi di interfaccia quando questa non venga visualizzata!

Per fortuna l’ambiente PureData mette a disposizione l’oggetto pdsend (e il duale pdreceive) per comunicare con le patch di PureData usando il particolare protocollo FUDI tramite TCP/UDP.

Basta quindi aggiungere alla patch un oggetto [netreceive], impostarlo per ascoltare su di una particolare porta (la stessa usata come argomento per invocare il tool pdsend) e cablarlo affinchè i diversi messaggi FUDI vadano a triggerare gli opportuni comportamenti dell’interfaccia.

pd netreceive

A tale punto basta quindi rilanciare la patch con il comando visto poco fa e poi, da una seconda istanza di terminale (oppure da una seconda finestra usando il tool tmux), usare il comando:

pdsend <numerodellaporta>

A seguire si possono inviare i comandi di interfaccia precedentemente convenuti in una sequenza di istruzioni come quella mostrate qui di seguito:

dev;
port 0;
connect 1;
polling 1;
dsp 1;
led 1;
led 0;

e così via.


Abbiamo parlato velocemente dei bitwise operators che ci permettono di svolgere con maggiore efficienza operazioni a basso livello sui singoli bit.

Abbiamo anche parlato del software git, uno dei sistemi di controllo del versionamento più usati. Git è il tool da cui prende il nome il sito GitHub, server di hosting per repository di codice.

Approfondimenti

  • multiplexer: per leggere dati da più sensori analogici o digitali senza per questo impegnare troppi pin digitali o analogici della scheda Arduino. Ecco un tutorial tratto dal sito del rivenditore SparkFun. Come questo ce ne sono moltissimi altri associati ad altrettante tipologie di componenti!
  • tmux (terminal multiplexer): multiplexer dei processi da terminale;

Tra le altre cose…

… abbiamo anche parlato di:

  • di Daniel Shiffman un simpaticissimo insegnante dell ITP di New York. Shiffman è il portabandiera del progetto Processing . Il suo canale Youtube è una grandissima risorsa per imparare questo linguaggio e i fondamenti della scrittura di codice in generale. Sul canale anche il corso su p5.js che trasla le funzionalità di Processing sul web tramite l’uso di JavaScript. Non si possono non citare le sue fatiche editoriali: “Learning Processing” e il bellissimo “Nature of Code”.