plot grafico tramite processing real time

ciao a tutti,
ho già scritto un post (sezione hardware) su un primo lavoro di tesi che sto facendo: rilevare la caratteristica corrente tensione di un alimentatore.http://arduino.cc/forum/index.php/topic,126840.0.html

dopo aver implementato questo circuito su breadboard

con il codice corrispondente per rilevare tensioni e corrente

#define RELAY_PIN 3 //digital pin to control the relay
#define c_pin A1    //capacitor analog pin
#define ps_pin A0   //power supply analog pin


void setup()
{
  pinMode(RELAY_PIN, OUTPUT);
  Serial.begin(9600); // open serial
  Serial.println("Press the spacebar to toggle relay on/off");
}


void loop()
{
  static int relayVal = 0;
  int cmd;
  float c_voltage, ps_voltage, current;
  static float res = 5.2;
  
  while (Serial.available() > 0)
  {
    cmd = Serial.read();
      
    switch (cmd)
    {
    case ' ':
      {
        relayVal ^= 1; // xor current value with 1 (causes value to toggle)
        if (relayVal)
          Serial.println("Relay on");
        
        break;
      }
    default:
      {
        Serial.println("Press the spacebar to toggle relay on/off");
      }
    }
      
    if (relayVal)
    {
      digitalWrite(RELAY_PIN, HIGH);
      delay(20);           // relay transition time
      do
      {
        c_voltage = analogRead(c_pin);
        ps_voltage = (analogRead(ps_pin));
        
        c_voltage = c_voltage *5 /1023;
        ps_voltage = ps_voltage * 5 / 1023;
        current = (ps_voltage - c_voltage)/res;
        
        Serial.print(ps_voltage);
        Serial.print("  ");
        Serial.print(c_voltage);
        Serial.print("  ");
        Serial.print(current);
        Serial.print("\n");
      }
      while(current >0.000001);
      
      Serial.println("Please wait for the capacitor to discharge");
      digitalWrite(RELAY_PIN, LOW);
      do
      {
        c_voltage = analogRead(A1);
        c_voltage = c_voltage * 5 / 1023;
        
        Serial.println(c_voltage);
        delay(50);  // just a delay to not print all the values
        
      }
      while(c_voltage > 0.001);
      
      relayVal = 0;  // reinitialze the relayVal 
      Serial.println("\nThe relay is off, press the spacebar");
      Serial.println("again if you want to repeat the measure\n");
    }  
      

    }
}

vorrei riuscire a plottare in real time i dati che ricevo su corrente e tensione tramite processing.

ho letto tutti i vari post qui sul forum che riguardano processing e visto anche qualche tutorial, ma non ho trovato come fare a prendere le due serie di dati e plottarli in un semplice grafico tipo questo ottenuto con excel

grazie per l'aiuto

Ciao,
prova a dare un'occhiata a questo progetto:
http://code.google.com/p/arduinoscope/
dovresti trovare tutti gli strumenti che ti servono per realizzare il tuo progetto :stuck_out_tongue:

ciao

questo in c#, sorgenti su GitHub:
http://www.lucadentella.it/2011/11/08/analogdemo-plot-di-dati-e-invio-a-pachube-in-c/

bye

leggi i dati dalla seriale e li metti in un ArrayList. Questo arrayList va sempre usato in un blocco
syncronize(nomeArrayList){
//ora puoi usare in sicurezza l'arrayList
}

Questo è necessario perchè l'acquisizione su seriale e il disegno sono su due thread diversi; usare una line per ogni valore seriale manderebbe in palla la grafica, che non riuscirebbe a disegnare abbastanza in fretta, mentre usare un array non sincronizzato può creare dei casini si concorrenza esagerati

ogni draw() scorri gli ultimi A valori dell'arraylist (dove X è la scala lungo l'asse X) e poi disegni delle righe col comando
line(X1, Y1, X2, Y2);

dove Y1 è il valore attuale, Y2 il prossimo valore, X1 e X2 un valore che si incrementa di Tot, dove tot è la larghezza della finestra diviso A

se vuoi disegnare più righe, basta ripetere questa parte del codice, sommando una costante ad Y per evitare che i dati si sovrappongano (se non vuoi la sovrapposizione) facendo attenzione anche a non uscire dallo schermo!

