Using Teensy 3.5 Built-in SD card reader for data acquisition

Status
Not open for further replies.
Hi, I am trying to use Teensy 3.5 to write data that I acquire from two sensors (encoder and an EMG). I found that there are previous forum mentioning about this too and I've also tried it. I tried changing the code from the forum. It does made new file in my SD card but the data sensor isn't saved, so basically it's just an empty csv data.

I use the SdFat library.

This is the code
Code:
#include <SdFat.h>
#define outputA 5
#define outputB 6
#define RawEMG A0
#define MaxCount 10000
int counter = 0;
int derajat = 0;
int EMG;
int aState;
int aLastState;
SdFatSdio sd;
File file;

void setup()
{
  pinMode (outputA, INPUT_PULLUP);
  pinMode (outputB, INPUT_PULLUP);
  Serial.begin(9600);
  //Reads the initial state of the outputA
  aLastState = digitalRead(outputA);

  if (!sd.begin()) {
    sd.initErrorHalt();

  }
}
void doLogging(uint32_t timeStamp, int *data1,int *data2, int *data3, int nb)
{
  // following is for logging
  static uint32_t ifl = 0;
  static uint32_t count = 0;
  char filename[32];

  if(!count)
  {
     //open file
     sprintf(filename,"Data.csv",ifl); ifl++;
     if (!file.open(filename, O_RDWR | O_CREAT)) {
       sd.errorHalt("open failed");
     count=1;
     }
  }
  //
  if(count>0)
  {
    // write to file
    file.print(timeStamp);
    for(int ii=0; ii<nb; ii++)
    { file.print(',');
      file.print(data1[ii]);
      file.print(',');
      file.print(data2[ii]);
      file.print(',');
      file.print(data3[ii]);
    }
    file.println();
    count++;
    if(count>MaxCount) count=0;
  }
  //
  if(!count)
  {
     // close file
     file.close();
  }
}


void loop()
{
  uint32_t t0 = millis();
  EMG = analogRead(RawEMG); //reads EMG sensor
  //float EMG = EMG * (5.0 / 1023.0);
  aState = digitalRead(outputA); //reads the 'current' state of the outputA
  //if outputB state is different to the outputA state, that means the encoder is rotating clockwise
  if (aState != aLastState){
    //if outputB state is different to the outputA state, that means the encoder is rotating clockwise
    if (digitalRead(outputB) != aState){
      counter ++;
    } else {
      counter --;
    }
    if (counter > 2048 or counter < -2048) //omron sebelumnya 1200 yang 600ppr, resolusi x 2
    {
      counter = 0;
    }
    derajat = counter * 360/2048;

  }
   aLastState = aState;
   Serial.print(counter);
   Serial.print(",");
   Serial.print(derajat);
   Serial.print(",");
   Serial.println(EMG);
  doLogging(t0,&counter, &derajat, &EMG,1); //insert data (?)
}

Is there anything wrong with it? How can I make it work? I think there's something wrong with the dologging void but I'm not entirely sure how to work my way around. Please help me, thank you.
 
I would first apply the following change
Code:
//open file
     sprintf(filename,"Data%03d.csv",ifl); ifl++;
than can you tell us what the serial monitor is printing
 
I would first apply the following change
Code:
//open file
     sprintf(filename,"Data%03d.csv",ifl); ifl++;
than can you tell us what the serial monitor is printing

Thank you for replying. I've tried your code but it doesn't have any effect. It was saving empty csv files instead and there were a lot of it. When I run your code, I have this warning.

Code:
SaveToSDCard: In function 'void doLogging(uint32_t, int*, int*, int*, int)':
C:\Users\HP\Documents\Arduino\SaveToSDCard\SaveToSDCard.ino:37:41: warning: format '%d' expects argument of type 'int', but argument 3 has type 'uint32_t {aka long unsigned int}' [-Wformat=]

The serial monitor is actually printing the values of the sensors this can be seen from the loop void.

Code:
   Serial.print(counter);
   Serial.print(",");
   Serial.print(derajat);
   Serial.print(",");
   Serial.println(EMG);

