Frequenza di campionamento e interfacciamento matlab

Ciao a tutti,
sto cercando di acquisire dati tramite arduino per poi graficarli con matlab. Mi servirebbe una frequenza di campionamento di almeno 1 kHz; il problema è che dai test effettuati con un potenziometro, la frequenza sembra essere intorno ai 30Hz. E' un problema di matlab o di arduino? Qual è la massima frequenza di campionamento di arduino? Ho un arduino uno r3, e per interfacciare matlab e arduino ho utilizzato il toolbox apposito di mathworks. Allego il codice matlab per l'acquisizione:

passo=1;
interv=1000;
t=1;
x=0;
while(t<interv)
b=a.analogRead(0);
x=[x,b];
plot(x);
axis([0 interv 0 1023]);
grid
t=t+passo;
drawnow
end

Ho anche provato a preallocare l'array ma stranamente le prestazioni sembrano peggiorare.
Grazie per l'aiuto

E il codice di Arduino?
Sei sicuro che sia l'acquisizione lenta e non l'invio dei dati da Arduino?

questo è lo sketch che ho caricato su arduino, è quello che si trova all'interno del toolbox scaricato dal sito di mathworks.
In questo caso non programmo da arduino ma direttamente da matlab!

/* define internal for the MEGA as 1.1V (as as for the 328) */
#if defined(AVR_ATmega1280) || defined(AVR_ATmega2560)
#define INTERNAL INTERNAL1V1
#endif

void setup() {
/* Make sure all pins are put in high impedence state and
that their registers are set as low before doing anything.
This puts the board in a known (and harmless) state /
int i;
for (i=0;i<20;i++) {
pinMode(i,INPUT);
digitalWrite(i,0);
}
/
initialize serial */
Serial.begin(115200);
}

