Teensy 3.1: ADC Measurement, SD logging, problem with Sample Rate?

Status
Not open for further replies.

Chopsticks

Active member
Hello!

I've been busy with measuring analog values. It's possible to measure the values, but somehow i don't know how to increase the sample rate, it's maybe a noob question, but i'm using a combination of the following libraries
- SDfat (for logging to SDcard)
- RTC
- Teensy Synchronous measurement

When i remove the 'WriteToSD' part, the samples are higher then when i use 'WriteToSD'. My first thought it has something to do with continuously opening and closing the file over and over again...
Or has it something to do with the averaging part which is used in the 'Teensy's synchronous measurement' example?
Does anyone had the same problem? Or know how to fix this to get higher sample rates. At the moment I have 50 samples/second.

My logfile:
View attachment ADC_syncMeasSDlogging.txt

My sourcecode:
Code:
/*
- Sensing two ADC values,
- Analog files being logged on sd-card
    Time:  A-B:  A:  B:
*/


//Including Libraries
#include <ADC.h>
#include <string.h>
#include <Time.h>  
#include <SdFat.h>
SdFat sd;
SdFile myFile;



//Define ADC Inputs
#define ADC_0 0
#define ADC_1 1



//Teensy 3.1 has the LED on pin 13
const int ledPin = 13;
const int readPin0 = A2;  //ADC read A2
const int readPin1 = A3;  //ADC read A3
// Ported to SdFat from the native Arduino SD library example by Bill Greiman
// On the Ethernet Shield, CS is pin 4. SdFat handles setting SS
const int chipSelect = SS;
//const int chipSelect = 10;  //CS(SS) select SD-Card



char filename[] = "00000000.txt";

ADC *adc = new ADC(); // adc object
ADC::Sync_result result;
int ADCvalue_0 = ADC_ERROR_VALUE;
int ADCvalue_1 = ADC_ERROR_VALUE;



void setup() {
  setSyncProvider(getTeensy3Time);
  pinMode(ledPin, OUTPUT);
  pinMode(readPin0, INPUT);  //Read analog signal pin1
  pinMode(readPin1, INPUT);  //Read analog signal pin2



  Serial.begin(9600);
  SetTime();
  
  //Set ADC (Averaging and Resolution)  
  adc->setAveraging(32, ADC_0); // set number of averages
  adc->setResolution(8), ADC_0; // set bits of resolution
  adc->setAveraging(32, ADC_1); // set number of averages
  adc->setResolution(8, ADC_1); // set bits of resolution
  // always call the compare functions after changing the resolution!
    //adc->enableCompare(1.0/3.3*adc->getMaxValue(), 0, ADC_0); // measurement will be ready if value < 1.0V
    //adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V

  
  while (!Serial) {}  // Wait for serial
  Serial.println("Type any character to start");
  while (Serial.read() <= 0) {}
  delay(400);
  
  
  
  
  // Initialize SdFat or print a detailed error message and halt
  // Use half speed like the native library.
  // change to SPI_FULL_SPEED for more performance.
  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt();

  getFileName();
  createFileName();  

}




void loop() {
  InitializeClock();
  WriteToSerial(); 
  WriteToSD();  
}


//Test sequence read from file
void readFile(){
  // re-open the file for reading:
  if (!myFile.open(filename, O_READ)) {
    sd.errorHalt("opening test.txt for read failed");
  }
  Serial.println("test.txt:");

  // read from the file until there's nothing else in it:
  int data;
  while ((data = myFile.read()) >= 0) Serial.write(data);
  // close the file:
  myFile.close();
}


//Test sequence write to file
void writeFile(){
  // open the file for write at end like the Native SD library
  if (!myFile.open(filename, O_RDWR | O_CREAT | O_AT_END)) {
    sd.errorHalt("opening filename for write failed");
  }
  // if the file opened okay, write to it:
  Serial.print("Writing to new filename");
  myFile.println("testing 1, 2, 3.");

  // close the file:
  myFile.close();
  Serial.println("done.");
}




void getFileName(){
filename[0] = (month()/10)%10 + '0';
filename[1] = month()%10 + '0';
filename[2] = (day()/10)%10 + '0';
filename[3] = day()%10 + '0';
filename[4] = (hour()/10)%10 + '0';
filename[5] = hour()%10 + '0';
filename[6] = (minute()/10)%10 + '0';
filename[7] = minute()%10 + '0';
/*
filename[0] = (year()/1000)%10 + '0'; //To get 1st digit from year()
filename[1] = (year()/100)%10 + '0'; //To get 2nd digit from year()
filename[2] = (year()/10)%10 + '0'; //To get 3rd digit from year()
filename[3] = year()%10 + '0'; //To get 4th digit from year()
filename[4] = (month()/10)%10 + '0'; //To get 1st digit from month()
filename[5] = month()%10 + '0'; //To get 2nd digit from month()
filename[6] = (day()/10)%10 + '0'; //To get 1st digit from day()
filename[7] = day()%10 + '0'; //To get 2nd digit from day()                      
*/
filename[8] = '.';
filename[9] = 't';
filename[10] = 'x';
filename[11] = 't';
Serial.println(filename);
}



void createFileName(){
//Check file name exist?
if (sd.exists(filename)) {
  Serial.println("exists.");
  }
else {
  Serial.println("Filename doesn't exist.");
  Serial.println("Creating a new file");
  Serial.println(filename);
  if (!myFile.open(filename, O_RDWR | O_CREAT | O_AT_END)) {
    sd.errorHalt("opening filename for write failed");
  }
    myFile.print("Date: ");
    myFile.print(day());
    myFile.print("-");
    myFile.print(month());
    myFile.print("-");
    myFile.print(year()); 
    myFile.print("\t");
    myFile.print(hour());
    printDigitsSD(minute());
    printDigitsSD(second());
      myFile.println("");
      myFile.print("Time:");
      myFile.print("\t");
      myFile.print("A-B:");
      myFile.print("\t");
      myFile.print("A:");
      myFile.print("\t");
      myFile.println("B:");
  myFile.close();
}
}


//Digital Clock for Serial
void digitalClockDisplay_WriteToSerial() {
  // digital clock display of the time
  Serial.print(day());
  Serial.print("-");
  Serial.print(month());
  Serial.print("-");
  Serial.print(year()); 
  Serial.print("\t");
  Serial.print(hour());
  printDigitsSerial(minute());
  printDigitsSerial(second());
  Serial.print(",");
  Serial.print(millis());
  //Serial.println("\t"); 
}

//Digital Clock for SD Card
void digitalClockDisplay_WriteToSD() {
  // digital clock display of the time
  myFile.print(hour());
  printDigitsSD(minute());
  printDigitsSD(second());
  myFile.print(",");
  myFile.print(millis());
 //Serial.println("\t"); 
}

