Create 'Next Integer' new file on SD card

BlaineB

New member
First post.
I'm a mech engg who pokes about in code. I never got to objects and actually using C the way it was meant to be. (What's even a class?? when's it the right time to make one? for example)
Ready sorted Commercial Off The Shelf DAQ systems are expensive, and this is a learning opportunity for me. I'm trying to make a DAQ that will get values off ADC pins, and an I2C Bus, and write these in csv format to be imported to a computer into a Pandas Dataframe where 0-1023 will be calculated into a real world temperature or force etc.

I understand there's a new SdFat library for Teensy and I think was meant to use many of the same calls but that's not been my experience thus far, lots of new words and phrases that don't match legacy norms. They're just variable names, but feels far less friendly to newbies - myself a newbie there's something I'm just not grasping here.

sketch_aug31a:39: error: no matching function for call to 'FsFile::eek:pen(String&, int)'
if(!file.open(fileNumString, O_RDWR | O_CREAT | O_TRUNC)){

Looking through the example files TeensySdioLogger uses this function in this exact way with no issue, only difference, as I see it, is file name.

future improvement, initSD will just find the next file name and return it, a function createFile(fileName); will be called when the DATA_BUTTON falls - no sense creating a file if you never turn on data collection. For right now though, I just can't even create a file as is.

Code:
#include "SdFat.h"
#include "RingBuf.h"
#include <Bounce2.h>
#include <Wire.h>
#include <FastLED.h>

#define RING_BUF_CAPACITY 400*512
#define LOG_FILENAME "DataFile"
// Size to log 10 byte lines at 25 kHz for more than ten minutes.
#define LOG_FILE_SIZE 10*25000*600  // 150,000,000 bytes.
#define DATA_BUTTON  0

Bounce debouncer = Bounce();
unsigned long buttonPressTimeStamp;
boolean dataFlag;

SdFs sd;
FsFile file;
FsFile root;

RingBuf<FsFile, RING_BUF_CAPACITY> rb;

void initSD(){
  //My struggles are here, trying to initialize the file on the SD card
  if(!sd.begin(SdioConfig(FIFO_SDIO))){
    sd.initErrorHalt(&Serial);
  }
  //Open the 'next integer' Data log file
  String dir = "/HotWheelzData/";
  int exist = 0;
  String fileNumString = "";

  for (int fileNum = 0; exist == 0; fileNum ++){
    fileNumString = dir + LOG_FILENAME + "_" + String(fileNum) + ".csv";
    exist = sd.exists(fileNumString);
  }
  
  //Now we've figured out the filename, let's create the file
  if(!file.open(fileNumString, O_RDWR | O_CREAT | O_TRUNC)){
    Serial.println("open failed\n");
    return;
  }
  //File must be pre-allocated to avoid huge delays searching for free clusters.
  if(!file.preAllocate(LOG_FILE_SIZE)){
    Serial.println("preAllocate failed\n");
    file.close();
    return;
  }
} 

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  while (!Serial){}
  initSD();

  pinMode(DATA_BUTTON, INPUT);
  debouncer.attach(DATA_BUTTON);
  debouncer.interval(5);

  //FastLED setup removed for brevity
}

void loop() {
  // put your main code here, to run repeatedly:
  debouncer.update();
  if (debouncer.fell() ){
    //initiate MPU_6050's
    if (dataFlag == false){
      dataFlag = true;
    }
  }
  if (debouncer.rose() ){
    //closeFile();  //safely close the file, release memory.  Haven't gotten here yet
    if (dataFlag == true){
      dataFlag = false;
    }
  }

  if (dataFlag == true){
    //logData();  //Read ADC pins, read I2C for MPU data, and write the data to SD file
  }
}
 