sempre per non uscire dallo schermo, probabilmente dovrai usare uno zoom anche sull'asse Y, ovvero tutti i valori di Y moltiplicarli per B, esattamente come quelli di X li ai divisi per A

scusate ragazzi, il professore mi ha chiesto di mofificare lo sketch che ho sopra scritto, per avere più misure e visualizzare anche il tempo a cui vengono effettuate le misure, impostando come t=0 l'istante in cui inizio l'acquisizione dei dati?
coem faccio a impostare un cronometro? e che ''risoluzione'' può avere questo cronometro secondo voi?

guarda la funzione millis() di Arduino:

secondo voi come potrei aumentare il numero di misure effettuate? esiste un modo per aumentare la frequenza dell'analogRead?

sì, diminuendo il prescaler. Però diminuisce anche la precisione della lettura, se non erro a 16 (il prescaler di defautl mi pare sia sui 128) ha una precisione a 8bit se non meno (cioè, i valori "reali" vanno da 0 a 255, il resto è rumore, anzichè i classici 10bit da 0 a 1023)

http://arduino.cc/forum/index.php/topic,6549.0.html
mi basta mettere il codice che trovo in questo post per aumentare la velocità dell'adc quindi?

esatto

ma con il codice che ho scritto, anzi esattamente questo segmento:

c_voltage = analogRead(c_pin);
          time1 = micros()-time0; //time c_voltage measurement
          ps_voltage = analogRead(ps_pin);
          time2 = micros() - time0; //time ps_voltage measurement

secondo voi time1 e time2 che poi andrò a stampare, sono esattamente il tempo in cui arduino prende la misura o sono posticipati?

posticipati, direi di circa 204microsecondi (200 microsecondi di lettura + 4 microsecondi per la nuova richiesta di micros)

essendo un valore fisso, di solito si può ignorare senza conseguenze, semplicemente il grafico è sballato di 200 micros sull'asse del tempo (se prendi come riferimento l'inizio lettura, se prendi la fine lettura sei quasi perfetto)

possibile che siano addirittura posticipati di 204 microsecondi? forse ho sbaglaito a non postarvi tutto il codice, perchè da quello che vedo io in output ad esempio, la prima misura sul c_voltage avviene al tempo di 20us

#define RELAY_PIN 3 //digital pin to control the relay
#define c_pin A1    //capacitor analog pin
#define ps_pin A0   //power supply analog pin
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif



void setup()
{
  // set prescale to 16
  sbi(ADCSRA,ADPS2) ;
  cbi(ADCSRA,ADPS1) ;
  cbi(ADCSRA,ADPS0) ;

  pinMode(RELAY_PIN, OUTPUT);
  Serial.begin(9600); // open serial
  Serial.println("How many measure do you want to take?");
}


void loop()
{
  
  int num_misure;
  float c_voltage, ps_voltage, current;
  static float res = 3.59;
  unsigned long time1, time2, time0;
  int i ;
  
  
  if(Serial.available() > 0)
  { 
     num_misure = Serial.parseInt(); // Arduino stream function which receive a string on the serial port and then changes it int an integer 
     if(num_misure > 0)
     { 
     
      for(i = 1 ; i <= num_misure; i++)
     {
      Serial.print("Misura n.");
      Serial.println(i);
      
      digitalWrite(RELAY_PIN, HIGH);
      delay(20);            // relay transition time::::::: problema, ho impostato questo ritardo per aspettare la commutazione del ralay, ma questo non ha un tempo fisso
      time0 = micros();     // time the measure start
      do
      {
        c_voltage = analogRead(c_pin);
        time1 = micros()-time0; //time c_voltage measurement
        ps_voltage = analogRead(ps_pin);
        time2 = micros() - time0; //time ps_voltage measurement
        
        c_voltage = c_voltage *5 /1023;
        ps_voltage = ps_voltage * 5 / 1023;
        
        current = (ps_voltage - c_voltage)/res;
        
        Serial.println(ps_voltage);
        Serial.println(time1);
        Serial.println(c_voltage);
        Serial.println(time2);
        Serial.println(current);
        Serial.print('\n');
      }
      while(current >0.000001);
      
      Serial.println("Please wait for the capacitor to discharge");
      digitalWrite(RELAY_PIN, LOW);
      do
      {
        c_voltage = analogRead(A1);
        c_voltage = c_voltage * 5 / 1023;
        
        Serial.println(c_voltage);
        delay(50);  // just a delay to not print all the values
        
      }
      while(c_voltage > 0.001);
      Serial.print("\n\n");
        
      
     
    }   
  }
  }}