//Print digits for Serial
void printDigitsSerial(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

//Print digits for SD
void printDigitsSD(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  myFile.print(":");
  if(digits < 10)
    myFile.print('0');
  myFile.print(digits);
}




//Setting Time
void SetTime(){
  //Set Time
  if (timeStatus()!= timeSet) {
    Serial.println("Unable to sync with the RTC");
  } else {
    Serial.println("RTC has set the system time");
  }
}



//Initializing RTC
void InitializeClock() {
  if (Serial.available()) {
     time_t t = processSyncMessage();
     if (t != 0) {
     Teensy3Clock.set(t); // set the RTC
     setTime(t);
     }
  }
}  


//Set RTC Time
time_t getTeensy3Time()
{
  return Teensy3Clock.get();
}

/*  code to process time sync messages from the serial port   */
#define TIME_HEADER  "T"   // Header tag for serial time sync message

unsigned long processSyncMessage() {
  unsigned long pctime = 0L;
  const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 

  if(Serial.find(TIME_HEADER)) {
     pctime = Serial.parseInt();
     return pctime;
     if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
       pctime = 0L; // return 0 to indicate that the time is not valid
     }
  }
  return pctime;
}
// END Set RTC Time



//Write ADC value to serial monitor
void WriteToSerial() {
   if( (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE) ) {
    //ADC values   
      result = adc->analogSynchronizedRead(readPin0, readPin1);
      float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
      float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue(); 
      
        Serial.print("Time: ");
        digitalClockDisplay_WriteToSerial();
        Serial.print("\tDiff(ADC0-ADC1): ");
        Serial.print(ADCvalue_0-ADCvalue_1, DEC);  
        Serial.print("\t");  
        Serial.print("A2(ADC0): ");  
        Serial.print(ADCvalue_0, DEC);
        Serial.print("\t");  
        Serial.print("A3(ADC1): ");
        Serial.println(ADCvalue_1, DEC); 
  } else {
      Serial.println("Error 2: Comparison failed");
  }    
}

//Write ADC value to sdcard
void WriteToSD() {
  if (!myFile.open(filename, O_RDWR | O_CREAT | O_AT_END)) {
    sd.errorHalt("opening filename for write failed");
  }
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  
   if( (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE) ) {
    //ADC values   
      result = adc->analogSynchronizedRead(readPin0, readPin1);
      float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
      float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue(); 
  
      digitalClockDisplay_WriteToSD();
      myFile.print("\t");
      myFile.print(ADCvalue_0-ADCvalue_1, DEC);  
      myFile.print("\t");
      myFile.print(ADCvalue_0, DEC);
      myFile.print("\t");
      myFile.println(ADCvalue_1, DEC);

      //close the file:
      myFile.close();
      //Serial.println("done.");*

     } else {
     // if the file didn't open, print an error:
      Serial.println("Error 1: Can't open file!");
     }  
}
Code:
 
Last edited:
It's a bit weird that you do:
Code:
if( (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE) ) {
    //ADC values   
      result = adc->analogSynchronizedRead(readPin0, readPin1);
      float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
      float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue(); 
...

Why do you check result before actually reading it?

When i remove the 'WriteToSD' part, the samples are higher then when i use 'WriteToSD'.

Well, both in WriteToSD and WriteToSerial you are starting a new measurement, so it's logical that when you only have either it takes less time!
What you probably want is something like

Code:
loop() {
      result = adc->analogSynchronizedRead(readPin0, readPin1);
      float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
      float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue(); 
      ...
      WriteToSerial(ADCvalue_0, ADCvalue_1);
      WriteToSD(ADCvalue_0, ADCvalue_1);
}
Where the WritetoX functions take the values as parameters and print them, like:
Code:
//Write ADC value to serial monitor
void WriteToSerial(float ADCvalue_0, float ADCvalue_1) {
   if( (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE) ) {     
        Serial.print("Time: ");
        digitalClockDisplay_WriteToSerial();
        Serial.print("\tDiff(ADC0-ADC1): ");
        Serial.print(ADCvalue_0-ADCvalue_1, DEC);  
        Serial.print("\t");  
        Serial.print("A2(ADC0): ");  
        Serial.print(ADCvalue_0, DEC);
        Serial.print("\t");  
        Serial.print("A3(ADC1): ");
        Serial.println(ADCvalue_1, DEC); 
  } else {
      Serial.println("Error 2: Comparison failed");
  }    
}
 
I did change the analog read part the way you did, and so far it seems to work, although it still lacks on the sample rate(almost the same results as the attached log file).

I did indeed start two measurements, because i was using serial or SD. when using one of them, it only start one measurement at the time tho...
And i wasn't aware of the check before reading. That's also fixed by now.

I have added a 'for loop' in the adc measurement sequence:
Code:
void loop() {
  openFile();
    for (int samples=0; samples <= 1000; samples++){ 
      result = adc->analogSynchronizedRead(readPin0, readPin1);
      float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
      float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue();
      //WriteToSerial(ADCvalue_0, ADCvalue_1);
      WriteToSD(ADCvalue_0, ADCvalue_1);
    //delay(1);
    } 
  closeFile();
}

it seems i have 500-1000 samples/s now, but not the 1kS/s i want.... but when i remove the for loop, it reduces again to 50 samples/s. In some examples on the internet there are used rates like 100kS/s. I don't see how I can reach such sample rate like that...

With only WriteToSerial, i have much and much more samples, like 3kS/s

At this point i still have the following questions:
- At what frequency is the ADC measuring values?
- How to increase the sample rate without losing significant values?
 
Last edited:
I can only talk about the ADC stuff, but do you really need to open and close the file every loop iteration? That's going to take some time. Also, all the Serial stuff take time too, so that might be a limiting factor (you should research what's the maximum speed you can write to the SD card). What happens if you comment out the analogSynchronizedRead and just write a constant number to the card? How long does it take per iteration?

For the ADC the most important factors are in the setup:
Code:
adc->setAveraging(32, ADC_0); // set number of averages
  adc->setResolution(8), ADC_0; // set bits of resolution

You can try decreasing the number of averages, the noise will go up, of course, so it depends on the actual resolution you need.
There isn't really any other way of doing it with the internal ADC (except using the continuous mode simultaneously in both ADCs, I can try to push on github some kind of 'synchronizedContinuousRead' function in the evening, it should be slightly faster than starting readings manually, but not so much). You can find external ADCs with more resolution and/or faster, at a price of course.
 
Depending on the desired resolution, averaging, and the analog front end, your sampling rate can vary a great deal. You're sampling at 8 bits, so it should be very fast. Then there is the question of averages. How many samples are you trying to average for every reading. You can do this using the ADC library that Pedevide developed or use the code that Paul has previously published. At 32 samples per calculated average, you should still be sampling pretty quickly on account of the 8-bit resolution setting.

But there is a tradeoff that you should consider while averaging... averaging only really works so long as your sampling rate (with averaging) can properly capture the signal you're after (Mr. Nyquist says hi). I don't know enough about the signal you're after but the Teensy is capable of phenomenal speeds if the front end is set up for it.

Speaking of which, I hope you have carefully considered the inputs into the analog front end. Especially at high sampling frequencies, an op-amp may be required to act as a buffer with a very low source impedance for the ADC inside the Teensy to function well.

But as far as sampling rate, the bigger issue here is the use of the SD card on every sample. The teensy 3.1 has a big RAM buffer and you should use it. Create an array of 1000+ samples and only hit the SD card every time the buffer fills, squirt it all in one go, then return to sampling. Should be much faster than writing individual entries or using the Serial Monitor (which I would also limit).
 
