Tag

, , , , ,

Processing è un linguaggio di programmazione molto potente, disponibile per PC, Mac e Linux. La sua IDE è molto simile a quella di Arduino, e il motivo è semplice: l’IDE di Arduino è una versione semplificata di quella di Processing.

Le applicazioni scritte con Processing possono girare stand-alone sia su PC che su Mac, o su Linux, senza dover cambiare una singola riga di codice – e questo è fantastico. Inoltre Arduino è in grado di caricare direttamente sketch creati con Processing: la sola differenza è nell’estensione, che in questo caso non è .ino, ma .pde

Processing Logo

Processing Logo

Processing è un linguaggio basato su Java, ed è semplice ma estremamente evoluto, e consente di creare con relativa facilità programmi complessi, che vanno dalla gestione di video, pdf, immagini, alla grafica 2D e 3D, ecc.

Esempio Processing

Esempio Processing

In genere, pertanto, Processing viene sfruttato per creare interfacce grafiche sofisticate, con le quali interagire con Arduino. Potremmo creare una vera “plancia di controllo” per pilotare Arduino, oppure costruire un oscilloscopio software – che è esattamente ciò che andremo a realizzare con questo studio.

Cominciamo allora a creare il nostro oscilloscopio con Processing!

Requisiti

Tutto quello che ci serve per il nostro esperimento è un Arduino, l’IDE di Arduino, e l’IDE di Processing. Per scaricare e installare Processing, basta aprire questa pagina e scegliere la versione per il proprio sistema operativo.

Funzionamento

Per creare un oscilloscopio virtuale, il procedimento è piuttosto semplice: su Arduino, andiamo a caricare uno sketch che si limita a inviare un segnale sul pin 0 – il pin RX della scheda, quello adibito alla trasmissione dati (per intenderci, è il pin da cui “riceviamo” le informazioni del monitor seriale).

Una volta impostato Arduino per inviare informazioni, andremo a scrivere il codice per Processing, che dovrà ricevere i dati e mostrarli graficamente in tempo reale.

Arduino

Come abbiamo detto, il codice dello sketch da caricare su Arduino è semplice: si tratta, infatti, di poche righe:

// Definiamo il pin 0 (RX) che trasmette dati
#define ANALOG_IN 0

// Setup
void setup() 
{
  // Impostiamo la comunicazione seriale
  Serial.begin(9600); 
}

// Loop
void loop() 
{
  // Leggiamo il valore del pin 0 (RX)
  int val = analogRead(ANALOG_IN);
  // Stampiamo un valore di 255: 8 bits impostati a 1 (11111111) 
  Serial.write( 0xff );
  // Stampiamo il valore AND dopo aver spostato a destra 8 bits
  Serial.write( (val >> 8) & 0xff );
  // Stampiamo il valore AND tra il massimo e il valore letto
  Serial.write( val & 0xff );
}

La spiegazione dei comandi inviati nella funzione loop() è un po’ complessa da spiegare ma proviamo a farlo nella maniera più semplice: fondamentalmente, si tratta di operazioni bit a bit, dette bitwise. Perché si utilizzano? Semplice: perché è il modo più rapido di fare divisioni e moltiplicazioni su un elaboratore. Come funziona? Prendiamo il numero 6. In formato binario, si scrive: 110. Se spostiamo i suoi bits di verso destra otteniamo 011 – ossia 3. Non abbiamo fatto altro che calcolare, con un dispendio di risorse minino: 6/2! e se invece spostiamo tutto verso sinistra otteniamo 1100, ossia 12, cioé 6×2! Questo è il motivo grazie al quale l’uomo è riuscito ad arrivare sulla luna con la potenza di calcolo di un Commodore 64! 🙂

Nell’esempio, viene preso il valore massimo che si può scrivere su un pin digitale, ossia 255 (che è scritto in formato esadecimale: 0xff); quindi viene preso il valore del pin 0, diviso e poi “confrontato” con 255 (la funzione AND, scritta &, si utilizza per capire quali bit sono impostati a 1). In pratica, viene simulato l’invio di un’onda quadra, che verrà in seguito ricevuta da Processing e rappresentata graficamente.

Per approfondire: http://en.wikipedia.org/wiki/Bitwise_operation
Vedi anche: http://en.wikipedia.org/wiki/Bitwise_operations_in_C

Processing

Una volta scaricato e installato Processing, cominciamo con il creare un nuovo file; quindi aggiungiamo la libreria per la comunicazione seriale (che l’IDE di Arduino, invece, include di default) e definiamo un paio di variabili che ci serviranno per leggere e salvare i dati ricevuti da Arduino.