I would like to save the values of the sensors to SD card within a range of time that we can change, but still maintain its frequency sampling.
 
next thing I would try is to move the print to serial adjacent to prints to file move from loop into doLogging
Code:
for(int ii=0; ii<nb; ii++)
    { file.print(',');
      file.print(data1[ii]);
      file.print(',');
      file.print(data2[ii]);
      file.print(',');
      file.print(data3[ii]);

   Serial.print(",");
   Serial.print(data1[ii]);
   Serial.print(",");
   Serial.print(data3[ii]);
   Serial.print(",");
   Serial.println(data3[ii]);    }
to see what is exactly coming into subroutine
 
your logic around the file.open() is flawed. You only would set count = 1 if the open() failed! so count is never set to 1, and all your dologger() does is open the file then close the file. Your flawed logic doesn't write any data to the file.:(
 
your logic around the file.open() is flawed. You only would set count = 1 if the open() failed! so count is never set to 1, and all your dologger() does is open the file then close the file. Your flawed logic doesn't write any data to the file.:(

I completely missed that.
For me that is the reason, why I never liked '{' at the end of the line. I prefer that in line with '}'.
 
I completely missed that.
For me that is the reason, why I never liked '{' at the end of the line. I prefer that in line with '}'.

Keeping code formatted helps - doing it manually of course is suspect to seeing things like that. In the IDE a Ctrl+T will do formatting the their uniform standard.

That is an easy way to find missing syntax thingies. Very handy as the IDE cascades problems from the syntax error into who knows what.

Most other IDE's have such things - for Sublimetext I found it and mapped to Ctrl+Alt+F.
 
your logic around the file.open() is flawed. You only would set count = 1 if the open() failed! so count is never set to 1, and all your dologger() does is open the file then close the file. Your flawed logic doesn't write any data to the file.:(

Hi, thank you for your reply. I realized that the dologger() function is not right. But I had seen it from a forum, he said that it works for him. I thought it would work for me too. Is there any suggestion on how I should change the code? I'm actually not good with this.
 
next thing I would try is to move the print to serial adjacent to prints to file move from loop into doLogging
Code:
for(int ii=0; ii<nb; ii++)
    { file.print(',');
      file.print(data1[ii]);
      file.print(',');
      file.print(data2[ii]);
      file.print(',');
      file.print(data3[ii]);

   Serial.print(",");
   Serial.print(data1[ii]);
   Serial.print(",");
   Serial.print(data3[ii]);
   Serial.print(",");
   Serial.println(data3[ii]);    }
to see what is exactly coming into subroutine

Nothing happens when I try to comment my serial.print in the loop function and tried the code that you suggested. The serial monitor didn't show anything.
 