Last edited:
You can't write to the SD one value at a time, that is just crazy. You need to make an array and then write it every so often. I recently posted some of my code in the original ADC thread, and I use the raw write from SDfat, which takes 200us each write and I write 512bytes at a time. SDfat has been able to get 400k/sec using this method, so I am sure you could get 1000sps.
 
Spot on. Getting the attention of an SD card takes time, as do writes, which is where arrays come in handy. They are easy to fill easy to write to SD card, etc.

FWIW, the power meter I've been working on achieves about 1.4ksps at 16 bits of resolution while sharing the SPI bus with the SD card. Past iterations of the meter even featured two atmega 328P's, one dedicated to sampling, the other to post-processing and writing the results. Using two CPUs made it a lot easier to keep sampling rates consistent regardless of I/O. The present solution works well though, as the Teensy 3.1 has a much higher clock rate and works natively with the external analog front end.
 
I don't know enough about the signal you're after but the Teensy is capable of phenomenal speeds if the front end is set up for it.
............
Speaking of which, I hope you have carefully considered the inputs into the analog front end. Especially at high sampling frequencies, an op-amp may be required to act as a buffer with a very low source impedance for the ADC inside the Teensy to function well.
Well, i'm using the following sensors: http://www.mouser.com/ProductDetail/Plessey-Semiconductors/PS25101/?qs=sGAEpiMZZMttKWgNLnZcJgErdsXOV7xh
These sensor measures the ECG values of a person. The output enters an gain amplifier (x1 or x10), the output is connected to a levelshifter which is connected to the ADC input.

Create an array of 1000+ samples and only hit the SD card every time the buffer fills, squirt it all in one go, then return to sampling.
That sounds reasonable, although, i have 4 different values/elements which needs to be measured in one row,
"Time", "A-B", "A", "B"

So i thought i need an array with 1 row and 4 columns? Is this possible? Something like a 2 dimensional array?
Code:
int myArray[1000];

//read
int i;
for (i = 0; i < 1000; i++) {
read(myArray[i]);
}

//write
int i;
for (j = 0; j < 1000; j++) {
myFile.println(myArray[i]);
}

When I have this thing working... do i also need to send these filled arrays one by one into the sd card?
I have some experience with arduino and programming, but my knowledge about using array isn't that great...



Also...
When using undernead code, i loop a measurement 50 times, in this case i open the file once, and close it after the 50 samples, although, when i check the log file, i see 600sps, and the other second 800samples. It's not constant, what could be the issue here?
Code:
void loop() {
  openFile();
    for (int i=0; i <= 50; i++){ 
      result = adc->analogSynchronizedRead(readPin0, readPin1);
      float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
      float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue();
      //WriteToSerial(ADCvalue_0, ADCvalue_1);
      WriteToSD(ADCvalue_0, ADCvalue_1);
    //delay(1);
    } 
  closeFile();
}
 
Last edited:
Haven't been able to fix it yet with the arrays, i believe something like this possible

Code:
int cols = 4;
int rows = 100;
int[][] myArray = new int[cols][rows];

// Two nested loops allow us to visit every spot in a 2D array. 
// For every column I, visit every row J.
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
myArray[i][j] = 0;
}
}

or maybe this is possible aswell?

Code:
int cols = 4;
int rows = 100;
int[][] myArray = new int[cols][rows];

for (int j = 0; j < rows; j++) {

myArray[0][j]= Time;
myArray[1][j]= A-B;
myArray[2][j]= A;
myArray[3][j]= B;

}
 
In this case, an array of structs would make the code clearer. To access a field, you'll use syntax like this:
Code:
     myArray[j].Time = Time;
     myArray[j].A_B = A - B;
and so on.

To define and declare the sample memory, first define the struct:

Code:
typedef struct {
     int Time;
     int A_B;
     int A;
     int B;
} sample_t;

sample_t myArray[100];

If you allocate the memory statically, not using 'new', the compiler will be able to warn you when you use more memory than the Teensy actually has, rather than crashing mysteriously at runtime.
 
I disagree with the above approach to some extent. You may only need to store three types of numbers in RAM.

Start time (get via RTC and / or mills ())
End time (see above)
An array of A and B

The rest of the data can be written to a data file on the fly, i.e. if you know you have 600 measurements and 10 seconds elapsed, then you can time slice 60 samples per second in code, for example. Similarly, A-B is a superfluous number if you have A and B already in an array. Yes, you can write it to SD card for convenience sake, but there is no good reason to store it in RAM.

Why all this trouble? Well, the more data points you can store in RAM, the fewer times a second you have to hit the SD card, which should improve your sampling rate accordingly.
 
Last edited:
The rest of the data can be written to a data file on the fly, i.e. if you know you have 600 measurements and 10 seconds elapsed, then you can time slice 60 samples per second in code, for example. Similarly, A-B is a superfluous number if you have A and B already in an array. Yes, you can write it to SD card for convenience sake, but there is no good reason to store it in RAM.
I've been busy for a while now, and i got the array principle working. it isn't stable yet tho, i have samples between 1 and 2k samples/second. Does anyone have an idea how to get a stable measurement where i can get like 250 samples / second?

By the way... it is not possible to put a void sequence in a array? When i think of it, the void digitalClockDisplay_WriteToSD() sequence has severel components, i quess i need to make an array as well for these clock values?. When i write the array to the sd card, i first send the clock to the sd card, and then the array, so in my opinion i'm not using the measured time between values, but the time on how vast each value is written on the sd card....
Anyone has an idea how this can easily fixed?

sourcecode:
Code:
/*
ECG measurement Project:
- Sensing two ADC values,
- Analog files being logged on sd card
    Time  A-B  A  B
*/

//Including Libraries
#include <ADC.h>
#include <string.h>
#include <Time.h>  
#include <SdFat.h>
SdFat sd;
SdFile myFile;

//Define ADC Inputs
#define ADC_0 0
#define ADC_1 1

//Teensy 3.1 has the LED on pin 13
const int ledPin = 13;
const int readPin0 = A2;  //ADC read A2
const int readPin1 = A3;  //ADC read A3
const int chipSelect = SS;
char filename[] = "00000000.txt";

ADC *adc = new ADC(); // adc object
ADC::Sync_result result;
int ADCvalue_0 = ADC_ERROR_VALUE;
int ADCvalue_1 = ADC_ERROR_VALUE;

int rows = 250; //rows of the matrix (max 3000 )
int cols = 3; //colums of the matrix
float myArray [250][3] = {}; //2d matrix array

void setup() {
  setSyncProvider(getTeensy3Time);
  pinMode(ledPin, OUTPUT);
  pinMode(readPin0, INPUT);  //Read analog signal pin1
  pinMode(readPin1, INPUT);  //Read analog signal pin2
 // analogReference(INTERNAL);

  Serial.begin(115200);
  SetTime();
  InitializeClock();
  
  //Set ADC (Averaging and Resolution)  
  adc->setAveraging(32, ADC_0); // set number of averages
  adc->setResolution(12, ADC_0); // set bits of resolution
  adc->setAveraging(32, ADC_1); // set number of averages
  adc->setResolution(12, ADC_1); // set bits of resolution
  // always call the compare functions after changing the resolution!
    //adc->enableCompare(1.0/3.3*adc->getMaxValue(), 0, ADC_0); // measurement will be ready if value < 1.0V
    //adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V
  
  while (!Serial) {}  // Wait for serial
  Serial.println("Type any character to start");
  while (Serial.read() <= 0) {}
  delay(400);
    
  // Initialize SdFat or print a detailed error message and halt
  // Use half speed like the native library.
  // change to SPI_FULL_SPEED for more performance.
  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt();

  getFileName();
  createFileName();  
}

