gbm
Visualizzatore grafico con Processing - Grapher 1.0
Tutorials -
Sabato 14 Maggio 2011 19:35
Scritto da gbm

Ciao a tutti ragazzi. Con questa veloce guida vorrei sfruttare il lavoro e il tempo impegato su Grapher 1.0 per mostrarvi come si sviluppa un programma GUI, quali sono le basi della programmazione di interfacce, alcuni accenni alle proporzioni da seguire per il templating, ma soprattutto la comunicazione tra un Arduino (qualsiasi MCU o port basta che ci sia una Seriale hardware o la possibilià di lanciare softwareSerial) e il nostro computer.

 

 

 

Che cos'è Grapher

 

Grapher 1.0 è un' applicazione standalone eseguibile (scaricabile qui) che permette di visualizzare fino a 6 input analogici o digitali del vostro Arduino. Ho voluto creare questo strumento perchè cambia davvero l'esperienza e lo sviluppo di qualsiasi prototipo e sopperisce in parte alla quasi totale mancanza dell'uso dell'oscilloscopio in molti utilizzatori di Arduino. Quello che vi racconto piu' in basso, è l'effetto che ha avuto su di me il mio stesso strumento Surprised.

Ho inserito un jumper di 5/10cm in una porta analogica e ho iniziato a rendere grafici i dati ottenuti. Davvero interessante, ho scoperto girando per la casa che, avvicinando un pezzo di cavo di rame a una tazza di porcellana la tensione percepita dalla analogica sale in rapporto alla diminuzione della distanza, e sale molto di piu' al centro della tazza (???)!!! piu' invece, ci si avvicina a uno schermo lcd, con il cavo, piu' la sinusoide si schiaccia assumendo la forma di una retta (????)!! Toccando il cavo a epidermide scoperta appaiono oscillazioni 0 1023 ma cosa davvero inspiegabile, dando fuoco alla punta del cavo con un accendino la tensione percepita si innalza drasticamente, palesemente non in relazione alla temperatura, infatti, appena spenta la fiamma precipita al valore normale (nessun apparente effetto di inerzia termica).

Penserete che sono fuori hehehe, ma Grapher serve proprio a questo, innanzitutto cambiare punto di vista grazie ad un riscontro grafico e permettere di conoscere meglio gli input, la loro variazione, ma soprattutto la tensione e le sue oscillazioni. Questo programma, sarebbe di certo tornato utile per l'analisi dei dati del robot Self Balancing per principianti.

Conseguenza, penso passero' un bel po' di tempo su Grapher per sperimentare ma soprattutto per migliorarlo Tongue out.

Sarà rilasciata a breve una versione dedicata unicamente ai sensori E.R.E.R.

 

 

 

Come funziona

 

E' davvero interessante poter vedere cosi' comodamente, i soliti numerini a pioggia del Serial monitor.

