Misuratore analogico del traffico di rete (o di qualunque altra cosa vi passi per la mente).
Il dispositivo è composto da due parti: un software che raccoglie ed elabora i dati da visualizzare e uno strumento “ad ago” realizzato con una board a microcontroller e un servocomando che indica, istante per istante, la misura effettuata.
La board Arduino viene utilizzata come terminale per il controllo del servocomando: riceve la posizione desiderata attraverso la porta seriale (USB) e muove il servocomando di conseguenza.
Il servocomando ha 3 terminali: 2 due di alimentazione (GND e 5V) e uno di controllo posizione. I servi standard possono effettuare una rotazione del loro asse da 0° a 180°. Il servo può essere mosso fornendo sul pin di controllo una serie di impulsi di durata variabile da 0.9ms a 2.1ms proporzionale alla posizione desiderata (0.9ms per 0° fino a 2.1ms per 180°). La distanza massima fra un impulso e il successivo deve essere compresa tra 10ms e 50ms, tipicamente si usa inviare un impulso ogni 20ms (50Hz).
Possiamo ottenere il segnale di controllo molto facilmente utilizzando la libreria Servo disponibile su arduino:
Con questa libreria non ci dobbiamo preoccupare dei dettagli implementativi in quanto sono già stati realizzati per noi. Per poter lavorare con la libreria bisogna scompattare il file servo.zip all'interno della cartella arduino-0011/hardware/libraries prima di procedere nella compilazione.
Lo stesso vale per la comunicazione seriale, in quanto è già incluso nelle funzioni di libreria l'oggetto Serial che si occupa di tutta la gestione a basso livello.
Il firmware da caricare sulla board è semplice e si commenta praticamente da solo. Quello che fa è sentire se ci sono dati in arrivo dalla porta seriale e in caso affermativo leggere un byte e usarlo come valore di “angolo” a cui posizionare il servo.
// Includi la libreria di controllo del servo nel firmare #include <Servo.h> // Dichiara un oggetto Servo Servo servo; // Inizializzazioni varie // ---------------------- // (vengono eseguite una sola volta all'accensione della board) void setup() { // Associa il pin 9 al segnale generato per il servo servo.attach(9); // Imposta la velocità della porta seriale a 9600 bps Serial.begin(9600); } // Ciclo di controllo principale // ----------------------------- // (funzione ripetuta all'infinito dopo l'inizializzazione) void loop() { // Se ci sono dati in arrivo dalla seriale... // (il numero di byte disponibili è > 0?) ) if (Serial.available() > 0) { // Leggi un byte dalla porta seriale e int i; i = Serial.read(); // muovi il servo nella posizione indicata a servo.write(i); } // Questa funzione è di servizio per il funzionamento della la // libreria Servo e va richiamata almeno una volta ogni 20ms ms // (noi la chiamiamo a ogni ciclo quindi molto più di frequente) te) Servo::refresh(
Lo schema dei collegamenti è, oserei dire, banale, il servocomando standard ha i seguenti contatti:
| Colore | Funzione | Pin su Arduino |
|---|---|---|
| Marrone o Nero | Massa | GND |
| Rosso | Alimentazione 5.0V-9.0V | +5V |
| Giallo | Impulsi di controllo | Pin 9 |
Abbiamo utilizzato il pin 9 perchè la libreria Servo può controllare al massimo due servocomandi contemporaneamente e questi devono essere collegati per forza al pin 9 e al pin 10 (in ogni caso questi due pin non potranno essere utilizzati per fare altro, anche disponendo di un solo servocomando).
Vediamo ora come pilotare il nostro hardware da PC.
Collegando la porta USB al PC, il driver installato nel sistema operativo creerà una porta seriale “virtuale” per comunicare con l'arduino. Se avete Linux, il driver è già fornito come modulo del kernel nella maggiorparte delle distribuzioni e la porta seriale creata si chiamerà /dev/ttyUSB0 (o /dev/ttyUSB1…9 se ne avete più di una).
Il software è scritto in python, ma chiaramente potete utilizzare un linguaggio qualunque che sia in grado di controllare la porta seriale.
In python è particolarmente semplice, basta utilizzare la libreria pyserial. Nel caso non fosse già installata python la può scaricare e installare automaticamente per noi con il comando:
sudo easy_install pyserial
quello che segue è un pezzo di programma che si occupa di far muovere il servo
# Importa la libreria per la porta seriale import serial # Crea un oggetto serial sul device /dev/ttyUSB0, velocità 9600 bps ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) # Funzione move(angolo) def move(ang): # Se l'angolo non è compreso tra 0 e 180 riportalo nei limiti i if ang>180: ang=180 if ang<0: ang=0 # Invia il valore attraverso la porta seriale come un singolo byte. . # (dobbiamo usare la funzione chr(...) per convertire ang in un singolo o # byte, altrimenti la write invierebbe una stringa con la codifica a # in ASCII del valore di ang) ) ser.write(chr(ang
Una volta dichiarata la funzione move(..) possiamo muovere il servocomando con un istruzione del tipo:
# Muove il servocomando alla posizione di 100° move(100
Adesso che abbiamo il controllo del servocomando, possiamo utilizzarlo per far visualizzare praticamente qualsiasi grandezza ci viene in mente, basta scrivere un programma che invii uno stream continuo del valore da visualizzare alla board.
Nel nostro caso abbiamo scritto un piccolo software che calcola l'utilizzo della scheda di rete calcolandolo sulla quantità di byte ricevuti. La funzione seguente legge le statistiche dell'interfaccia di rete prelevandole dal file /proc/net/dev e restituisce il numero di byte ricevuti (i dettagli di questa funzione e delle successive esulano dagli scopi dell'articolo ma in caso di dubbi vi consigliamo comunque di guardare la documentazione di python o uno dei migliaia di tutorial disponibili in rete):
# Importa la libreria per la gestione delle regular expression import re # Restituisce il numero di byte ricevuti dall'interfaccia di rete "interface" def read_traffic(interface): f = open("/proc/net/dev") for line in f: if line[6-len(interface):6]==interface: return re.split('[ :]+', line)[2] f.close()
Il programma principale contiene un ciclo che viene eseguito ogni 200ms, che effettua la lettura dei byte ricevuti dalla scheda di rete, calcola la differenza con la lettura precedente in modo da ottenere il numero di byte ricevuti fra un ciclo e il successivo. Una volta ottenuta la lettura questa viene portata in una scala logaritmica e convertita in gradi. La conversione in scala logaritmica ci permette di vedere anche i valori più bassi di traffico che altrimenti risulterebbero compressi sotto l'1% di tutta la scala.
| Byte ricevuti | % su byte | log(Byte ricevuti) | % su log |
|---|---|---|---|
| 0 | 0% | non esiste |
|
| 1 | < del 1% | 0 | 0.0% |
| 10 | < del 1% | 1 | 12.5% |
| 100 | < del 1% | 2 | 25.0% |
| 1K | < del 1% | 3 | 37.5% |
| 10K | < del 1% | 4 | 50.0% |
| 100K | < del 1% | 5 | 62.5% |
| 1M | 1% | 6 | 75.0% |
| 10M | 10% | 7 | 87.5% |
| 100M | 100% | 8 | 100.0% |
il risultato poi viene scalato in modo da avere un range da 0 a 180. Ecco il codice:
# Importa la libreria matematica e di gestione dei timings import math import time actual = read_traffic("eth0") # Effettua la prima lettura while True: time.sleep(0.200) # pausa di 200ms # Effettua una nuova lettura e salva la precedente old = actual actual = read_traffic("eth0") # Calcola il valore da visualizzare... res = math.log(int(actual)-int(old)+1) # ...lo converte in gradi... res = res * 180 / 9 # Invia il valore al servocomando move(int(res))