void loop() {

/* variables declaration and initialization */

static int s = -1; /* state /
static int pin = 13; /
generic pin number */

int val = 0; /* generic value read from serial /
int agv = 0; /
generic analog value /
int dgv = 0; /
generic digital value */

/* The following instruction constantly checks if anything
is available on the serial port. Nothing gets executed in
the loop if nothing is available to be read, but as soon
as anything becomes available, then the part coded after
the if statement (that is the real stuff) gets executed */

if (Serial.available() >0) {

/* whatever is available from the serial is read here */
val = Serial.read();

/* This part basically implements a state machine that
reads the serial port and makes just one transition
to a new state, depending on both the previous state
and the command that is read from the serial port.
Some commands need additional inputs from the serial
port, so they need 2 or 3 state transitions (each one
happening as soon as anything new is available from
the serial port) to be fully executed. After a command
is fully executed the state returns to its initial
value s=-1 */

switch (s) {

/* s=-1 means NOTHING RECEIVED YET ******************* */
case -1:

/* calculate next state /
if (val>47 && val<90) {
/
the first received value indicates the mode
49 is ascii for 1, ... 90 is ascii for Z
s=0 is change-pin mode
s=10 is DI; s=20 is DO; s=30 is AI; s=40 is AO;
s=90 is query script type (1 basic, 2 motor)
s=340 is change analog reference
/
s=10
(val-48);
}

/* the following statements are needed to handle
unexpected first values coming from the serial (if
the value is unrecognized then it defaults to s=-1) */
if ((s>40 && s<90) || (s>90 && s!=340)) {
s=-1;
}

/* the break statements gets out of the switch-case, so
/* we go back to line 97 and wait for new serial data /
break; /
s=-1 (initial state) taken care of */

/* s=0 or 1 means CHANGE PIN MODE */

case 0:
/* the second received value indicates the pin
from abs('c')=99, pin 2, to abs('t')=116, pin 19 /
if (val>98 && val<117) {
pin=val-97; /
calculate pin /
s=1; /
next we will need to get 0 or 1 from serial /
}
else {
s=-1; /
if value is not a pin then return to -1 /
}
break; /
s=0 taken care of */

case 1:
/* the third received value indicates the value 0 or 1 /
if (val>47 && val<50) {
/
set pin mode /
if (val==48) {
pinMode(pin,INPUT);
}
else {
pinMode(pin,OUTPUT);
}
}
s=-1; /
we are done with CHANGE PIN so go to -1 /
break; /
s=1 taken care of */

/* s=10 means DIGITAL INPUT ************************** */

case 10:
/* the second received value indicates the pin
from abs('c')=99, pin 2, to abs('t')=116, pin 19 /
if (val>98 && val<117) {
pin=val-97; /
calculate pin /
dgv=digitalRead(pin); /
perform Digital Input /
Serial.println(dgv); /
send value via serial /
}
s=-1; /
we are done with DI so next state is -1 /
break; /
s=10 taken care of */

/* s=20 or 21 means DIGITAL OUTPUT ******************* */

case 20:
/* the second received value indicates the pin
from abs('c')=99, pin 2, to abs('t')=116, pin 19 /
if (val>98 && val<117) {
pin=val-97; /
calculate pin /
s=21; /
next we will need to get 0 or 1 from serial /
}
else {
s=-1; /
if value is not a pin then return to -1 /
}
break; /
s=20 taken care of */

case 21:
/* the third received value indicates the value 0 or 1 /
if (val>47 && val<50) {
dgv=val-48; /
calculate value /
digitalWrite(pin,dgv); /
perform Digital Output /
}
s=-1; /
we are done with DO so next state is -1 /
break; /
s=21 taken care of */

/* s=30 means ANALOG INPUT *************************** */

case 30:
/* the second received value indicates the pin
from abs('a')=97, pin 0, to abs('f')=102, pin 6,
note that these are the digital pins from 14 to 19
located in the lower right part of the board /
if (val>96 && val<103) {
pin=val-97; /
calculate pin /
agv=analogRead(pin); /
perform Analog Input /
Serial.println(agv); /
send value via serial /
}
s=-1; /
we are done with AI so next state is -1 /
break; /
s=30 taken care of */

/* s=40 or 41 means ANALOG OUTPUT ******************** */

case 40:
/* the second received value indicates the pin
from abs('c')=99, pin 2, to abs('t')=116, pin 19 /
if (val>98 && val<117) {
pin=val-97; /
calculate pin /
s=41; /
next we will need to get value from serial /
}
else {
s=-1; /
if value is not a pin then return to -1 /
}
break; /
s=40 taken care of */

case 41:
/* the third received value indicates the analog value /
analogWrite(pin,val); /
perform Analog Output /
s=-1; /
we are done with AO so next state is -1 /
break; /
s=41 taken care of */

/* s=90 means Query Script Type (1 basic, 2 motor) /
case 90:
if (val==57) {
/
if string sent is 99 send script type via serial /
Serial.println(1);
}
s=-1; /
we are done with this so next state is -1 /
break; /
s=90 taken care of */

/* s=340 or 341 means ANALOG REFERENCE *************** */

case 340:
/* the second received value indicates the reference,
which is encoded as is 0,1,2 for DEFAULT, INTERNAL
and EXTERNAL, respectively */

switch (val) {

case 48:
analogReference(DEFAULT);
break;

case 49:
analogReference(INTERNAL);
break;

case 50:
analogReference(EXTERNAL);
break;

default: /* unrecognized, no action */
break;
}

s=-1; /* we are done with this so next state is -1 /
break; /
s=341 taken care of */

/* ******* UNRECOGNIZED STATE, go back to s=-1 ******* */

default:
/* we should never get here but if we do it means we
are in an unexpected state so whatever is the second
received value we get out of here and back to s=-1 */

s=-1; /* go back to the initial state, break unneeded */

} /* end switch on state s */

} /* end if serial available */

} /* end loop statement */

Di tutto il codice sopra riportato questo commento è la cosa più interessante.

/* This part basically implements a state machine that
reads the serial port and makes just one transition
to a new state, depending on both the previous state
and the command that is read from the serial port.
Some commands need additional inputs from the serial
port, so they need 2 or 3 state transitions (each one
happening as soon as anything new is available from
the serial port) to be fully executed. After a command
is fully executed the state returns to its initial
value s=-1 */