In piu', grazie ad alcune funzionalità inserite, Grapher risulta effettivamente utile per analizzare lo stato della tensione di una qualsiasi fonte di alimentazione, l'analisi dei dati ottenuti da un sensore, il tracing del voltaggio di qualsiasi device, ma soprattutto la possibilità di comparare piu curve. Grazie alla comunicazione seriale via cavo usb (o wireless per i piu' fortunati), Arduino comunica con una semplice stringa, con suddivisori / 6 variabili diverse che contengono i dati delle 6 curve che vengono rese grafiche dal programma. C' è la possibilità di regolare il sampling rate, cioè il rateo con cui si prendono campioni dal flusso seriale entrante, ma anche l'offset della curva per poterla visualizzare piu' comodamente al centro, con un ghost axis sempre presente, che vi aiuta a capire dov'è lo 0. Per puro feticismo ho inserito le referenze in scala aggiornata, quante linee spezzate per secondo vengono disegnate e quante stringhe per secondo riceve la porta seriale del computer. Ho inserito i due pulsanti sulla destra, Start graph e Stop serial per permettervi di tenere aperto il programma, e per dire riprogrammare arduino senza il problema di avere la porta seriale occupata!

 

 

Cosa c'è dietro

Arriviamo al nodo fondamentale, di cosa è fatta questa applicazione??

Quali sono i sorgenti?? Come si posizionano le icone??

Tutte domande con risposte semplici e soluzioni ancora piu' semplici: Processing e l'Opensource software development. 

 

import processing.serial.*;
Serial serial;
//includes
//allegati
PFont font;
PImage b;
PImage start;
PImage stop;
PImage upReg;
PImage dnReg;
//Cartesian variables
//Variabili Cartesiane
float x = 0;
float y = 0;
float y1 = 0;
float y2 = 0;
float y3 = 0;
float y4 = 0;
float y5 = 0;
float yRef = 0;
float lastX = 0;
float lastY = 0;
float lastY1 = 0;
float lastY2 = 0;
float lastY3 = 0;
float lastY4 = 0;
float lastY5 = 0;
float lastYRef = 0;

//Time variables
//Variabili temporali
float interval = 0;
float time = 0;
float previousTime = 0;
float samplingRate = 0;
float dotTime = 0;
float refreshTime = 0;
//Theme variables
//Variabili estetica
int menuWidth = 1000;  
int menuHeight = 25;
int margin = 5;
int textPadding = 13;
int baudRate = 115200;
float  dotTimeY = 0;
float dotXsec = 0;
int pointCount = 0;
int serialReveice = 0;
int serialReceiveXsec = 0;
int regulation = 0;
int Vscale = 0;

void setup() {
  size(1000, 530); 
  background(#333333);
  /// blue menu bar
  fill(#135CAE);
  stroke(#135CAE);
  rect(0, 0, menuWidth, menuHeight);
  //includes
  //allegati
  b = loadImage("favicon.png");
  font = loadFont("Ubuntu-Regular-12.vlw"); 
  b = loadImage("favicon.png");
  start = loadImage("start.png");
  stop = loadImage("cross.png");
  upReg = loadImage("arrow_up.png");
  dnReg = loadImage("arrow_down.png");
  font = loadFont("Ubuntu-Regular-12.vlw");
  //build GUI interface
  //genera l'interfaccia GUI 
  refreshGUI();
}

void refreshGUI() {
  if(y == 0){ background(#333333);} 
  //menu bar
  fill(#135CAE);
  stroke(#135CAE);
  rect(0, 0, menuWidth, menuHeight);
  //Cartesian diagram
  stroke(#FFFFFF);
  fill(#FFFFFF);
  //Axis reference
  //Referenze assi
  text("0", 0 + margin + textPadding, height - margin - textPadding);
  text((((regulation + height)) / 89), -7 + margin + textPadding, menuHeight + margin + textPadding);
  text("Time", width - textPadding - 23, height - margin - textPadding);
  //axis
  //assi
  line(0 + margin,height - margin, width - margin ,height - margin);
  line(0 + margin,menuHeight + margin , 0 + margin , height - margin);
  //images
  //immagini
  image(b, width - 23, 0 + margin); 
  image(start, width - 44, 1 + margin); 
  image(stop, width - 135, 1 + margin); 
  //Texts
  //Testi
  stroke(#FFFFFF);
  fill(#FFFFFF);
  textFont(font, 12); 
  text("lines/second:", 1 + margin, 13 + margin);
  if(time - previousTime >= 0){stroke(#FFFFFF);fill(#FFFFFF);text(int(dotXsec), 78 + margin, 13 + margin);}
  else { text("0", 81 + margin, 13 + margin); }
  text("Serial prints/second:", 110 + margin, textPadding + margin);
  text(serialReceiveXsec, 228 + margin, textPadding + margin);
  text("Start Graph", width - 110, textPadding + margin);
  text("Pause Serial", width - 202, textPadding + margin);
  text("| Use Up and Down to edit scale |", width - 392, textPadding + margin);
  text("| Use Left and Right to edit sampling rate ", width - 630, textPadding + margin);
  text("www.gioblu.com - Grapher1.0", width - textPadding - 156, textPadding + margin + 21);
}

void draw() {
  refreshTime = millis();
  if(mouseX < 1000 && mouseX > 900 && mouseY > 0 && mouseY < 25 ||
     mouseX < 900 && mouseX > 800 && mouseY > 0 && mouseY < 25) {
    cursor(HAND);} else { cursor(ARROW); }
  }
  
void mouseClicked() {
  if(mouseX < 1000 && mouseX > 900 && mouseY > 0 && mouseY < 25) {
    serial = new Serial(this, Serial.list()[0], baudRate);
    serial.bufferUntil('\n');
  }
   if(mouseX < 900 && mouseX > 800 && mouseY > 0 && mouseY < 25) {
     serial.clear();
     serial.stop();
  }
}

void keyPressed() {
if (keyCode == UP) regulation++;
if (keyCode == DOWN) regulation--;
if (keyCode == LEFT) samplingRate = samplingRate + 0.3;
if (keyCode == RIGHT) if(samplingRate > 0){samplingRate = samplingRate - 0.3;}
}

void serialEvent(Serial serial) {
  String valueStr = serial.readStringUntil('\n'); //leggi la seriale suddivisa da a capo
  if(valueStr != null) {      //se la stringa non è nulla
    serialReveice++;
    time = millis();
    if(millis() - dotTime >= 1000) {
     dotXsec = pointCount - dotTimeY;
     dotTime = millis();
     dotTimeY = pointCount;
     serialReceiveXsec = serialReveice;
     serialReveice = 0;
    }
    if(time - previousTime < samplingRate) { return; }
    valueStr = trim(valueStr);                    //trimmo
    String[] data = split(valueStr, "/");    //spezzo la stringa dove c'è lo spazio
    y = map(int(data[0]), 0, 1023, float(height) - float(margin) ,0 + float(menuHeight) + float(margin)) + regulation;
    y1 = map(int(data[1]), 0, 1023, float(height) - float(margin) ,0 + float(menuHeight) + float(margin))+ regulation;
    y2 = map(int(data[2]), 0, 1023, float(height) - float(margin) ,0 + float(menuHeight) + float(margin))+ regulation;
    y3 = map(int(data[3]), 0, 1023, float(height) - float(margin) ,0 + float(menuHeight) + float(margin))+ regulation;
    y4 = map(int(data[4]), 0, 1023, float(height) - float(margin) ,0 + float(menuHeight) + float(margin))+ regulation;
    y5 = map(int(data[5]), 0, 1023, float(height) - float(margin) ,0 + float(menuHeight) + float(margin))+ regulation;
    
    stroke(#FF0000); //curva rossa
    if(x >= 1) line(lastX, lastY, x, y);
    stroke(#135CAE); // curva blu
    if(x >= 1)line(lastX, lastY1, x, y1); 
    stroke(#FFFFFF); // curva bianca
    if(x >= 1)line(lastX, lastY2, x, y2); 
    stroke(#888888); // curva ??
    if(x >= 1)line(lastX, lastY3, x, y3); 
    stroke(#4f8888); // curva ??
    if(x >= 1)line(lastX, lastY4, x, y4); 
    stroke(#6CC888); // curva ??
    if(x >= 1)line(lastX, lastY5, x, y5); 
    
    stroke(#666666); // curva referenza 0
    if(x >= 1)line(lastX, height - margin + regulation, x, height - margin + regulation); 
    pointCount++;
    lastX = x;
    lastY = y;
    lastY1 = y1;
    lastY2 = y2;
    lastY3 = y3;
    lastY4 = y4;
    lastY5 = y5;    
    if(x++ >= width) {
      lastX = 0;
      lastY = 0;
      x = 0;
      y = 0;
      refreshGUI();
    }  
  }previousTime = time;
}

 

Come vedete il codice non è cosi' complesso, si risolve in 3 componenti fondamentali, una che è la funzione refreshGUI() che permette di visualizzare tutti gli elementi della pagina. Questa funzione risolve il principale problema in cui incorre un novizio su Processing. Quando si crea qualcosa, qualsiasi cosa, questa rimane impressa nello schermo e non la si puo' spostare finchè questo non viene refreshato, cioè in termini semplici, coperto con un nuovo sfondo.

Purtroppo non esiste un controllo della z degli oggetti, come lo z-index del css per esempio, ma è possibile con una funzione di questo tipo, richiamata nel momento corretto,  riuscire a coprire le vecchie informazioni con le nuove e "cancellare le curve dallo sfondo" quando raggiungono il margine destro dell'applicazione, tornando all'inizio. Il secondo componente fondamentale del programma è la funzione SerialEvent() dove avviene la rappresentazione dei valori ottenuti dalla seriale, previo trimming e spezzettamento della stringa grazie ai suddivisori conosciuti, in questo caso /.

Non per ultime ci sono le funzioni che permettono l'uso del mouse e dei pulsanti, per interagire con l'applicazione. Grazie a keyPressed() possiamo regolare il sampling e quindi rallentare la curva (cosa davvero comoda per osservarla con calma) usando i tasti sinistra e destra della tastiera.

 

Cosa va messo su Arduino?

La parte Arduino è davvero basica e semplice da capire.

Per permettere a tutti di testare un buon range di cose ho scelto di permettere di base la visualizzazione di sei curve contemporaneamente. Questo pero' implica che Arduino mandi sempre 6 dati a Processing, suddivisi da /, basterà rendere nulli (0) quelli che non si vogliono visualizzare. La stringa avrà questo aspetto: analogRead(0)/0/0/0/0/0 se si vuole visualizzare solo una curva.

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.print(analogRead(0));
  Serial.print("/");
  Serial.print(0);
  Serial.print("/");
  Serial.print(0);
  Serial.print("/");
  Serial.print(0);
  Serial.print("/");
  Serial.print(0);
  Serial.print("/");
  Serial.println(0);
}

 

Qui trovate l'applicativo eseguibile, i sorgenti e tutto quello che puo' essere utile:

Sorgente: www.gioblu.com/GiO/Grapher/grapher-001-src.tar.gz

Eseguibile win32 www.gioblu.com/GiO/Grapher/grapher-001-win32.tar.gz

Eseguibile win64:  www.gioblu.com/GiO/Grapher/grapher-001-win64.tar.gz

Eseguibile macosx:  www.gioblu.com/GiO/Grapher/grapher-001-macosx.tar.gz

Eseguibile linux32:  www.gioblu.com/GiO/Grapher/grapher-001-linux32.tar.gz

Eseguibile linux64:  www.gioblu.com/GiO/Grapher/grapher-001-linux64.tar.gz

Creative Commons License
Grapher 1.0 by Giovanni Blu Mitolo is licensed under a Creative Commons Attribution-NonCommercial 3.0 Unported License.
Based on a work at www.gioblu.com.

 

Gioblu Robotics © 2010 - 2012 · Sitemap · privacy

gioscarab@gmail.com · Via Savona 123 20146 Milano MI · PI 06977550968 · Codice fiscale MTLGNN86S09F205F

Gioblu BOTServer è online dal 10 Aprile 2010 - 319.232 Visite - 1.027.175 Pagine visualizzate - 182.309 Visitatori unici - 536 utenti attivi