Campionamento segnale analogico Arduino

Ciao a tutti.
Sto facendo una tesi di ingegneria dell'energia basata su un algoritmo NILM. Come primo passo ho la necessità di campionare il segnale analogico, posto lo sketch che sto utilizzando. La mia frequenza di campionamento deve essere di 600Hz o superiore.

#define SAMPLING_FREQUENCY 600
#define n_ripetizioni 5
unsigned int sampling_period_us;

unsigned int T;

void setup() {
 Serial.begin(9600);
   sampling_period_us = (1000000*(1.0/SAMPLING_FREQUENCY));
}
int current = 0;

void loop() {
unsigned long microseconds = micros();
for (int i = 0; i < n_ripetizioni*SAMPLING_FREQUENCY; i++) {
   T = i;
   while (micros() - microseconds == sampling_period_us);
   current = analogRead(A1);
   microseconds += sampling_period_us;
   Serial.print(current);
   Serial.print(" ");
   //Serial.print(microseconds);
   //Serial.print(" ");
   //Serial.print(T);
   //Serial.print(" ");
   Serial.println();
}
//Serial.print( sampling_period_us );
while(1);
//delay(1000);
}

Premetto che questo non è uno sketch da me ideato, ma frutto di diverse ricerche. Ciò che voglio fare è che in (es) 5s eseguo 5 volte il loop per avere 5*600 campioni (teoricamente). Il mio problema è che non riesco ad avere questi 3000 campioni, anzi ne ho molti meno. Sapreste dirmi dove sbaglio?
Sto utilizzando un arduino uno e come sensore un Talema AC1030. Grazie.

Beh, al di la di ogni altra considerazione, anche solo trasmettere via seriale 600 letture e 600 spazi, ipotizzando le letture di 3 cifre, di media
Richiede almeno 4 millisecondi a 9600 nos come fai tu
Se ci aggiungi tempi di lettura e altre cosette, non ci arriverai mai a 600 letture
Anche far fare il calcolo del numero di cicli dentro la for non è il massimo per la velocità
Credo anche che usare un int come indice non sia il massimo

Ciao, in conformità al regolamento, punto 7, devi editare il tuo post (quindi NON scrivendo un nuovo post, ma utilizzando il bottone More -> Modify che si trova in basso a destra del tuo post) e racchiudere il codice all'interno dei tag CODE (... sono quelli che in edit inserisce il bottone con icona fatta così: </>, tutto a sinistra).
Fatto ciò...
Quoto quanto detto da Standardoil, se rimuovi le scritture su seriale, considerando che un'analog read impiega circa 100 microsecondi per leggere il valore (come scritto nel reference) dovresti riuscire a leggere molti più campioni. Un'altra probabile correzione potrebbe essere questa:

while (micros() - microseconds == sampling_period_us);

diventa

while (micros() - microseconds >= sampling_period_us);

Perché credo che se manchi il microsecondo preciso stai li fineché non va in overflow micros() e a quel punto aspetti finché non arrivi all'uguaglianza precisa

Intanto grazie per le risposte. Ho provato ad eliminare il superfluo (conteggio cicli, spazi ...), inoltre ho fatto come mi suggeriva fabpolli e cambiato la riga:

while (micros() - microseconds >= sampling_period_us);

Il problema è che adesso trovo meno di 100 campioni.
Ritornando alla riga:

while (micros() - microseconds == sampling_period_us);

Riesco comunque a leggere 600 campioni ma in almeno 3 secondi; se aumentassi la velocità dei dati bit al secondo? Cioè da 9600 a 19200?

In merito a ciò che diceva Standardoil:
"Credo anche che usare un int come indice non sia il massimo"
Come la posso migliorare?

Sali a 115200, perchè limitare la velocità?

Dipende da cosa vuoi fare
Se devi definire un numero di conteggi devi usare una for,
Se invece definisce una cadenza di conteggio usi un test su micros
Se decidi di usare una for, dovrebbe, dico solo dovrebbe, essere più veloce , invece di un ciclo con indice intero, usare due cicli nidificati, indice byte, una csavdel tipo for 6 ripetizioni che contiene un for 100 letture, che fa 600 letture

Mauro93:
Il problema è che adesso trovo meno di 100 campioni.
Riesco comunque a leggere 600 campioni ma in almeno 3 secondi; se aumentassi la velocità dei dati bit al secondo? Cioè da 9600 a 19200?

In effetti la condizione va girata perché è un attesa, non ci avevo pensato, comunque sia credo che per massimizzare la frequenza di lettura, al netto degli altri suggerimenti, dovresti memorizzare i vari risultati e solo dopo aver terminato tutte le letture inviarle sia seriale che comunque rappresenta un elaborazione che "porta via" un discreto tempo anche aumentando la velocità di comunicazione. Poi nel tuo caso a 115200 non credo faccia la vera diiferenza ma strutturare il codcie in modo che sia performante dovrebbe comunque essere meglio

Potrebbe anche stampare i valori in binario invece che in ASCII. Già dal codice si vede che le stampe in ASCII vanno da 4 a 7 caratteri.

Mauro93:
Riesco comunque a leggere 600 campioni ma in almeno 3 secondi; se aumentassi la velocità dei dati bit al secondo? Cioè da 9600 a 19200?