void loop() {
   openFile();
      for (int n = 0; n < rows; n++) { 
    //for (int i=0; i <= 10000; i++){ 

      result = adc->analogSynchronizedRead(readPin0, readPin1);
      float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
      float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue();

        myArray[n][0] = ADCvalue_0-ADCvalue_1; //time
        myArray[n][1] = ADCvalue_0; //A-B
        myArray[n][2] = ADCvalue_1; //A

            } 
      WriteToSerial(ADCvalue_0, ADCvalue_1);
      WriteToSD(ADCvalue_0, ADCvalue_1);
        //} 
    //delay(1);
 
       for (int i = 0; i < rows; i++) {
 //       WriteArrayToSerial(i);
        WriteArrayToSD(i);
        }
        
       // Serial.println("");
       // myFile.println("");

        //delay(200); 

  closeFile();
}

//Generate filename with timestamp
void getFileName(){
filename[0] = (month()/10)%10 + '0'; //To get 1st digit from year()      '2';
filename[1] = month()%10 + '0'; //To get 2nd digit from year()      '0';
filename[2] = (day()/10)%10 + '0'; //To get 3rd digit from year()      '1';
filename[3] = day()%10 + '0'; //To get 4th digit from year()      '4';
filename[4] = (hour()/10)%10 + '0'; //To get 1st digit from month()      '0';
filename[5] = hour()%10 + '0'; //To get 2nd digit from month()      '5';
filename[6] = (minute()/10)%10 + '0'; //To get 1st digit from day()      '1';
filename[7] = minute()%10 + '0'; //To get 2nd digit from day()      '6';
/*
filename[0] = (year()/1000)%10 + '0'; //To get 1st digit from year()      '2';
filename[1] = (year()/100)%10 + '0'; //To get 2nd digit from year()      '0';
filename[2] = (year()/10)%10 + '0'; //To get 3rd digit from year()      '1';
filename[3] = year()%10 + '0'; //To get 4th digit from year()      '4';
*/
filename[8] = '.';
filename[9] = 't';
filename[10] = 'x';
filename[11] = 't';
Serial.println(filename);
}

//Creating a file with generated filename
void createFileName(){
//Check file name exist?
if (sd.exists(filename)) {
  Serial.println("exists.");
  }
else {
  Serial.println("Filename doesn't exist.");
  Serial.println("Creating a new file");
  Serial.println(filename);
  if (!myFile.open(filename, O_RDWR | O_CREAT | O_AT_END)) {
    sd.errorHalt("opening filename for write failed");
  }
    myFile.print("Date: ");
    myFile.print(day());
    myFile.print("-");
    myFile.print(month());
    myFile.print("-");
    myFile.print(year()); 
    myFile.print("\t");
    myFile.print(hour());
    printDigitsSD(minute());
    printDigitsSD(second());
      myFile.println("");
      myFile.print("Time:");
      myFile.print("\t");
      myFile.print("A-B:");
      myFile.print("\t");
      myFile.print("A:");
      myFile.print("\t");
      myFile.println("B:");
  myFile.close();
}
}

//Digital Clock for Serial
void digitalClockDisplay_WriteToSerial() {
  // digital clock display of the time
  Serial.print(hour());
  printDigitsSerial(minute());
  printDigitsSerial(second());
  Serial.print(",");
  Serial.print(millis());
  //Serial.println("\t"); 
}

//Digital Clock for SD Card
void digitalClockDisplay_WriteToSD() {
  // digital clock display of the time
  myFile.print(hour());
  printDigitsSD(minute());
  printDigitsSD(second());
  myFile.print(",");
  myFile.print(millis());
 //Serial.println("\t"); 
}

//Print digits for Serial
void printDigitsSerial(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

//Print digits for SD
void printDigitsSD(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  myFile.print(":");
  if(digits < 10)
    myFile.print('0');
  myFile.print(digits);
}

//Setting Time
void SetTime(){
  //Set Time
  if (timeStatus()!= timeSet) {
    Serial.println("Unable to sync with the RTC");
  } else {
    Serial.println("RTC has set the system time");
  }
}

//Initializing RTC
void InitializeClock() {
  if (Serial.available()) {
     time_t t = processSyncMessage();
     if (t != 0) {
     Teensy3Clock.set(t); // set the RTC
     setTime(t);
     }
  }
}  

//Set RTC Time
time_t getTeensy3Time()
{
  return Teensy3Clock.get();
}

/*  code to process time sync messages from the serial port   */
#define TIME_HEADER  "T"   // Header tag for serial time sync message

unsigned long processSyncMessage() {
  unsigned long pctime = 0L;
  const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 

  if(Serial.find(TIME_HEADER)) {
     pctime = Serial.parseInt();
     return pctime;
     if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
       pctime = 0L; // return 0 to indicate that the time is not valid
     }
  }
  return pctime;
}
// END Set RTC Time



//Write ADC value to serial monitor
void WriteToSerial(float ADCvalue_0, float ADCvalue_1) {
   if( (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE) ) { 
        Serial.print("Time: ");
        digitalClockDisplay_WriteToSerial();
        Serial.print("\t"); 
        Serial.print("A-B: ");
        Serial.print(ADCvalue_0-ADCvalue_1, DEC);  
        Serial.print("\t");  
        Serial.print("A: ");  
        Serial.print(ADCvalue_0, DEC);
        Serial.print("\t");  
        Serial.print("B: ");
        Serial.println(ADCvalue_1, DEC); 
  } else {
      Serial.println("Error 2: Comparison failed");
  }    
}

//Write ADC value to sdcard
void WriteToSD(float ADCvalue_0, float ADCvalue_1) {
     if( (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE) ) {
      digitalClockDisplay_WriteToSD();
      myFile.print("\t");
      myFile.print(ADCvalue_0-ADCvalue_1, DEC);  
      myFile.print("\t");
      myFile.print(ADCvalue_0, DEC);
      myFile.print("\t");
      myFile.println(ADCvalue_1, DEC);
    } else {
     // if the file didn't open, print an error:
      Serial.println("Error 1: Can't open file!");
     } 
}

//Write ADC value to serial monitor
void WriteArrayToSerial(int i) {
        digitalClockDisplay_WriteToSerial();
        Serial.print("\t");
        Serial.print((myArray[i][0]),7);
        Serial.print("\t");
        Serial.print((myArray[i][1]),7);
        Serial.print("\t");
        Serial.println((myArray[i][2]),7);
    
}

//Write ADC value to sdcard
void WriteArrayToSD(int i) {
      digitalClockDisplay_WriteToSD();
        myFile.print("\t");
        myFile.print((myArray[i][0]),7);
        myFile.print("\t");
        myFile.print((myArray[i][1]),7);
        myFile.print("\t");
        myFile.println((myArray[i][2]),7);
}

