DataLogger for 4 analog Inputs: code optimization

G'day all,

I am looking to utilize a teensy 2.0 to eventually act as a controller for a control system that is required to take for analog inputs and drive a mirror depending on the values of these inputs.

In order to get the first stage working and get some results I am trying to implement some code that samples the 4 analog inputs as fast as possible (ideally up to 20kHz to satisfy the minimum desired Nyquist rate for a 10 kHz signal). I want to write this to an SD card I have attached through an SD adapter in order to be able to analyse to the results. My initial attempt runs at an embarrassingly slow rate of approx 30 Hz.

After writing some code to test the speed of the various functions it was obvious the File open procedure took ages so i am trying to implement code without this in every loop as provided in the datalogger sample that comes with the Arduino library.

my code is below:

#include <SD.h>
//  Included to match SD shield or module: Teensy 2.0 utilised therefore pin 0
const int chipSelect = 0;
const int ledPin = 11;
//  Boolean to allow for one iteration
boolean done = false;
char fileName[] = "QC_out.txt";
//  Time variables
long timeStart;
long timeTotal;

void setup()
{
  //  Setup for the LED
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
  
  //  Code taken from provided datalogger example
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  if (!SD.begin(chipSelect)) 
  {
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialized.");
  
  //  Checking for any pre existing file and deleting if found
  Serial.println("Checking if file already exists");
  if (SD.exists(fileName)) {
    SD.remove(fileName);
    Serial.println("File found and deleted");
  }
  else {
    Serial.println("No file found");
  }
  
  //  Write the informaiton into the top lines of the file for reference
  File dataFile = SD.open(fileName, FILE_WRITE);
  if (dataFile) {
    dataFile.println("Results for the testing");
    dataFile.println("Author: This guy");
    dataFile.println("");
    dataFile.close();
    Serial.println("Header Written to File");
  }
  else {
    Serial.println("Error in opening the file");
  }  
  timeStart = micros();
}

void loop() {
  //  Open the file to write the info to
  File dataFile = SD.open(fileName, O_CREAT|O_APPEND|O_WRITE);
  if(timeTotal<10000000) {
    String data = "";
    
    //  This sequence reads the values on the analog input pins and
    //  adds them straight to the string
    for (int analogPin = 21; analogPin >17; analogPin--) {
      int sensor = analogRead(analogPin);
      data += String(sensor);
      if (analogPin>18) {
        data +=",";
      }  
    }
    //  write results to file
    if (dataFile) {
      dataFile.println(data);
      dataFile.flush();
    }
    else {
      Serial.println("error in opening the file");
    }
  //  Timing for loop  
  timeTotal=micros()-timeStart;
  Serial.println(timeTotal);
  }  
  else {
    if(!done) {
      dataFile.close();
      Serial.println("Data acquisition complete");
      done = true;
      digitalWrite(ledPin, LOW);
    }
  }
}

The problem occuring is that it stops being able to access the file after about 1 second resulting in the serial out showing the ("error in opening file") message. i have an inkling it might be a memory issue but am unsure...

My initial attempt runs at an embarrassingly slow rate of approx 30 Hz.

I'm not surprised. You are doing so many things wrong.

First, the way to write to an SD card as fast as possible is to open the file ONCE, write all the data needed, and close the file ONCE.

Second, the File class derives from Print, which knows how to convert ints and floats to strings, as it writes to the file. Converting all the values to strings first, and appending them to a String, and then unpacking the String to write to the file is way too slow. Knock that crap off.

After writing some code to test the speed of the various functions it was obvious the File open procedure took ages so i am trying to implement code without this in every loop as provided in the datalogger sample that comes with the Arduino library.

But, you posted some other code. Well, fine. Forget I tried to help.

PaulS,

Thanks for the heads up. I have done as you said and cut the conversion to string away.

But, you posted some other code. Well, fine. Forget I tried to help.

The code I posted I tried to implement this, as the fileopen operation was outside the loop where the data was getting written to file.

I have implemented your suggestion, However it is still quite slow. I moved the file open operation to the setup loop and set it to run for 10 seconds. The reason for the temp variable is that the write() function requires to be passed a byte, char or string. Im trying to store as binary data so I'm trying to get all the information (the first 8 bits were being lost when the anlogread is passed straight to the write function)

#include <SD.h>
//---------------------Constant variables---------------------
const int chipSelect = 0;          //  Must be set to 0 for SPI communication
const int ledPin = 11;             //  LED pin to indicate when measurements are completed


//---------------------Changing variables---------------------
boolean done = false;              //  Controls the while loop
byte out_bytes[2];                 //  Array for converting int16 to 2 bytes for file.write
char fileName[11]="Result.bin";    //  filename for save file
int analogPin = A0;                //  Starting Analog pin
int cycles=0;                        //count the number of cycles
int temp;                          //  temp int to store analogRead
File output;                       //  File object to write results to
unsigned long timeStart;           //  Time varibles to measure time
unsigned long timeFinal;
unsigned long timeTotal;

//---------------------Setup----------------------------------
void setup() {
  //Setup for the LED
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
  
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  Serial.print("Initializing SD card...");
  pinMode(chipSelect, OUTPUT);  
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialized.");
  
  //  Checking for any pre existing file and deleting if found
  Serial.println("Checking if file already exists");
  if (SD.exists(fileName)) {
    SD.remove(fileName);
    Serial.println("File found and deleted");
  }
  else {
    Serial.println("No file found");
  }
  
  //  Open the file for the data to be written to
  output = SD.open(fileName, O_CREAT|O_APPEND|O_WRITE);
}

//---------------------Main Loop-------------------------------
void loop() {
  //  Fixed to be able to read in to MATLAB as UNIT16
  //  file.write() function only accepts bytes, char or string
  //  hence the 16 bit number must be split into two bytes and 
  //  parsed as an array to the write function.
  //  In this implementation operates at approx 
  timeStart = micros();
  while(timeTotal<10000000) {
    for (analogPin = A0;analogPin>A4;analogPin--) {
        temp = analogRead(analogPin);
        out_bytes[0] = byte(temp);
        out_bytes[1] = byte(temp>>8);  
        output.write(out_bytes,2);
        output.flush();
    }
    timeTotal = micros()-timeStart;
  }
    output.close();
  if(!done) {
    done = true;
    digitalWrite(ledPin,LOW);
    Serial.print("time taken: ");
    Serial.println(timeTotal/1000000);
    Serial.print("cycles performed: ");
    Serial.println(cycles);
    Serial.print("Speed of system: ");
    Serial.print(float(cycles)/(timeTotal/1000000));
    Serial.println("Hz");    
  }
}

EDITED: code was for 100 second cycle from test I ran last night

    for (analogPin = A0;analogPin>A4;analogPin--) {

Starting at 14, while the value is greater than 18, execute the body of the loop and decrement the value.

How many times will that loop actually iterate?

for (analogPin = A0;analogPin>A4;analogPin--) {

I was under the impression from the Teensy2 datasheet that A0 was pin 21 and A4 is pin 17. So the intention is for the loop to iterate 4 times for the 4 analog inputs.

Is this not the case?

I was under the impression from the Teensy2 datasheet that A0 was pin 21 and A4 is pin 17.

I missed the fact that you were using a Teensy. I can't believe that the analog pin to digital pin mapping is backwards like that, though.

Update:

removing the flush function from the loop speeds the program up from approx 20 Hz -> 1.8kHz.
I was incorrectly working under the assumption that the write function required a flush after writing if the close function wasn't called.

It's the little things that'll get you