Speeding up the reading of several analog sensors working simultaneously

Hello everyone,

I am curently working on a student project to study vibrations on 3 differents analog sensors (piezoelectric sensors).

I have to store the data they send me back on a SD card plugged on my arduino uno. The problem is that I must have a quite high sampling frequency (around 1000Hz would be a good beginning but faster would be even better) but I can't reach such a frequency.

I did my best to optimize my code but I am barely reaching a frequency of 55Hz (I got values every 17-18ms). Here is my code :

#include <SD.h>
#include <SPI.h>

File myFileA1;

Sd2Card card;
SdVolume volume;
SdFile root;


int capteur1=0;
int capteur2=0;
int capteur3=0;

int pin1=A1;
int pin2=A2;
int pin3=A3;

String chaine;

unsigned long temps;

const int chipSelect = 10;

void setup() {

Serial.begin(9600);

while (!Serial) {
    ; 
  }

if(!SD.begin(10))
{
  Serial.println("L'initialisation de la carte SD a echoue");
  return;
}

  Serial.println("l'initialisation de la carte SD a réussi");

  SD.remove("1.csv"); //suppresion du dernier fichier

  myFileA1=SD.open("1.csv",FILE_WRITE);
  myFileA1.print("Temps");
  myFileA1.print(";");
  myFileA1.print("Capteur1");
  myFileA1.print(";");
  myFileA1.print("Capteur2");
  myFileA1.print(";");
  myFileA1.println("Capteur 3");
  myFileA1.close();
}


void loop() {
  
    myFileA1=SD.open("1.csv",FILE_WRITE);
    
    chaine=String(millis());
    chaine+=";";
    chaine+=String(analogRead(pin1));
    chaine+=";";
    chaine+=String(analogRead(pin2));
    chaine+=";";
    chaine+=String(analogRead(pin3));

    myFileA1.println(chaine);
    //Serial.println(chaine);
    
    myFileA1.close();
}

Would you see a method to speed up these value readings and save time between each readings? Or should I use an arduino for each sensor or maybe change the card or the way the data are stored?
I also join the file I get at the end of my test : 1.xlsx - Google Drive

Thank you in advance for your help.

I think that the opening and closing of the file is where you're losing time.

Thank you for your fast answer.

How could I change that ? Can I just open the file in the void setup() and the file will be left opened for the rest of the program ?

Something like that ?

#include <SD.h>
#include <SPI.h>

File myFileA1;

Sd2Card card;
SdVolume volume;
SdFile root;


int capteur1=0;
int capteur2=0;
int capteur3=0;

int pin1=A1;
int pin2=A2;
int pin3=A3;

String chaine;

unsigned long temps;

const int chipSelect = 10;

void setup() {

Serial.begin(9600);

while (!Serial) {
    ; 
  }

if(!SD.begin(10))
{
  Serial.println("L'initialisation de la carte SD a echoue");
  return;
}

  Serial.println("l'initialisation de la carte SD a réussi");

  SD.remove("1.csv"); //suppresion du dernier fichier

  myFileA1=SD.open("1.csv",FILE_WRITE);
  myFileA1.print("Temps");
  myFileA1.print(";");
  myFileA1.print("Capteur1");
  myFileA1.print(";");
  myFileA1.print("Capteur2");
  myFileA1.print(";");
  myFileA1.println("Capteur 3");
}


void loop() {
    
    chaine=String(millis());
    chaine+=";";
    chaine+=String(analogRead(pin1));
    chaine+=";";
    chaine+=String(analogRead(pin2));
    chaine+=";";
    chaine+=String(analogRead(pin3));

    myFileA1.println(chaine);
    //Serial.println(chaine);
}

If you really need to push things, then don't do the string conversion.
Just store them as binary values, you can restore them later when you are going to use the readings.

Your data storage format and use of the String class isn't helping either. I'd be tempted to store the data as binary records: Three binary uint16_t values for each record. That's only 6 bytes per record. Then, pretty it up later in a non-real time post-processing stage.

EDIT:
@LightuC was quicker.

I'd try putting the open in setup, then try reading continuously for, say, 20 seconds and then do the close.

Hi everyone,

I have listened to all your advice and modified my program accordingly :

#include <SD.h>
#include <SPI.h>

File myFileA1;

Sd2Card card;
SdVolume volume;
SdFile root;

int pin1=A1; //x5
int pin2=A2; //x2
int pin3=A3; //pas amplifié

String chaine;

unsigned long temps;

const int chipSelect = 10;