l'ho modificato rispetto a quello postato prima settando il prescaler a 16 come mi avevi descritto anche tu

l'output che ho è questo:(lo posto come codice èper renderlo leggibile)

How many measure do you want to take?
Misura n.1
2.65
32
1.01
48
0.46

2.74
1592
1.03
1612
0.48

2.83
3332
1.11
3352
0.48

3.83
25316
2.09
25336
0.49

5.00
59636
3.58
59656
0.39

5.00
93956
4.86
93976
0.04

5.00
128280
5.00
128296
0.00

Please wait for the capacitor to discharge
5.00
1.64
0.47
0.13
0.01
0.00

inoltre vorrei eliminare quella serie di Serial.print() per aumentare i ''punti'' di misura
avrei pensato alla costruzione di tre array PS_VOLTAGE[], C_VOLTAGE, CURRENT[], in cui storare i dati, e solo in una seconda parte stamparli, magari direttamente su excel o su matlab se riesco a trovare il modo di farlo, in maniera tale da poterli manipolare per grafici direttamente.

secondo voi riesco ad aumentare il numero di misure che riesco a prendere se li metto negli array invece che stamparli subito in output?

ah ok, se cambi il prescaler cambia la velocità dell'analogRead, e quindi è giusto che il tempo di "posticipazione" diminuisce.

per rendere il codice più veloce usa la write() invece che la pritn(): la print converte in caratteri ascii, ovvero 1 byte per ogni cifra; con la write scrivi in maniera binaria, quindi scrivi tanti byte quanto la dimensione della variabile.