sketch_aug31a:39: error: no matching function for call to 'FsFile:eek:pen(String&, int)'
if(!file.open(fileNumString, O_RDWR | O_CREAT | O_TRUNC)){

Looking through the example files TeensySdioLogger uses this function in this exact way with no issue, only difference, as I see it, is file name.

This error occurs because FsFile:eek:pen() requires the first argument (filename) to be a C string, and what you are passing in is a C++ String. If you look again at the TeensySdioLogger example program, it uses a macro LOG_FILENAME defined as "SdioLogger.csv". This is a C string, i.e. a null-terminated char* variable.

The code below is a C equivalent to what you're doing to build your filename. Note that variable name is fileNumCString to distinguish it from FileNumString.

Code:
  char fileNumCString[64];
  sprintf( fileNumCString, "\/HotWheelzData\/%s_%1d.csv", LOG_FILENAME, fileNum );

I haven't tried to figure out the details, but I suggest that for now you comment out the loop that goes through all of the file names and try to get it to do what you want for a single file.
 
Can confirm

I didn't find that documented anywhere, and it's not obvious to a newbie ... file.open( C String, write mode integer) requires a C string, not a C++ string. I now know that's a thing.


THANK YOU! Jose Pasquariello
 
I didn't find that documented anywhere, and it's not obvious to a newbie ... file.open( C String, write mode integer) requires a C string, not a C++ string. I now know that's a thing.

You're welcome. If you go to this github page (https://github.com/greiman/SdFat) and download the ZIP file and extract it somewhere on your computer, you can go to the "doc" folder and double-click on file "sdfat.html". This will open up a very nice HTML help system for SDFAT. If you search for "open", you'll see that there are a number of different versions, but the first one is the only one with two arguments. The first argument is "const char *path", which means it's C string, and it defines the path to the file you want to open.
 
coming back to this, for someone else looking to have their project create it's own data logging file and just append the next integer to the name, this is what I finally settled on that worked well

Code:
#include "SdFat.h"
#include "RingBuf.h"
#include <Bounce2.h>
#include <Wire.h>
#include <FastLED.h>

#define RING_BUF_CAPACITY 400*512
#define LOG_FILENAME "DataFile"
// Size to log 10 byte lines at 25 kHz for more than ten minutes.
#define LOG_FILE_SIZE 10*25000*600  // 150,000,000 bytes.
#define DATA_BUTTON  0

Bounce debouncer = Bounce();
unsigned long buttonPressTimeStamp;
boolean dataFlag;

SdFs sd;
FsFile file;
FsFile root;

RingBuf<FsFile, RING_BUF_CAPACITY> rb;

void initSD(){
  
  if(!sd.begin(SdioConfig(FIFO_SDIO))){
    sd.initErrorHalt(&Serial);
    return;
  }
  //Open the 'next integer' Data log file
  char dirName[64] = "HotWheelzData";
  int exist = 1;
  char fileNumString[64];

  if(!sd.exists(dirName)){
    if(!sd.mkdir(dirName)){
      Serial.println("sd.mkdir failed\n");
      return;
    }
  }

  if(!sd.chdir(dirName)){
    Serial.println("sd.chdir failed\n");
    return;
  }

  for (int fileNum = 0; exist == 1; fileNum ++){
    sprintf(fileNumString, "\/HotWheelzData\/%s_%1d.csv", LOG_FILENAME, fileNum);
    exist = sd.exists(fileNumString);
    if(fileNumString == 0 && !exist){
      break;
    }
  }
  
  //Now we've figured out the filename, let's create the file
  if(!file.open(fileNumString, O_RDWR | O_CREAT | O_TRUNC)){
    Serial.println("open failed\n");
    return;
  }
  //File must be pre-allocated to avoid huge delays searching for free clusters.
  if(!file.preAllocate(LOG_FILE_SIZE)){
    Serial.println("preAllocate failed\n");
    file.close();
    return;
  }
} 

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  while (!Serial){}
  initSD();

  pinMode(DATA_BUTTON, INPUT);
  debouncer.attach(DATA_BUTTON);
  debouncer.interval(5);

  //FastLED setup removed for brevity
}

void loop() {
  // put your main code here, to run repeatedly:
  debouncer.update();
  if (debouncer.fell() ){
    //initiate MPU_6050's
    if (dataFlag == false){
      dataFlag = true;
    }
  }
  if (debouncer.rose() ){
    //closeFile();  //safely close the file, release memory.  Haven't gotten here yet
    if (dataFlag == true){
      dataFlag = false;
    }
  }

  if (dataFlag == true){
    //logData();  //Read ADC pins, read I2C for MPU data, and write the data to SD file
  }
}


So this makes DataLog_0 if it can't find any files, if it finds that DataLog_0 exists, it looks for DataLog_1 "oh already exists" and carries on until for example DataLog_4 doesn't exist, it makes that file and starts using that
 
coming back to this, for someone else looking to have their project create it's own data logging file and just append the next integer to the name, this is what I finally settled on that worked well

...

Looks good - what does this "fileNumString == 0" do and why?
Code:
  char fileNumString[64];
...
      if([B][U]fileNumString == 0[/U][/B] && !exist){
 
Back
Top