====== Il neTTarello ======
{{:netarello.png}}
===== Descrizione =====
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.
===== Materiale necessario per la realizzazione: =====
* 1 [[http://arduino.cc/|Arduino Board]]
* 1 servocomando RC (quelli utilizzati per il modellismo radiocomandato)
* 1 po' di fantasia per realizzare l'indicatore :-)
===== Funzionamento board e servocomando =====
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.
==== Controllo del servocomando ====
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).
{{:grafico_servocomando.png}}
==== Firmware ====
Possiamo ottenere il segnale di controllo molto facilmente utilizzando la libreria
Servo disponibile su arduino:
- Homepage libreria Servo: http://www.arduino.cc/playground/ComponentLib/Servo
- Download: http://www.arduino.cc/playground/uploads/ComponentLib/servo.zip
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
// 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
int i;
i = Serial.read();
// muovi il servo nella posizione indicata
servo.write(i);
}
// Questa funzione è di servizio per il funzionamento della
// libreria Servo e va richiamata almeno una volta ogni 20ms
// (noi la chiamiamo a ogni ciclo quindi molto più di frequente)
Servo::refresh();
}
==== Collegamenti e schemi elettrici ====
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).
===== Software di raccolta dati dal PC =====
Vediamo ora come pilotare il nostro hardware da PC.
==== Driver di comunicazione seriale ====
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
==== Software di movimentazione ====
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
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
# byte, altrimenti la write invierebbe una stringa con la codifica
# 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.
==== Software di raccolta dati ====
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))