Ma tu che ci devi fare con quei dati? Considera che la Serial.print() di un unsigned int più uno spazio va da 2 a 8 caratteri quindi a 9600 ci vogliono fino a circa 8 ms. Se poi ci lasci anche il println() devi aggiungere altri 2 byte e stiamo messi male...

Prima idea, sfruttare i "tempi morti" del while:

while (micros() - microseconds >= sampling_period_us);

Se fai 600 campioni attenderai circa 1.6ms, in 1ms puoi mandare 8 caratteri se vai almeno a 80kbps, quindi se imposti 115200 potresti farlo.

Ma dato che ogni campione da inviare è un uint16 puoi mandarlo sotto forma di 2 byte invece che di 5 caratteri (più separatore più crlf), e a 115200 ci metti solo 0.1ms... Aggiungici un byte di start (diciamo 0xFF ad esempio) se vuoi semplificare il parsing dall'altra parte ed essere certo di aver beccato i due byte.

Tre byte, un separatore serve anche in formato binario, anzi è ancor più necessario

Per quanto con un minimo di trattamento ci si sta anche con 2 byte, ma non è banale

Scusate l'ignoranza ma come li mando sotto forma di 2/3 byte?

docdoc:
Ma dato che ogni campione da inviare è un uint16 puoi mandarlo sotto forma di 2 byte invece che di 5 caratteri (più separatore più crlf), e a 115200 ci metti solo 0.1ms... Aggiungici un byte di start (diciamo 0xFF ad esempio) se vuoi semplificare il parsing dall'altra parte ed essere certo di aver beccato i due byte.

Non ci sto capendo molto..

Con una istruzione write

Mauro93:
Scusate l'ignoranza ma come li mando sotto forma di 2/3 byte? Non ci sto capendo molto..

Un unsigned int (a proposito, per sicurezza definiscilo così, non solo "int") è composto da 2 byte.
Quindi invece di fare:

   Serial.print(current);
   Serial.print(" ");

mandi 3 byte:

   uint8_t b1  = current >> 8;
   uint8_t b2 = current & 0x00FF;
   Serial.write(0xFF);  // Marker inizio blocco dati
   Serial.write(b1); // Byte più significativo (MSB)
   Serial.write(b2); // Byte meno significativo (LSB)

il primo Serial.write è un valore fisso (0xFF ossia 255 decimale) e ti servirà dall'altra parte per essere certo di leggere i dati nei due successivi byte;
il secondo Serial.write manda il byte più alto ossia prende current e sposta i bit a destra di 8 posizioni (tra l'altro equivale a dividere per 256...);
il terzo Serial.write manda il byte più basso ossia effettua un "and" con maschera 0x00FF quindi azzera il byte più alto che abbiamo già mandato.

Dall'altra parte attendi di ricevere 0xFF (255), e quando lo ricevi leggi i 2 byte successivi diciamo b1 e b2 e ricomponi il valore come:
v = b1*256 + b2

Ok docdoc, sto capendo meglio, ma per leggere b1 e b2 devo sempre effettuare il serial.print?
Questo è al momento lo sketch

#define SAMPLING_FREQUENCY 600
#define n_ripetizioni 1
unsigned int sampling_period_us;

void setup() {
  Serial.begin(115200);
    sampling_period_us = (1000000*(1.0/SAMPLING_FREQUENCY));
}
int current = 0;

void loop() {
unsigned long microseconds = micros();
for (int i = 0; i < n_ripetizioni*SAMPLING_FREQUENCY; i++) {
    while (micros() - microseconds >= sampling_period_us);
    current = analogRead(A1);
    microseconds += sampling_period_us;
    uint8_t b1  = current >> 8;
    uint8_t b2 = current & 0x00FF;
    Serial.write(0xFF);  // Marker inizio blocco dati
    Serial.write(b1); // Byte più significativo (MSB)
    Serial.write(b2); // Byte meno significativo (LSB)
}
while(1);
}

Due cose:
Micros richiede unsigned long, non unsigned int
Poi occorre considerare il caso che la lettura sia proprio 0xFF
A chi Trasmetti?

Mauro93:
Ok docdoc, sto capendo meglio, ma per leggere b1 e b2 devo sempre effettuare il serial.print?

Per leggere i due byte dall'altra parte intendi? Sicuramente non una "print", visto che questa scrive mentre tu dovresti leggere.
Ma dipende da "cosa" c'è dall'altra parte della seriale: chi è che deve acquisire questi dati che mandi via seriale? Un programma su PC oppure un altro Arduino (ma penso di no...)??

Una volta effettuato il campionamento devo portare questi dati su Exel. Con il mio primo sketch (in maniera bruta) copiavo e incollavo direttamente. Una volta su Exel, questi dati li devo rielaborare per fare una serie di operazioni.

Standardoil, con unsigned int (dal primo sketch) ottengo molti meno valori.

Enno, allora no
Non puoi facilmente importarli in Excel (ricordati la x...) se usi dati binari con un separatore qualsiasi
O esiste qualche macro di Excel già pronta?
Mi interesserebbe....

Questa riga

while (micros() - microseconds >= sampling_period_us);

secondo me deve diventare

while (micros() - microseconds < sampling_period_us);

perché altrimenti non aspetta che la differenza tra l'attuale micros() e il precedente sia trascorsa anzi l'esatto opposto, la prima si uca solitamente negli if per determinare se è il momento di fare una certa cosa, tu invece vuoi aspettare finché non è trascorso il tempo