Go Down

Topic: Campionamento segnale analogico Arduino (Read 638 times) previous topic - next topic

Mauro93

May 20, 2019, 12:07 pm Last Edit: May 20, 2019, 12:50 pm by Mauro93
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.

Code: [Select]
#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.

Standardoil

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
Prima legge di Nelson (che sono io): fai le stesse cose sempre alla stessa maniera, quegli errori li hai già corretti

Non bado a studenti, che vadano a copiare da un'altra parte

Se io ti domando e tu non mi rispondi vuol dire che non ti serve più

fabpolli

#2
May 20, 2019, 12:23 pm Last Edit: May 20, 2019, 12:32 pm by fabpolli
 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:
Code: [Select]

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

diventa
Code: [Select]

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

Mauro93

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:
Code: [Select]

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

Il problema è che adesso trovo meno di 100 campioni.
Ritornando alla riga:
Code: [Select]

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?


zoomx

Sali a 115200, perchè limitare la velocità?

Standardoil

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
Prima legge di Nelson (che sono io): fai le stesse cose sempre alla stessa maniera, quegli errori li hai già corretti

Non bado a studenti, che vadano a copiare da un'altra parte

Se io ti domando e tu non mi rispondi vuol dire che non ti serve più

fabpolli

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

zoomx

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.

docdoc

#8
May 20, 2019, 02:17 pm Last Edit: May 20, 2019, 02:18 pm by docdoc
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.
Alex "docdoc" - ** se ti sono stato d'aiuto, un punto karma sarà gradito, clicca su "add" qui a sinistra, vicino al mio nome ;) **

Standardoil

Tre byte, un separatore serve anche in formato binario, anzi è ancor più necessario
Prima legge di Nelson (che sono io): fai le stesse cose sempre alla stessa maniera, quegli errori li hai già corretti

Non bado a studenti, che vadano a copiare da un'altra parte

Se io ti domando e tu non mi rispondi vuol dire che non ti serve più

Standardoil

Per quanto con un minimo di trattamento ci si sta anche con 2 byte, ma non è banale
Prima legge di Nelson (che sono io): fai le stesse cose sempre alla stessa maniera, quegli errori li hai già corretti

Non bado a studenti, che vadano a copiare da un'altra parte

Se io ti domando e tu non mi rispondi vuol dire che non ti serve più

Mauro93

Scusate l'ignoranza ma come li mando sotto forma di 2/3 byte?
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..

Standardoil

Prima legge di Nelson (che sono io): fai le stesse cose sempre alla stessa maniera, quegli errori li hai già corretti

Non bado a studenti, che vadano a copiare da un'altra parte

Se io ti domando e tu non mi rispondi vuol dire che non ti serve più

docdoc

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:

Code: [Select]
   Serial.print(current);
   Serial.print(" ");


mandi 3 byte:

Code: [Select]
   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
Alex "docdoc" - ** se ti sono stato d'aiuto, un punto karma sarà gradito, clicca su "add" qui a sinistra, vicino al mio nome ;) **

Mauro93

#14
May 20, 2019, 04:07 pm Last Edit: May 20, 2019, 04:08 pm by Mauro93
Ok docdoc, sto capendo meglio, ma per leggere b1 e b2 devo sempre effettuare il serial.print?
Questo è al momento lo sketch
Code: [Select]
#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);
}

Go Up