Code:
void setup()
{
  pinMode (outputA, INPUT_PULLUP);
  pinMode (outputB, INPUT_PULLUP);
  Serial.begin(9600);
[B]  while( !Serial ); // Add this when seeing output is needed[/B]  // or to wait 2 seconds // while( !Serial && millis()<2000 ); 
  //...
 
Is there any suggestion on how I should change the code? I'm actually not good with this.
Simply use
Code:
  if(!count)
  {
     //open file
     sprintf(filename,"Data%03d.csv",ifl); ifl++;
     if (!file.open(filename, O_RDWR | O_CREAT))  sd.errorHalt("open failed");
     count=1;
  }
 
Thank you! Your suggestions work! Just a quick question, is there a way to add an id and a column title? I am also wondering if using an SD card to record a data would have a fix frequency sampling?
 
Thank you! Your suggestions work! Just a quick question, is there a way to add an id and a column title? I am also wondering if using an SD card to record a data would have a fix frequency sampling?

Code:
  if(!count)
  {
     //open file
     sprintf(filename,"Data%03d.csv",ifl); ifl++;
     if (!file.open(filename, O_RDWR | O_CREAT))  sd.errorHalt("open failed");

    // write header to file
    file.println("timeStamp,counter,derajat,EMG");

    // flag open file
    count=1;
  }

And No the sampling rate in not fixed, it is as fast as 'loop() and ADC allow

If you need precise sampling then you need a more complex program with timed interrupt.
But I would simply try it out, you have a timestamp, so you can assess the regularity of the ADC
 
what is your expected data rate?

The following code may be a start
Code:
//
#define SDFAT_BETA 0
#if SDFAT_BETA == 0
  #include <SdFat.h>
#else
  #include <SdFat-beta.h> // latest SdFat needed for SDIO and (renamed SdFat.h into SdFat-beta.h)
#endif
#define outputA 5
#define outputB 6
#define RawEMG A0
#define MaxCount 10000

uint32_t t0 = 0;
int counter = 0;
int derajat = 0;
int EMG = 0;

int aState;
int aLastState;

#if SDFAT_BETA == 0
  SdFatSdio sd;
  File file;
#else
  SdFs sd;
  FsFile file;
#endif
#define SD_CONFIG SdioConfig(FIFO_SDIO)

#define F_SAMP 500
#define PIT_PERIOD (F_BUS/F_SAMP)

uint32_t ADC_ready=0;
uint32_t ADC_overflow=0;

void ADC_input(void)
{
  t0 = millis();
  EMG = analogRead(RawEMG); //reads EMG sensor
  //float EMG = EMG * (5.0 / 1023.0);
  aState = digitalRead(outputA); //reads the 'current' state of the outputA

  //if outputB state is different to the outputA state, that means the encoder is rotating clockwise
  if (aState != aLastState){
    //if outputB state is different to the outputA state, that means the encoder is rotating clockwise
    if (digitalRead(outputB) != aState){
      counter ++;
    } else {
      counter --;
    }
    if (counter > 2048 or counter < -2048) //omron sebelumnya 1200 yang 600ppr, resolusi x 2
    {
      counter = 0;
    }
    derajat = counter * 360/2048;

  }
   aLastState = aState;
   //
   ADC_ready=1; //flag to loop that we have new data
}

void ADC_Trigger_isr(void)
{
  PIT_TFLG2=1;
  static uint16_t busy;
  digitalWriteFast(13,!digitalReadFast(13));
  if(busy && ADC_ready) { ADC_overflow++; return; }
  busy=1;
  ADC_input();
  busy=0;
}

void ADC_Trigger_init(void)
{  // check with AudioStream if we are responsable for updates
  const int prio=10;
  // assign local function as ISR
  _VectorsRam[IRQ_PIT_CH2 + 16] = ADC_Trigger_isr;
  NVIC_SET_PRIORITY(IRQ_PIT_CH2, prio*16);
  NVIC_ENABLE_IRQ(IRQ_PIT_CH2);

  // turn on PIT clock
  SIM_SCGC6 |= SIM_SCGC6_PIT;
  // turn on PIT     
  PIT_MCR = 0x00;
  
  // Timer 0     
  PIT_LDVAL2 = PIT_PERIOD;
  PIT_TCTRL2 = 2; // enable Timer 2 interrupts
  PIT_TCTRL2 |= 1; // start Timer 2
}


void setup()
{
  pinMode (outputA, INPUT_PULLUP);
  pinMode (outputB, INPUT_PULLUP);

  Serial.begin(9600);
  while(!Serial);

  //Reads the initial state of the outputA
  aLastState = digitalRead(outputA);

  if (!sd.begin(SD_CONFIG)) {
    sd.initErrorHalt(&Serial);
  }
  pinMode(13,OUTPUT);
  ADC_Trigger_init();
}
void doLogging(uint32_t timeStamp, int *data1,int *data2, int *data3, int nb)
{
  // following is for logging
  static uint32_t ifl = 0;
  static uint32_t count = 0;
  char filename[32];

  if(!count)
  {
     //open file
     sprintf(filename,"Data%03u.csv",(unsigned int)ifl); ifl++;
     if (!file.open(filename, O_RDWR | O_CREAT)) sd.errorHalt("open failed");    

     Serial.println(filename);
     // write header to file
     file.println("timeStamp,counter,derajat,EMG");

     // flag open file     
     count=1;
  }
  //
  if(count>0)
  {
    // write to file
    file.print(timeStamp);
    for(int ii=0; ii<nb; ii++)
    { file.print(',');
      file.print(data1[ii]);
      file.print(',');
      file.print(data2[ii]);
      file.print(',');
      file.print(data3[ii]);
    }
    file.println();
    count++;
    if(count>MaxCount) count=0;
  }
  //
  if(!count)
  {  Serial.print("Overflow: "); Serial.println(ADC_overflow);
     ADC_overflow=0;
     // close file
     file.close();
  }
}

void loop()
{
  if(ADC_ready)  
  { doLogging(t0,&counter, &derajat, &EMG,1); //insert data (?)
    ADC_ready=0;
  }
}
 
Last edited:
I have tried your code but there's an error in #define SD_CONFIG SdioConfig(FIFO_SDIO). Is that a library that I need to download? Where can I download it?

I was expecting a sampling rate that can acquire both sensors values properly without any data missing I hope.
 
Sorry, that is my fault.
I'm using the latest (beta) version of SdFat.

You could change code to become
Code:
#if SDFAT_BETA == 0
  SdFatSdio sd;
  File file;
#else
  SdFs sd;
  FsFile file;
  #define SD_CONFIG SdioConfig(FIFO_SDIO)
#endif
and
Code:
#if SDFAT_BETA == 0
  if (!sd.begin()) {
    sd.initErrorHalt();
  }
#else
  if (!sd.begin(SD_CONFIG)) {
    sd.initErrorHalt(&Serial);
  }
#endif
 
Sorry, that is my fault.
I'm using the latest (beta) version of SdFat.

You could change code to become
Code:
#if SDFAT_BETA == 0
  SdFatSdio sd;
  File file;
#else
  SdFs sd;
  FsFile file;
  #define SD_CONFIG SdioConfig(FIFO_SDIO)
#endif
and
Code:
#if SDFAT_BETA == 0
  if (!sd.begin()) {
    sd.initErrorHalt();
  }
#else
  if (!sd.begin(SD_CONFIG)) {
    sd.initErrorHalt(&Serial);
  }
#endif

Where should I paste this? I have tried running the first code with the previous code you shared and the error is that FIFO_SDIO was not declared.
 
Looking at Post #14 code - it looks like nothing gets pasted - but the line
Code:
#define SDFAT_BETA 0
is removed
 
lets try again:
actually post #14 says
Code:
#if SDFAT_BETA == 0
  SdFatSdio sd;
  File file;
#else
  SdFs sd;
  FsFile file;
#endif
#define SD_CONFIG SdioConfig(FIFO_SDIO)
that should become
Code:
#if SDFAT_BETA == 0
  SdFatSdio sd;
  File file;
#else
  SdFs sd;
  FsFile file;
  #define SD_CONFIG SdioConfig(FIFO_SDIO)
#endif
you see the difference?
also later in the code we have in setup()
Code:
  if (!sd.begin(SD_CONFIG)) {
    sd.initErrorHalt(&Serial);
  }
this should become
Code:
#if SDFAT_BETA == 0
  if (!sd.begin()) {
    sd.initErrorHalt();
  }
#else
  if (!sd.begin(SD_CONFIG)) {
    sd.initErrorHalt(&Serial);
  }
#endif
Why this changes?
this allows me to run the code with latest SdFat-beta library and using ExFAT formatted disks, and you with the older SdFat library, by simply changing a number

use old SdFat library:
Code:
#define SDFAT_BETA 0
use new SdFat-beta
Code:
#define SDFAT_BETA 1
clearer now?
 
Okay, thanks. It's more clearer now and I've changed the code. There is no error when I compiled it but I tried printing the sensor values using the code below.

Code:
 if (count > 0)
  {
    // write to file
    file.print(timeStamp);
    for (int ii = 0; ii < nb; ii++)
    { file.print(',');
      file.print(data1[ii]);
      file.print(',');
      file.print(data2[ii]);
      file.print(',');
      file.print(data3[ii]);

      Serial.print(data1[ii]); //counter
      Serial.print(",");
      Serial.print(data2[ii]); //derajat
      Serial.print(",");
      Serial.println(data3[ii]); //EMG
    }

But there's nothing printed in the serial monitor and when I checked the SD card, there's no file saved there.
 
But there's nothing printed in the serial monitor and when I checked the SD card, there's no file saved there.

which seems to indicate, that opening files fails.
I'm on travel for a week and have no Teensy with me, so I cannot check the program.
 
Hi, it's okay. I can wait and while at it, I will find other ways. I think I will try to compare with the previous code that works.
 
Hi, so I had help from a friend and have found a way to save to the SD car while also having a stable frequency rate. I would like to share it with everyone just in case someone encounter the same problem as I am.

Code:
#include <SoftwareSerial.h>
#include <SdFat.h>
//========================================= inisiasi
#define outputA 5 //untuk encoder
#define outputB 6
#define RawEMG A0
int counter = 0;
int derajat = 0;
int EMG;
int aState;
int aLastState;
long timenow = 0 ;
long timeprev = 0;
//========================================= variable
#define MaxCount 1000
int timesampling = 20;
int encoderPPR = 2048;                             // real ppr*2

SdFatSdio sd;
File file;

void setup() {
  pinMode (outputA, INPUT_PULLUP);
  pinMode (outputB, INPUT_PULLUP);
  Serial.begin(9600);
  if (!sd.begin()) {
    sd.initErrorHalt();
  }

  aLastState = digitalRead(outputA);
}

void doLogging(uint32_t timeStamp, int *data1, int *data2, int *data3, int nb)
{
  //================================================= init
  static uint32_t ifl = 0;
  static uint32_t count = 0;
  char filename[32];

  if (!count)
  {
    //open file
    sprintf(filename, "Data1207-01-%03d.csv", ifl); ifl++;
    if (!file.open(filename, O_RDWR | O_CREAT))  sd.errorHalt("open failed");

    // write header to file
    file.println("timeStamp,counter,derajat,EMG");

    // flag open file
    count = 1;
  }

  if (count > 0)
  {
    // write to file
    file.print(timeStamp);
    for (int ii = 0; ii < nb; ii++)
    { file.print(',');
      file.print(data1[ii]);
      file.print(',');
      file.print(data2[ii]);
      file.print(',');
      file.print(data3[ii]);
      
      Serial.print(counter);
      Serial.print(",");
      Serial.print(derajat);
      Serial.print(",");
      Serial.println(EMG);
    }
    file.println();
    count++;
    if (count > MaxCount) count = 0;
  }
  if (!count)
  {
    Serial.println("SAVING DATA................");
    file.close();
  }
}

void loop() {
  //========================================= read data
  uint32_t t0 = millis();
  EMG = analogRead (RawEMG);                       //reads EMG sensor
  //float EMG = EMG * (5.0 / 1023.0);
  aState = digitalRead(outputA);                   //reads the 'current' state of the outputA

  //========================================= do ... every "timesampling"
  timenow = millis();
  if (timenow - timeprev > timesampling) {
    timeprev = timenow;
//    Serial.print(counter);
//    Serial.print(",");
//    Serial.print(derajat);
//    Serial.print(",");
//    Serial.println(EMG);
    doLogging(t0, &counter, &derajat, &EMG, 1);    // Save data
  }

  //========================================= counter encoder to degrees
  if (aState != aLastState) {
    if (digitalRead(outputB) != aState) {
      counter ++;
    } else {
      counter --;
    }
    if (counter > encoderPPR or counter < -encoderPPR)
    {
      counter = 0;
    }
  }
  derajat = counter * 360 / encoderPPR;
  aLastState = aState;
}

As you can see, the code tries to limit the looping process so it won't go faster than the time sampling variable.
 
Status
Not open for further replies.
Back
Top