Qui viene descritto il funzionamento dello sketch.
Per ogni lettura servono o 2 o 3 cambiamenti di stato che suppongo possano corrispondere ai cicli di loop.
Credo che questo rallenti molto le letture e quindi gli invii dei dati.
Se nella tua implementazione non devi modificare la configurazione della scheda o fare altre letture oltre quella analogica potresti tagliere un po' qua e un po ' la riducendo e velocizzando drasticamente il codice. Perdi naturalmente in flessibilità.

intanto grazie per la disponibilità! Comunque, a prescindere dal collegamento con matlab, è possibile acquisire dati a 1 kHz con arduino? è fisicamente possibile? Perchè se si tratta solo di un problema di software e di implementazione, insisto nel trovare una soluzione, se invece è un limite hardware a quel punto devo procurarmi una scheda di acquisizione più potente!

Arduino funziona con una frequenza di 16Mhz.
La sua funzione più lenta è la AnalogRead che impiega nella singola lettura 100 microsecondi; quindi la massima velocità di acquisizione è di 10kHz ovvero un ordine di grandezza sopra le tue necessità.

analogRead()
Description
Reads the value from the specified analog pin. The Arduino board contains a 6 channel (8 channels on the Mini and Nano, 16 on the Mega), 10-bit analog to digital converter. This means that it will map input voltages between 0 and 5 volts into integer values between 0 and 1023. This yields a resolution between readings of: 5 volts / 1024 units or, .0049 volts (4.9 mV) per unit. The input range and resolution can be changed using analogReference().
It takes about 100 microseconds (0.0001 s) to read an analog input, so the maximum reading rate is about 10,000 times a second.

Anche per la trasmissione dei dati non dovresti avere problemi perché leggi "solo" 1000 valori al secondo, quindi impostando la seriale a 115000 baud se ben al di sopra.

intanto mi scuso perché non avevo minimamente pensato a guardare nella libreria il metodo analogRead per vedere a che velocità acquisisse :blush:
In ogni caso ho provato a fare l'acquisizione senza matlab con questo codice:

int t=0;
int val = 0;

void setup() {
pinMode(A0,INPUT);
Serial.begin(9600);
}

void loop() {
if (t<1000){ //voglio acquisire 1000 campioni
val = analogRead(A0);
Serial.println(val);
t++;
}
else if(t==1000){ //per controllare il tempo di acquisizione
Serial.println(millis());
t++;
}
}

Settando Serial.begin con argomento 115200, non funziona il print sul monitor seriale! compaiono strani simboli e il computer si rallenta. Pensando che fosse un problema di sovraccarico della porta seriale ho anche provato a mettere un delay all'interno del ciclo di acquisizione, con il risultato che il computer non si è rallentato ma in ogni caso sul monitor seriale continuano ad apparire simboli e non valori numerici!

Mi sa che non hai settato il monitor Seriale alla stessa velocità con cui hai settato Arduino :slight_smile:

Allora arduino lo setto a 115200 baud semplicemente mettendo nel setup Serial.begin(115200) giusto? Mentre per settare il monitor seriale ho messo 115200 dal menù a tendina che trovo nel monitor seriale stesso...ma non funziona...in cosa sbaglio? :cold_sweat:

Prova così

int t = 0;
int val = 0;

void setup() {
  delay(2000); // Anti Serial Brick
  pinMode(A0,INPUT);
  Serial.begin(9600);
}

void loop() {
  t++;
  delay(1); // pausa di 1 millisecondo
  if (t<1000){                //voglio acquisire 1000 campioni
    val = analogRead(A0);
    Serial.println(val);
  }

  if(t>=1000){         //per controllare il tempo di acquisizione
    Serial.println(millis());
    t=0; // resetto il contatore per un nuovo ciclo.
  }

}

La pausa di un millisecondo serve per rallentare il ciclo che altrimenti risulterebbe troppo veloce; nell'ordine dei 100 microsecondi.