void setup() {

Serial.begin(9600);

while (!Serial) {
    ; 
  }

if(!SD.begin(10))
{
  Serial.println("L'initialisation de la carte SD a echoue");
  return;
}

  Serial.println("l'initialisation de la carte SD a réussi");

  SD.remove("1.csv"); //suppresion du dernier fichier

  myFileA1=SD.open("1.csv",FILE_WRITE);
  myFileA1.print("Temps");
  myFileA1.print(";");
  myFileA1.print("Capteur1");
  myFileA1.print(";");
  myFileA1.print("Capteur2");
  myFileA1.print(";");
  myFileA1.println("Capteur 3");
  myFileA1.close();

  myFileA1=SD.open("1.csv",FILE_WRITE); // file is opened for the loop
}

void loop () {
  
  myFileA1.print(millis());
  myFileA1.print(";");
  myFileA1.print(analogRead(pin1));
  myFileA1.print(";");
  myFileA1.print(analogRead(pin2));
  myFileA1.print(";");
  myFileA1.println(analogRead(pin3));

}

The problem is that when I don't open and close my file in my loop, the program doesn't write any data to my file which is pretty annoying.

mMoreover, would you know how to avoid writing MyFileA1.print(...) several times and do it all at once ?

Thanks for your help :slight_smile:

I'd try putting the open in setup, then try reading continuously for, say, 20 seconds and then do the close.

Ok great ! This worked for me !

void loop () {
  
  myFile=SD.open("1.csv",FILE_WRITE);
  
  while(millis()<10000){
    myFile.print(millis());
    myFile.print(";");
    myFile.print(analogRead(pin1));
    myFile.print(";");
    myFile.print(analogRead(pin2));
    myFile.print(";");
    myFile.println(analogRead(pin3));
  }
  
  myFile.close();

  Serial.println("End of the loop");
  
  exit(0);
}

One thing I've noticied is that the function Serial.println("..."); takes a lot of time (around 5 ms). With this code, I take my values every millisecond.
I saw that we could make up to 10,000 value readings per second. Do you have any ideas to further optimize my code? I would also need a more precise function than millis() to know the time. Is there one?

See reply #3

I would also need a more precise function than millis() to know the time. Is there one?

Finer granularity would be micros()

LouisSnz:
One thing I've noticied is that the function Serial.println("..."); takes a lot of time (around 5 ms). With this code, I take my values every millisecond.
I saw that we could make up to 10,000 value readings per second. Do you have any ideas to further optimize my code?

It was suggested in both Reply #3 and Reply #4 to write the values as binary to the file. You ignored those suggestions.

I don't really understant, isn't it what I have done ? I only use the function print and do not convert the values I get. How could I do otherwise?

You could write the binary values directly, without semicolons or newlines.
You know how many you write each time, and they'll be fixed length, two bytes per reading.

I only use the function print and do not convert the values

print() converts the int to a string and write()s the string.

write() does no such conversion.

Hello, I'm back, I didn't have enough time to work on my project.

I tried to use the write() function but in my excel file, I didn't have readable values. I suppose I should convert the binary values into strings but I don't know how to do that and if it should be in the program or on excel.

Moreover, I have to separate my values so I must use the " myFile.print(";"); " function (or am I missing something ?). Won't it slow down the execution ?

I never worked with binary values so please lenient :smiley:

Moreover, I have to separate my values so I must use the " myFile.print(";");

No,if you write binary, all the entries will be of the same length, so no need for separators.

Think of your problem as having two phases:
first, data collection,
second, data formatting and transmission.

I tried to use the write() function but in my excel file, I didn't have readable values.

Of course not. Excel doesn't do binary input via the serial port.

Won't it slow down the execution ?

I'd suggest that writing to excel is going to slow down execution, period. If you want a responsive program, have some other app running on the PC, taking binary data in and converting it to a csv file that Excel can read. Otherwise, some expectation adjustments are in order.

PaulS:
If you want a responsive program, have some other app running on the PC, taking binary data in and converting it to a csv file that Excel can read.

The arduino will have to work on its own. I store all my data on a SD Card so I won't be able to use an other app.

Are they other ways to store values on a SD Card without using a Excel file ?

AWOL:
Think of your problem as having two phases:
first, data collection,
second, data formatting and transmission.

When you say "data formatting", you mean that I should convert my data at the end of the program ? How could I do that ?

Are they other ways to store values on a SD Card without using a Excel file ?

The Arduino can't create an Excel file, so, obviously there other ways.

PaulS:
The Arduino can't create an Excel file, so, obviously there other ways.

When I'm doing that :

  myFile=SD.open("1.csv",FILE_WRITE);

I create a file that excel can read, don't I ?