//Opening generated file
void openFile(){
  if (!myFile.open(filename, O_RDWR | O_CREAT | O_AT_END)) {
        sd.errorHalt("opening filename for write failed");
  }
}

//Closing generated file
void closeFile(){
  myFile.close();
  //Serial.println("done.");*
}
 
Hi,

Couple of things i've done in the past to write quickly to the SD card is use the cache system that SDfat uses in his RawWrite example. It is very confusing at first, but once you get the hang of it, it becomes a great tool. For one of my data collection methods I am measure Hz output for every 10deg during a pedal revolution while someone is cycling, so every 16000-20000us I am reading upto 100 samples and then I write that to and SD card. So your goal of 250s/s should be pretty do able. From your code, it seems you want to timestamp each sample? Why is this, just to determine sample rate, or do you use it later for analysis? Most ADC cards I use for data collection just write the data at a set rate and then later I know if I sampled at 2kHz, I should have 2000 samples per second. If I was you, i'd set up an interval timer that just got millis every time it is called, along with using the ADC library sync function so you can read both A and B at the same time. You would then put these into an array and when your array hits a size of 512 bytes, all your values are 32bit, so 4 bytes per value, meaning every 128 samples you write it to the SD card. When I first started using the SDfat library, I was using your method, however the write latency was random and I was missing values, however using the cache system the write latency is always 200us +- 5us. Also, do you need to do the math of A-B now, or will you be doing post processing of your data. If you are, then will be very easy to do that math later. The only issue with the cache method is the fact it creates a binary file.....meaning you have to write a program to make it a csv file post processing. Sorry for the rambling, but have to get to work...I will try to post some code later that may help you.
 
Hi,

Couple of things i've done in the past to write quickly to the SD card is use the cache system that SDfat uses in his RawWrite example. It is very confusing at first, but once you get the hang of it, it becomes a great tool. For one of my data collection methods I am measure Hz output for every 10deg during a pedal revolution while someone is cycling, so every 16000-20000us I am reading upto 100 samples and then I write that to and SD card. So your goal of 250s/s should be pretty do able. From your code, it seems you want to timestamp each sample? Why is this, just to determine sample rate, or do you use it later for analysis? Most ADC cards I use for data collection just write the data at a set rate and then later I know if I sampled at 2kHz, I should have 2000 samples per second. If I was you, i'd set up an interval timer that just got millis every time it is called, along with using the ADC library sync function so you can read both A and B at the same time. You would then put these into an array and when your array hits a size of 512 bytes, all your values are 32bit, so 4 bytes per value, meaning every 128 samples you write it to the SD card. When I first started using the SDfat library, I was using your method, however the write latency was random and I was missing values, however using the cache system the write latency is always 200us +- 5us. Also, do you need to do the math of A-B now, or will you be doing post processing of your data. If you are, then will be very easy to do that math later. The only issue with the cache method is the fact it creates a binary file.....meaning you have to write a program to make it a csv file post processing. Sorry for the rambling, but have to get to work...I will try to post some code later that may help you.


I'm looking forward for some code of yours, i'm continuing my work tomorrow, have to work right now. :)
I'll check your post tomorrow i quess :)

Thanks in advance!
 
Here is my quick attempt at making it work. I have removed a lot of your stuff. I am not sure how you want to name your files, but this program will timestamp your file, but I am sure it would be easy to implement you file naming system if you want to keep using the date/time. I was having issues with the conversion, sometimes it would work great,sometimes not. I was just reading in the 3.3V from the teensy and a 1.5V signal from a voltage divider. I am using the newest ADC library, so maybe there is an issue, not sure. I am sure there are way better methods than what I did, but seems to work for me. I am also not sure how you want to start/stop collection, so I just put in a serial command to stop/start. This might give you some ideas and there is a lot of improvements to be had (I have no programming background, just stumble my way through other peoples code).

Code:
/*
ECG measurement Project:
- Sensing two ADC values,
- Analog files being logged on sd card
    Time  A-B  A  B
*/

//Including Libraries
#include <ADC.h>
#include <string.h>
#include <Time.h>  
#include <SdFat.h>
#include <SdFatUtil.h>
SdFat sd;
SdFile myFile;

//Define ADC Inputs
#define ADC_0 0
#define ADC_1 1

//Teensy 3.1 has the LED on pin 13
const int ledPin = 13;
const int readPin0 = A2;  //ADC read A2
const int readPin1 = A3;  //ADC read A3
const int chipSelect = SS;
//char filename[] = "00000000.txt";
#define SD_CHIP_SELECT  10  // SD chip select pin
#define error(s) sd.errorHalt_P(PSTR(s))
#define FILE_BASE_NAME "LOG" //Can be max size of 5 character, XXXXX000.BIN DOS 8.3 format
#define TMP_FILE_NAME "TMP_LOG.BIN"
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
const uint32_t FILE_BLOCK_COUNT = 500UL; //500=250kb 1 block=512bytes
const uint32_t ERASE_SIZE = 262144L;
uint32_t bgnBlock, endBlock;
uint8_t*  pCache;
char binName[13] = FILE_BASE_NAME "000.BIN";

ADC *adc = new ADC(); // adc object
ADC::Sync_result result;
//int ADCvalue_0 = ADC_ERROR_VALUE;
//int ADCvalue_1 = ADC_ERROR_VALUE;
float myArray [250][3] = {}; //2d matrix array
int SDBuffer [128];
int count = 0;
uint32_t bn = 1; //block number
int value0;
int value1;

//Interval Timer
IntervalTimer timer0;
char c=0;

//------------------------------------------------------------------------------
/*
 * User provided date time callback function.
 * See SdFile::dateTimeCallback() for usage.
 */
void dateTime(uint16_t* date, uint16_t* time) {
  // User gets date and time from GPS or real-time
  // clock in real callback function
  time_t now();
  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(year(), month(), day());

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(hour(), minute(), second());
}
void createFile(){

  bn=1; //reset block count
  count=0;
  // Find unused file name.
  if (BASE_NAME_SIZE > 5) {
    //sendError(3,1);
  }
  while (sd.exists(binName)) {
    if (binName[BASE_NAME_SIZE + 2] != '9') {
      binName[BASE_NAME_SIZE + 2]++; //changed from +1 since now 0-999 instead of 0-99
    } 
    else {
      binName[BASE_NAME_SIZE + 2] = '0';
      //binName[BASE_NAME_SIZE + 1]++;
      if (binName[BASE_NAME_SIZE+1] == '9') {
        binName[BASE_NAME_SIZE + 1] = '0';
        binName[BASE_NAME_SIZE ]++;
      }
      else{
      if (binName[BASE_NAME_SIZE] == '9') ;
      binName[BASE_NAME_SIZE + 1]++;
      }
    }
 
  }
 // Serial.println(binName);
  // delete old log file
  if (sd.exists(TMP_FILE_NAME)) {
    if (!sd.remove(TMP_FILE_NAME)) ;
  }
  // create new file
  myFile.close();
  if (!myFile.createContiguous(sd.vwd(),
  TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT));
  // get address of file on SD
  if (!myFile.contiguousRange(&bgnBlock, &endBlock));
  // use SdFat's internal buffer
  pCache = (uint8_t*)sd.vol()->cacheClear();
  if (pCache == 0); 
  memset(pCache, '0', 512); 
  // flash erase all data in file
  uint32_t bgnErase = bgnBlock;
  uint32_t endErase;
  while (bgnErase < endBlock) {
    endErase = bgnErase + ERASE_SIZE;
    if (endErase > endBlock) endErase = endBlock;
    if (!sd.card()->erase(bgnErase, endErase)) ;
    bgnErase = endErase + 1;
  }
}