Cole prescaler a 16 hai una precisione a 8 byte, quindi puoi prendere sono gli 8 byte più significativi (sui 10 che ti restituisce l'analogRead) e quindi aresti un byte per lettura.

Però devi inventarti un sistema per sincronizzare i dati.

a casa ho del codice sia per inviare che per ricevere, ma devo upparlo sul repo. Se hai facebook, ho postato del codice che fa 3 letture analogice (a scelta a 10 o 8 bit di precisione), nel caso di 8 bit le invio dirette al PC, nel caso di 10 bit, faccio la lettura dei 3 dati in un array, poi anzichè inviare 6byte (2 byte per letura, un int) unisco i dati per fare in modo di usare solo 4 byte (4*8=32bit, avanzano 2 bit a scrittura, in teoria facendo un array di 30 elementi guadagni un byte "a gratis" sfruttando anche quei due bit)

il trucco si sincronizzazione è che l'arduino andava in loop finchè processing,. quando pronto a ricevere, inviava un comando.

uesto perchè se inizi a legegre il flusso seriale in un momento a caso non sai il byte che stai leggendo a che misurazione si riferisce, e nel caso di invio di più di un byte a varibaile (da int in su) non sai nemmeno se il byte che hai letto è la parte bassa o la parte alta.

ok, ho capito tutto fino al byte gratis che si guadagna, dopo mi sono perso. ti mando un mess privato

lesto:
ah ok, se cambi il prescaler cambia la velocità dell'analogRead, e quindi è giusto che il tempo di "posticipazione" diminuisce.

per rendere il codice più veloce usa la write() invece che la pritn(): la print converte in caratteri ascii, ovvero 1 byte per ogni cifra; con la write scrivi in maniera binaria, quindi scrivi tanti byte quanto la dimensione della variabile.

Cole prescaler a 16 hai una precisione a 8 byte, quindi puoi prendere sono gli 8 byte più significativi (sui 10 che ti restituisce l'analogRead) e quindi aresti un byte per lettura.

Però devi inventarti un sistema per sincronizzare i dati.

a casa ho del codice sia per inviare che per ricevere, ma devo upparlo sul repo. Se hai facebook, ho postato

ok, fino qui ho capito,

del codice che fa 3 letture analogice (a scelta a 10 o 8 bit di precisione), nel caso di 8 bit le invio dirette al PC, nel caso di 10 bit, faccio la lettura dei 3 dati in un array, poi anzichè inviare 6byte (2 byte per letura, un int) unisco i dati per fare in modo di usare solo 4 byte (4*8=32bit, avanzano 2 bit a scrittura, in teoria facendo un array di 30 elementi guadagni un byte "a gratis" sfruttando anche quei due bit)

il trucco si sincronizzazione è che l'arduino andava in loop finchè processing,. quando pronto a ricevere, inviava un comando.

uesto perchè se inizi a legegre il flusso seriale in un momento a caso non sai il byte che stai leggendo a che misurazione si riferisce, e nel caso di invio di più di un byte a varibaile (da int in su) non sai nemmeno se il byte che hai letto è la parte bassa o la parte alta.

questa partte invece mi è un po' oscura :grin: soprattutto la fine.

se prendiamo 10 bit per lettura (risoluzione max ADC), abbiamo 30 bit.
se prendiamo 4 byte ("risoluzione" min seriale), abbiamo 32bit. Quindi possiamo mettere 3 letture ADC in 4 byte! ma sprechiamo 2 bit a volta.

Se volessimo usare anche questi 2 bit, basta vedere ogni quante scritture abbiamo accumulato abbastanza "2bit" da fare 10 bit:
10/2=5scitture di 3 valori (avvero 4 byte)

quindi ogni 53=15 letture, ovvero 54=20byte scritti, abbiamo accumultao abbastanza 2bit per farci stare una lettura ADC.

Quindi il messaggio finale usa 20 byte e conterrà 16 letture invece che 15!

di seguito vedi come le letture di A, B e C (3 letture ADC a 10 bit) vengono posizionate nei primi 3 byte. nota come se ne fregano altamente della suddivisione in byte del messaggio; dunque ogni invio è assolutamente legato al precedente e necessita di un poco di gioco di bit-shift.
io ho preferito perdere 2 bit e creare il messaggio corto, ovvero di 3 letture.

In realtà per non perdere nessun bit il messaggio deve essere lungo multipli di 5byte (il 20 calcolato prima non è ottimizzato :slight_smile: ), infatti 5byte*8bit = 40bit, perfetti per contenere 40bit/10bit/lettureADC=4lettureADC
( bit/lettureADC è il "dimensionamento" della variabile, ovvero numero di bit fratto lettura ADC, tardotto in lingua corrente vuol dire bit necessari per lettura ADC)

1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 _ 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 _ 17 - 18 - 19 - 20 - 21 - 22 - 23 - 24
a a a a a a a a a a b b b b b b b b b b c c c c

NOTA: se usi solo gli 8bit più signifacativi a lettura, abbiamo che i bit ADC sono == ai bit utili via seriale, dunque questo casino assurdo non è necessario da fare

Per la sincronizzazione, abbiamo 3 byte (da 1 a 8, da 9 a 16, da 17 a 24), e per decodificare l'informazione bisgna avere per forza i dati a parteire dal primo byte fino a fine messaggio (che può essere lungo 4,5,20 byte a secondaa del valore che hai scelto per comprimere il più possibile).
Immagina che arduino inizia a leggere e sparare i dati via seriale al pc. finchè il programma lato PC non è attivato, i byte vengono persi nel nulla galattico (/dev/null, se sei linuxiano). Quindi la tua applicazione parte e gli arriva un byte. questo byte è forse l'inizio della sequenza? o quello di mezzo? o quello finale?
se invece usi L'ADC a 8 bit, sai che ogni byte che ti arriva è una lettura e non devi decodificarla, però nel caso tu faccia letture su più pin, o con significato differente, questa lettura cosa rappresenta? la lettura sul pin A0, quella sul pin A2, etc..?

per non complicarmi troppo la vita per il momento ho deciso di lasciare la risoluzione a 8bit, vorrei provare però a cambiare strategia mettendo i dati prima in alcuni array e poi a stamparli, il problema è che dopo che accendo il relay, non succede niente, non mi stampa niente.
non mi sembra che il codice sia sbagliato.
i due array bidimensionali fungono da tabelle: nella prima colonna ci sono i valori di tensione misurati, nella seconda colonna ci sono i tempi a cui avvengono le misure. il principio è di riempire gli array con un numero x di misure e poi ordinare i risultati in ordine crescente dei tempi, ma ripeto, non ho alcun output.
qualcuno riesce a vedere il problema?

#define RELAY_PIN 3 //digital pin to control the relay
#define c_pin A1    //capacitor analog pin
#define ps_pin A0  //power supply analog pin
#define num_misure 50

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif



void setup()
{
  // set prescale to 16
  sbi(ADCSRA,ADPS2) ;
  cbi(ADCSRA,ADPS1) ;
  cbi(ADCSRA,ADPS0) ;

  pinMode(RELAY_PIN, OUTPUT);
  Serial.begin(9600); // open serial
  Serial.println("Press the spacebar to start to measure");
}


void loop()
{ 
  static int relayVal = 0;
  int cmd;
  float c_voltage, ps_voltage, current;
  static float res = 3.59;
  unsigned long time1, time2, time0;
  float PS_VOLTAGE[1000][2];  // these two arrays are tables in which we put the voltage 
  float C_VOLTAGE[1000][2];   // measurements and the time arduino took em
  float CURRENT[1000];
  int j = 0;

  while(Serial.available() > 0)
  { 
    cmd = Serial.read();

    switch(cmd)
    {
    case ' ':
      {
        relayVal ^= 1;
        Serial.println("Relay on");
        break;
      }
    default:
      {
        Serial.println("Press the spacebar to start to measure");
      }
    }




    if(relayVal)
    {       
      do{
        digitalWrite(RELAY_PIN, HIGH);
        delay(20);            // relay transition time ---------- problema, ho impostato questo ritardo
        // per aspettare la commutazione del relay, ma questa non è tempocostante

        time0 = micros();     // time the measure start
        do
        {

          c_voltage = analogRead(c_pin);
          time1 = micros()-time0; //time c_voltage measurement
          ps_voltage = analogRead(ps_pin);
          time2 = micros() - time0; //time ps_voltage measurement

          c_voltage = c_voltage *5 /1023;
          ps_voltage = ps_voltage * 5 / 1023;
          current = (ps_voltage - c_voltage)/res;

          //put all the measurements in their arrays
          PS_VOLTAGE[j][0] = ps_voltage;
          PS_VOLTAGE[j][1] = time1;     
          C_VOLTAGE[j][0] = c_voltage;
          C_VOLTAGE[j][1] = time2;
          CURRENT[j] = current;
          j++;
        }
        while(current >0.000001);

        //capacitor discharge
        digitalWrite(RELAY_PIN, LOW);
        do
        {
          c_voltage =  analogRead(c_pin);
          c_voltage =  c_voltage * 5 / 1023;          
        }
        while(c_voltage > 0.001);

      }
      while( j <= num_misure - 1 );

      /*
      ordinamento vettori in base ai tempi per sincronizzare le misure
       */

      j= 0;

      //stampa risultati
      for ( j = 0; j < num_misure - 1; j++)
      {
        Serial.print(PS_VOLTAGE[j][0]);
        Serial.print("  ");
        Serial.println(PS_VOLTAGE[j][1]);
      }



      relayVal = 0; //reitialize relayVal

      Serial.print("\n\n");

      Serial.print("do you want to repeat the measure?");
    }


  }   
}
c_voltage = c_voltage *5 /1023;
          ps_voltage = ps_voltage * 5 / 1023;

è corretto, ma meglio mettere delle parentesi, è più leggibile e eviti errori stupidi di precedenza

while(current >0.000001);

uhmm valore moooolto basso, probabile problema n°1. Ricorda che il pin non è a GND ma floattante, quindi mi aspetto di leggere valòori random. A priori metti come limite di NON sfondare l'array:

while(current >0.000001 && j < 1000);

poi

while(c_voltage > 0.001);

probabile errore n°2, ma almeno non sfondi array :slight_smile:

poi non capisco:

while( j <= num_misure - 1 );

perchè? non capisco bene il ragionamento

dolcis in fundo:

c_voltage = analogRead(c_pin);

attenzione, l'analog read restituisce un int, ovvero due byte, di cui ti interessando 10 bit:
0 1 2 3 4 5 6 7 - 8 9 A B C D E F
N N N N N N S S S S S S S S L L

come puoi vedere i bit N sono NON usati. Gli S sono quelli importanti, ovvero quelli che variano di più il valore della variabile, e quelli con L sono unità (F) e decine (E) (in binario eh), quindi sono i meno importanti.

tu stai salvando i bit da 8 a F:

c_voltage = analogRead(c_pin);

invece devi eliminare i due bit L in modo da prendere solo quelli interessanti:

c_voltage = (analogRead(c_pin) >> 2);

il >> sigfnica proprio "sposta i bit 2 volte a sinistra" (la parte che rimane "vuota" diventa 0)