// Importiamo la libreria seriale (già inclusa nell'IDE Arduino)
import processing.serial.*;
// Creiamo l'oggetto porta seriale
Serial port; 
// Dato ricevuto dalla porta seriale
int val; 
// Array per contenere i dati
int[] values;

Una volta impostato il nostro programma, andiamo ad implementare le funzioni di base necessarie ad ogni programma Processing, che sono setup() e draw() – l’equivalente di loop(). Nel setup() di solito si inizializzano le variabili (come in Arduino) ma in aggiunta si impostano le dimensioni di quella che sarà la finestra dell’applicazione:

void setup() 
{
  // Impostiamo le dimensioni della finestra 
  size(640, 480);
  // Attiviamo una comunicazione seriale, alla velocità di 9600 bps
  // NB: è importante verificare su quale porta si utilizza Arduino!
  port = new Serial(this, Serial.list()[0], 9600);
  // Inizializziamo l'array di interi che conterrà i dati
  values = new int[width];
  // Questo comando prepara l'anti-aliasing per la grafica
  smooth();
}

La sintassi è leggermente diversa da quella di Arduino, perché Processing è un linguaggio orientato agli oggetti: questo significa che ogni elemento, prima di poter essere usato, deve prima essere creato e successivamente inizializzato. Perciò, dopo aver definito l’array di int[], dobbiamo inizializzarlo – ossia dobbiamo “prepararlo” – così il programma può conoscere le sue caratteristiche e lo spazio in memoria di cui ha bisogno.

Dopo aver inizializzato le nostre variabil nel setup(), andiamo a implementare il metodo draw(), che dovrà disegnare e aggiornare la grafica dell’applicazione:

void draw()
{
  // Se vengono trasmessi almeno 4 bytes sulla seriale...
  while (port.available() >= 3) 
  {
    // ... e se leggo il valore massimo
    if (port.read() == 0xff) 
    {
      // ... salvo la differenza con il valore seriale
      val = (port.read() << 8) | (port.read());
    }
  }

  // Per ogni elemento dell'array...
  for (int i=0; i<width-1; i++)
  {
    // ... sposto i valori salvati di una posizione
    values[i] = values[i+1];
  }
  // ... e aggiungo il valore appena letto
  values[width-1] = val;
  // Imposto il colore di sfondo a nero
  background(0);
  // Imposto il colore del disegno a bianco
  stroke(255);
  // Per ogni elemento dell'array...
  for (int x=1; x<width; x++) 
  {
    // ... disegno una linea:
    // dalle coordinate del punto precedente...
    line(width-x, height-1-getY(values[x-1]), 
    // ... alle coordinate del punto attuale
    width-x-1, height-1-getY(values[x]));
  }
}

Una funzione utilizzata che non è ancora stata descritta è getY(x), che dà come risultato l’altezza del grafico nel punto indicato:

int getY(int val) {
  // Restituisce l'altezza, in base al valore passato
  return (int)(val / 1023.0f * height) - 1;
}

Il codice può sembrare un po’ oscuro, ma in realtà non è molto complesso. Si tratta in sostanza di leggere il valore inviato dalla seriale, confrontarlo con il valore massimo che può avere un pin digitale, e salvarlo; quindi fare posto per il valore successivo. L’altezza del punto è adattata entro un limite di altezza massimo.

Verifica

Una volta che tutto è pronto vediamo se funziona: per testare l’applicazione è sufficiente collegare Arduino al computer tramite cavo USB, caricare lo sketch ed aprire Processing. Se tutto è stato fatto bene, vedrete un grafico muoversi in tempo reale. La curva del grafico indica la tensione che sta passando attraverso il pin di Arduino. Se l’applicazione mostra una finestra nera, provate ad aprire il monitor seriale, oppure controllate le note in fondo all’articolo.

Grafico Oscilloscopio

Grafico Oscilloscopio

Naturalmente esistono molti altri modi, più complessi e sofisticati, per fare interagire Arduino e Processing. È possibile persino lavorare esclusivamente in Processing e creare un vero e proprio “oggetto” Arduino. Ma per il momento, questo è tutto!

Download: oscilloscopio.zip

Update: se volete conoscere meglio Processing, seguite questo nuovo studio!

NB: il codice è basato sull’esempio di Poorman’s oscilloscope. Mi sono permesso di aggiungere, nel file di Processing, un paio di righe, commentate in italiano: se il programma non mostra il grafico, controllate nel log quale porta seriale state utilizzando, e nel caso cambiate il numero 0 alla riga:

port = new Serial(this, Serial.list()[0], 9600);

Annunci