time_t getTeensy3Time()
{
  return Teensy3Clock.get();
}

void setup() {
  Serial.begin(115200);
  setSyncProvider(getTeensy3Time);
  SdFile::dateTimeCallback(dateTime);
  pinMode(ledPin, OUTPUT);
  pinMode(readPin0, INPUT);  //Read analog signal pin1
  pinMode(readPin1, INPUT);  //Read analog signal pin2
  
  //Set ADC (Averaging and Resolution)  
  adc->setAveraging(32); // set number of averages
  adc->setResolution(12); // set bits of resolution
    // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED_16BITS, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  //try low/med speed since load cell has high impedence
  adc->setConversionSpeed(ADC_LOW_SPEED); // change the conversion speed
  // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  adc->setSamplingSpeed(ADC_LOW_SPEED); // change the sampling speed
  
  adc->setAveraging(32, ADC_1); // set number of averages
  adc->setResolution(12, ADC_1); // set bits of resolution
  adc->setConversionSpeed(ADC_LOW_SPEED, ADC_1); // change the conversion speed
  adc->setSamplingSpeed(ADC_LOW_SPEED, ADC_1); // change the sampling speed
  // always call the compare functions after changing the resolution!
    //adc->enableCompare(1.0/3.3*adc->getMaxValue(), 0, ADC_0); // measurement will be ready if value < 1.0V
    //adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V

    
  // Initialize SdFat or print a detailed error message and halt
  // Use half speed like the native library.
  // change to SPI_FULL_SPEED for more performance.
  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt();

  createFile();  
}

void loop() {
//not sure how you start/stop data collection
  //Example using serial input
    if (Serial.available()) {
	c = Serial.read();
	if(c=='r') { // Start recording
	
        if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT))Serial.println("Recording") ;
    
        //digitalWrite(LED,ledValue);//Turn LED on to indicate recording if you want
        timer0.begin(timerCallback0, 40000); //start timer 4000=250sps
    } else if(c=='s') { // stop recording
        stopRecording();
        
    } 
  }
 delay(1);

}

void stopRecording(){
        timer0.end(); //Turn off timer to stop SD card write
        //digitalWrite(FULL,LOW);
        if (!sd.card()->writeStop())Serial.println("Stopped");
        
       // digitalWrite(LED,ledValue); //Turn LED off
        // Truncate file if recording stopped early.
        if (bn != FILE_BLOCK_COUNT) {    
            if (!myFile.truncate(512L * bn)) ;
        }
        if (!myFile.rename(sd.vwd(), binName)) ;
        createFile(); 
		}

void timerCallback0(){
      result = adc->analogSynchronizedRead(readPin0, readPin1);

  if (count < 128){ //since using float values, 128x4=512bytes
   // && (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE) 
   //having conversion issues with new ADC library
    SDBuffer[count] = micros();
    SDBuffer[count+1] = (result.result_adc0)-(result.result_adc1); //conversion was not giving me volts, so just removed to test
    SDBuffer[count+2] = (result.result_adc0);
    SDBuffer[count+3] = (result.result_adc1);
        Serial.print("Time: ");
        Serial.print(SDBuffer[count]);
        Serial.print("\t"); 
        Serial.print("A-B: ");
        Serial.print(SDBuffer[count+1]);  
        Serial.print("\t");  
        Serial.print("A: ");  
        Serial.print(SDBuffer[count+2]);
        Serial.print("\t");  
        Serial.print("B: ");
        Serial.println(SDBuffer[count+3]); 
    count = count + 4;//move index 4 positions for next values
  }
 
  if (count== 128){
   noInterrupts(); 
   memcpy(pCache, &SDBuffer, 512);
       if (!sd.card()->writeData((uint8_t*)pCache)) ;
    bn++; //increment block number
    count = 0; 
    memset(pCache, '0', 512); 
      interrupts();
  }
       
 
}
 
Thanks for uploading your attempt!

I'm currently testing the code you've send me, although, when measuring, i get values like -70000 etc etc. after changing the 'int SDBuffer' to 'float SDBuffer'. i got some values.

also, had to change this:
Code:
void timerCallback0(){
      result = adc->analogSynchronizedRead(readPin0, readPin1);
      float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
      float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue();
  if (count < 128){ //since using float values, 128x4=512bytes
   // && (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE) 
   //having conversion issues with new ADC library
    SDBuffer[count] = micros();
    SDBuffer[count+1] = (ADCvalue_0-ADCvalue_1); //conversion was not giving me volts, so just removed to test
    SDBuffer[count+2] = (ADCvalue_0);
    SDBuffer[count+3] = (ADCvalue_1);
        Serial.print("Time: ");
        Serial.print(SDBuffer[count]);
        Serial.print("\t"); 
        Serial.print("A-B: ");
        Serial.print((SDBuffer[count+1]),DEC);  
        Serial.print("\t");  
        Serial.print("A: ");  
        Serial.print((SDBuffer[count+2]),DEC);
        Serial.print("\t");  
        Serial.print("B: ");
        Serial.println((SDBuffer[count+3]),DEC); 
    count = count + 4;//move index 4 positions for next values
  }

But... on the B side of the sensor, i get the wanted values, but it measures sometimes a value at -56 (see logfile)
View attachment LOG001.txt

An better log file to see the issue:
View attachment LOG002.txt

Any idea what causes this? Haven't had this before tho...


UPDATE:
(1) Ok, i just changed the 'timer0.begin(timerCallback0, 4000); //start timer 4000=250sps'
And in the serial monitor i have seen less values at -56, but it's still there sometimes... strange?

(2) It seems to behave only with ADC1, ADC0 gives the correct values, but ADC1 gives incorrect values, and it decreases or increases when changing the following values:
- changing the samples/second
- change averaging
- change resolution

(3) I quess i have fixed it?
a few changes in my code:
Code:
int ADCvalue_0 = ADC_ERROR_VALUE;
int ADCvalue_1 = ADC_ERROR_VALUE;
Code:
 if (count < 128 //since using float values, 128x4=512bytes
    && (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE)) {
   //having conversion issues with new ADC library
 
Last edited:
I haven't been able to log my files to sd, i see a .bin format, for my purposes i want a .txt file.
I have changed the 'char binName[13] = FILE_BASE_NAME "000.bin";' to 'char binName[13] = FILE_BASE_NAME "000.txt";'

on my sd card, i see now .txt files, but empty...

I have changed the serial.print to myFile.print although nothing seems to happen? In my version i had to open the file, and close the file. But that seems to happen when i start the recording and stop the recordings?

code:
Code:
/*
ECG measurement Project:
- Sensing two ADC values,
- Analog files being logged on sd card
    Time  A-B  A  B
*/

//Including Libraries
#include <ADC.h>
#include <string.h>
#include <Time.h>  
#include <SdFat.h>
#include <SdFatUtil.h>
SdFat sd;
SdFile myFile;

//Define ADC Inputs
#define ADC_0 0
#define ADC_1 1

//Teensy 3.1 has the LED on pin 13
const int ledPin = 13;
const int readPin0 = A2;  //ADC read A2
const int readPin1 = A3;  //ADC read A3
const int chipSelect = SS;
//char filename[] = "00000000.txt";
#define SD_CHIP_SELECT  10  // SD chip select pin
#define error(s) sd.errorHalt_P(PSTR(s))
#define FILE_BASE_NAME "LOG" //Can be max size of 5 character, XXXXX000.BIN DOS 8.3 format
#define TMP_FILE_NAME "TMP_LOG.txt"
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
const uint32_t FILE_BLOCK_COUNT = 500UL; //500=250kb 1 block=512bytes
const uint32_t ERASE_SIZE = 262144L;
uint32_t bgnBlock, endBlock;
uint8_t*  pCache;
char binName[] = FILE_BASE_NAME "000.txt"; //[13]

ADC *adc = new ADC(); // adc object
ADC::Sync_result result;
int ADCvalue_0 = ADC_ERROR_VALUE;
int ADCvalue_1 = ADC_ERROR_VALUE;
float myArray [250][3] = {}; //2d matrix array
float SDBuffer [128];
int count = 0;
uint32_t bn = 1; //block number
int value0;
int value1;

//Interval Timer
IntervalTimer timer0;
char c=0;

//------------------------------------------------------------------------------
/*
 * User provided date time callback function.
 * See SdFile::dateTimeCallback() for usage.
 */
void dateTime(uint16_t* date, uint16_t* time) {
  // User gets date and time from GPS or real-time
  // clock in real callback function
  time_t now();
  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(year(), month(), day());

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(hour(), minute(), second());
}
void createFile(){

  bn=1; //reset block count
  count=0;
  // Find unused file name.
  if (BASE_NAME_SIZE > 5) {
    //sendError(3,1);
  }
  while (sd.exists(binName)) {
    if (binName[BASE_NAME_SIZE + 2] != '9') {
      binName[BASE_NAME_SIZE + 2]++; //changed from +1 since now 0-999 instead of 0-99
    } 
    else {
      binName[BASE_NAME_SIZE + 2] = '0';
      //binName[BASE_NAME_SIZE + 1]++;
      if (binName[BASE_NAME_SIZE+1] == '9') {
        binName[BASE_NAME_SIZE + 1] = '0';
        binName[BASE_NAME_SIZE ]++;
      }
      else{
      if (binName[BASE_NAME_SIZE] == '9') ;
      binName[BASE_NAME_SIZE + 1]++;
      }
    }
 
  }
 // Serial.println(binName);
  // delete old log file
  if (sd.exists(TMP_FILE_NAME)) {
    if (!sd.remove(TMP_FILE_NAME)) ;
  }
  // create new file
  myFile.close();
  if (!myFile.createContiguous(sd.vwd(),
  TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT));
  // get address of file on SD
  if (!myFile.contiguousRange(&bgnBlock, &endBlock));
  // use SdFat's internal buffer
  pCache = (uint8_t*)sd.vol()->cacheClear();
  if (pCache == 0); 
  memset(pCache, '0', 512); 
  // flash erase all data in file
  uint32_t bgnErase = bgnBlock;
  uint32_t endErase;
  while (bgnErase < endBlock) {
    endErase = bgnErase + ERASE_SIZE;
    if (endErase > endBlock) endErase = endBlock;
    if (!sd.card()->erase(bgnErase, endErase)) ;
    bgnErase = endErase + 1;
  }
}

time_t getTeensy3Time()
{
  return Teensy3Clock.get();
}

void setup() {
  Serial.begin(115200);
  setSyncProvider(getTeensy3Time);
  SdFile::dateTimeCallback(dateTime);
  pinMode(ledPin, OUTPUT);
  pinMode(readPin0, INPUT);  //Read analog signal pin1
  pinMode(readPin1, INPUT);  //Read analog signal pin2
  
  //Set ADC (Averaging and Resolution)  
  adc->setAveraging(32); // set number of averages
  adc->setResolution(12); // set bits of resolution
    // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED_16BITS, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  //try low/med speed since load cell has high impedence
  adc->setConversionSpeed(ADC_HIGH_SPEED); // change the conversion speed
  // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  adc->setSamplingSpeed(ADC_HIGH_SPEED); // change the sampling speed
  
  adc->setAveraging(32, ADC_1); // set number of averages
  adc->setResolution(12, ADC_1); // set bits of resolution
  adc->setConversionSpeed(ADC_HIGH_SPEED, ADC_1); // change the conversion speed
  adc->setSamplingSpeed(ADC_HIGH_SPEED, ADC_1); // change the sampling speed
  // always call the compare functions after changing the resolution!
   // adc->enableCompare(1.0/3.3*adc->getMaxValue(), 0, ADC_0); // measurement will be ready if value < 1.0V
   // adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V

    
  // Initialize SdFat or print a detailed error message and halt
  // Use half speed like the native library.
  // change to SPI_FULL_SPEED for more performance.
  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt();

  createFile();  
}

void loop() {
//not sure how you start/stop data collection
  //Example using serial input
    if (Serial.available()) {
	c = Serial.read();
	if(c=='r') { // Start recording
	
        if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) Serial.println("Recording") ;
    
        //digitalWrite(LED,ledValue);//Turn LED on to indicate recording if you want
        timer0.begin(timerCallback0, 4000); //start timer 4000=250sps
    } else if(c=='s') { // stop recording
        stopRecording();
        
    } 
  }
 delay(1);

}

void stopRecording(){
        timer0.end(); //Turn off timer to stop SD card write
        //digitalWrite(FULL,LOW);
        if (!sd.card()->writeStop())Serial.println("Stopped");
        
       // digitalWrite(LED,ledValue); //Turn LED off
        // Truncate file if recording stopped early.
        if (bn != FILE_BLOCK_COUNT) {    
            if (!myFile.truncate(512L * bn)) ;
        }
        if (!myFile.rename(sd.vwd(), binName)) ;
        createFile(); 
		}

void timerCallback0(){
      result = adc->analogSynchronizedRead(readPin0, readPin1);
     float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
     float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue();
  if (count < 128 //since using float values, 128x4=512bytes
    && (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE)) {
   //having conversion issues with new ADC library
    SDBuffer[count] = second();
    SDBuffer[count+1] = ADCvalue_0-ADCvalue_1; //conversion was not giving me volts, so just removed to test
    SDBuffer[count+2] = ADCvalue_0;
    SDBuffer[count+3] = ADCvalue_1;
    
// to Serial 
/*
//        Serial.print("Time: ");
        Serial.print(SDBuffer[count]),-2;
        Serial.print("\t"); 
//        Serial.print("A-B: ");
        Serial.print((SDBuffer[count+1]),DEC);  
        Serial.print("\t");  
//        Serial.print("A: ");  
        Serial.print((SDBuffer[count+2]),DEC);
        Serial.print("\t");  
//        Serial.print("B: ");
        Serial.println((SDBuffer[count+3]),DEC); 
*/

// to SD        
//        myFile.print("Time: ");
        myFile.print(SDBuffer[count]),-2;
        myFile.print("\t"); 
//        myFile.print("A-B: ");
        myFile.print((SDBuffer[count+1]),DEC);  
        myFile.print("\t");  
//        myFile.print("A: ");  
        myFile.print((SDBuffer[count+2]),DEC);
        myFile.print("\t");  
//        myFile.print("B: ");
        myFile.println((SDBuffer[count+3]),DEC);    
       
        
        count = count + 4;//move index 4 positions for next values
  }
 
  if (count== 128){
   noInterrupts(); 
   memcpy(pCache, &SDBuffer, 512);
       if (!sd.card()->writeData((uint8_t*)pCache)) ;
    bn++; //increment block number
    count = 0; 
    memset(pCache, '0', 512); 
      interrupts();
  }
       
 
}
 
Last edited:
Hi,

Well I was having issues with the conversion, so when you get -70000, that means there was actually an error. I am not sure why it was happening, it was even happening on the supplied examples in the ADC library, so I am not sure what was up. Are you using the newest library? Also, the whole point of the example I showed you was the fact you are now saving the file as a binary file, 0 and 1's, instead of converting to text, which takes time. This is how you can write fast.....however you need to convert the binary back to a text file. I use labview for my purposes, but SDfat has an example in his fast analog logger, where he converts the binary to a txt file. You data is pretty simple, 4 samples that are each 4 bytes, so you could read the data in 16 byte chunks. Here is the code from SDfat, with my best guess at how it might work for you (you will have to also get bufferedwriter.h, which converts your binary to values you want). Again, I have no clue if this will work....or if you want to use this method. Search for SDfat fast analog logger, you can then see how he deals with logging quickly. As for the ADC not converting correctly, I am not sure what is wrong.
Just noticed in your code above, you can't use myFile.print anymore, like I said above, you are now using the cache on the SD card, but you also have it in an if statement that will try to write every sample, which is not good. Also, you are now using seconds() as your time stamp, does not seem accurate enough to test your sampling resolutions. Also, check the if statement if (count<128 //....remove the stuff after the //, that was there to tell you that if I put the other statements in and the conversion had an error, then it does not add the value, and hence you are not sampling at 250 sps since it is missing values. In your txt file you provided, I find it odd how it is 4 good values, then a bad one. Find it very odd how it follows that pattern for the whole file....meaning something is wonky, might need Pedvide (creator of ADC library) to figure that one out.

Code:
//------------------------------------------------------------------------------
// convert binary file to text file
void binaryToText() {
  uint8_t lastPct;
  int buf[4];
  uint32_t t = 0;
  uint32_t syncCluster = 0;
  SdFile binFile;
  SdFile textFile;
  BufferedWriter bw;
  
  if (!binFile.open(FILE_NAME, O_READ)) {
    error("open binary");
  }
  // create a new binFile
  char name[13];
  strcpy_P(name, PSTR("DATA00.TXT"));
  for (uint8_t n = 0; n < 100; n++) {
    name[4] = '0' + n / 10;
    name[5] = '0' + n % 10;
    if (textFile.open(name, O_WRITE | O_CREAT | O_EXCL)) break;
  }
  if (!textFile.isOpen()) error("open textFile");
  PgmPrint("Writing: ");
  Serial.println(name);
  
  bw.init(&textFile);
  
  while (!Serial.available() && binFile.read(&buf, 16) == 16) {
    uint16_t i;

    for (i = 0; i < 4; i++) {
      bw.putNum(buf[i]);
      bw.putCRLF();
    }
    
    if (textFile.curCluster() != syncCluster) {
      bw.writeBuf();
      textFile.sync();
      syncCluster = textFile.curCluster();
    }
    if ((millis() -t) > 1000) {
      uint8_t pct = binFile.curPosition() / (binFile.fileSize()/100);
      if (pct != lastPct) {
        t = millis();
        lastPct = pct;
        Serial.print(pct, DEC);
        Serial.println('%');
      }
    }
    if (Serial.available()) break;
  }
  bw.writeBuf();
  textFile.close();
  PgmPrintln("Done");
}
//------------------------------------------------------------------------------
 
Last edited:
ADC_ERROR_VALUE = -70000 (because that number can´t be represented with a 16bit number), Are you having problems now with this?
 
Hi Pedvide, I am not sure what is happening, but when I was trying to help out chopsticks, I was getting these conversion errors. I even tried just using the supplied syncro example and it won't work, just shows error for ADC1, think it was 0x10. I also had this issue with my differential reading for my project, it was reading 0.0000 if I uncommented the compare functions. But if I commented them out, it worked and if i changed the resolution, it still worked, meaning it gave me the correct volts. If you look at the log files he supplied above, you will see how it gave this error every 5th value. Is it because we are reading in the interval timer? Should we use your method of starting the reading in the interval timer and use the interrupts to then get the value? For my testing I just used a simple voltage divider, in case you were wondering.
 
ADC_ERROR_VALUE = -70000 (because that number can´t be represented with a 16bit number), Are you having problems now with this?

At this point it seems that i don't have problems anymore with -70000 values, i've been able to fix this issue. Although, as is shown in my log files, i was reading strange values at -56. And it seems to behave at every fifth value.

@turtle9er
i will check up your code with binary to text next monday.

Thanks in advance
 
-70000*3.3/4095 = -56,4. (12 bits resolution).

I'm looking into it. Do you have interrupts on for the ADC1? (you only have problems with the ADC1, right?)
 
Fuck me, silliest bug ever..... apparently I need to read about the differences between && and || operators......
I'll update a fix in a few minutes (I have to review a few logical statements....)
 
-70000*3.3/4095 = -56,4. (12 bits resolution).

I'm looking into it. Do you have interrupts on for the ADC1? (you only have problems with the ADC1, right?)

Ahhh so I actually had a -70000 bug, but because of the calculations it's -56,4.. sounds logical... and yes, it was only with ADC1

Fuck me, silliest bug ever..... apparently I need to read about the differences between && and || operators......
I'll update a fix in a few minutes (I have to review a few logical statements....)

that is silly, but that happens everyone. I'll check your update when it's ready!
btw....
in the example of synchronizedmeasurement there was a error aswell for ADC1, comparison failure....

I think that those && or || causes this? or am I wrong?
 
What error is that? which line?

The bug was in the analogSynchronizedRead function.
It starts the measurements on both ADCs and then waits for them to finish. The bug was that instead of waiting like this:
while (adc0_is_busy || adc1_is_busy ) { do nothing}
it was doing:
while (adc0_is_busy && adc1_is_busy ) { do nothing}
So when the ADC0 was done it stopped waiting and when later it checks whether the conversion was successful the ADC1 was false because sometimes it hadn't even finish converting.

This also shows that both measurements aren't perfectly synchronized, there are a few cpu instructions apart.
 
Status
Not open for further replies.
Back
Top