How could I put my file into a directory? [SOLVED]

I'm making a program for a datalogger; all the hardware etc is in the code as comments. As I could use the code to program different data loggers, I would like to use the same SD card, however, as the file name supports 8.3 file formatting, if I tried to log data on more than one program in the same day, the file names would be equal and append the data to the same file. So, I want to be able to create a directory for this file too, however, I'm a little stuck on the syntax of using the file name correctly and putting it into a directory. Any help would be appreciated. Here is the code:

/*    _______________________________________________________________________________________________________________________________________________________________________
ㅤㅤ /\             \                                                                                                                                          /            /\
ㅤ ㅤ|_\             \                                                               DATALOGGER                                                               /            /_|
ㅤㅤ |__\             \________________________________  16x2 LCD, SD ADAPTER, DS1307, DS18B20 (5.1kPU), 10k NTC THERMISTOR  ________________________________/            /__| 
ㅤㅤ |___\                                             \____________________________________________________________________/                                            /___|  
ㅤㅤ |____\                                                                                                                                                             /____|
ㅤ ㅤ|_____\                  _______________________________________________________________________________________________________________________                  /_____|
ㅤㅤ \______\                /                                                                                                                       \                /______/                                            
ㅤㅤ  \_____/\______________/  THIS PROGRAM MAY ALSO BE USED AS A GENERAL DATA LOGGER BY MODIFYING THE SENSOR CODE AND INPUTTING THE NECESSARY CODE   \______________/\_____/
ㅤ ㅤ                        \________________________________________________________________________________________________________________________/                     

___________________________________________________________________________________________________________________________________________________________________________________
ㅤ  ㅤ   ㅤ           ㅤ/                                                   \                           /
HARDWARE WIRING LIST: /                                                     \   PROGRAM DESCRIPTION:  /   
_____________________/                                                       \_______________________/
ㅤ                                                      
_________________________________                        _________________________________________________________________
FOR 16X2 LIQUID CRYSTAL DISPLAY: \                      /                                                                 \
............_____________________/                     /  This program is designed to log data from a DS18B20 temperature  \
-VSS GND   /                                           |  sensor   and   a  10K  NTC  thermistor.  Once the  SD  card  is  | 
-VDD 5V    \                                           |  initialised and the start button is pressed, the  program  will  | 
-V0 10K POT \                                          |  create a file in the SD card and name it according to the  date  | 
-RS PIN D2  |                                          |  (the date is retrieved using a DS1307 RTC). once the  file  and  | 
-RW GND    /                                           |  file name is created, 8 data samples from  the  DS18B20  sensor  | 
-E PIN D3  |                                           |  and thermistor are  averaged  and  logged  within  the  logging  | 
-D0 X     /                                            |  interval into the created file, as well as the time in  seconds  | 
-D1 X    /                                             |  that the program started  and  the  real  time  clock.  If  the  | 
-D2 X    \                                             |  program is ran  until  the  next  day,  data  logging  will  be  | 
-D3 X     \                                            |  stopped, and another file will be created and  named  according  | 
-D4 PIN D4 \                                           |  to the date. The  liquid  crystal  display  or  serial  monitor  | 
-D5 PIN D5  \                                          |  (dependant on the DEBUG state) can be used to ensure  that  the  | 
-D6 PIN D6  |                                          |  program runs correctly, displaying SD card initialisation,  RTC  | 
-D7 PIN D7  /                                          |  initialisation, the filename the data is writing to,  the  time  | 
-A 5V      /                                           |  the data started logging in seconds, and the  current  time  of  | 
-K GND    /                                            \  the RTC. If any errors occur, the LCD or  serial  monitor  will  / 
_________/                                              \__________________  display an error message.  __________________/ 
ㅤ                                                                         \___________________________/
____________________________
FOR SD CARD ADAPTER MODULE: \
..............______________/
-CS PIN D10   \
-MOSI PIN D11  |
-MISO PIN D12 /
-SCK PIN D13 /
____________/

_________________
FOR DS1307 RTC:  \
.............____/
-SCL PIN SCL \
-SDA PIN SDA  |
-SQW X  _____/
_______/

________________________________
FOR DS18B20 TEMPERATURE SENSOR: \
........._______________________/
-POS 5V  \
-NEG GND  \__
-DQ 5.1kPU   \_
-5.1kPU PIN D9 \_____________________________
-104pF CERAMIC CAPACITOR LIKNING POS AND NEG \
_____________________________________________/

________________________
FOR 10k NTC THERMISTOR: \
..........._____________/
-POS 5V    \
-NEG PIN A1 \___________________________
-10k RESISTOR LIKNING NEG AND A1 TO GND \
________________________________________/


// INCLUDE LIBRARIES AND DEFINED CONSTANTS:                                                                                      
/********************************************************************************************************************************************************************************/

#include <OneWire.h> //one wire communication

#include <DallasTemperature.h> //library for DS18B20 temperature sensor

#include <SPI.h> //library for SPI pins of the SD card module adapter

#include <SD.h> //library for the SD card 

#include <LiquidCrystal.h> //library for the LCD

#include <Wire.h> //wire communcation 

#include <TimeLib.h> //time elements library

#include <DS1307RTC.h> //library for the DS1307 RTC

#define totalTime() millis() / 1000 //gives the total time in seconds

#define ONE_WIRE_BUS 9 //define the communication pin of the DS18B20 temperature sensor to digital pin 9

#define getT1() sensors.getTempCByIndex(0) //redefine for ease of coding (I couldn't be bothered typing sensors.getTempCByIndex(0) all the time)

#define requestT1() sensors.requestTemperatures() //redefine for ease of coding, same as getTempCByIndex(0)

#define T2Pin A1 //define the pin of the thermistor to analog pin 1

#define year() tmYearToCalendar(tm.Year) - 2000 //gives the year as a 2 digit integer


// DEBUG STATE: 
/********************************************************************************************************************************************************************************/



#define DEBUG 0 //current DEBUG state, spaced out for ease of navigation. Equal to 0 or 1: 0 for LCD debugging data, 1 for serial monitor debugging data


// IF DEBUG STATE
/********************************************************************************************************************************************************************************/

#if DEBUG == 1 //if DEBUG == 1 then serial debugging data will be printed, lcd debugging data will not. x in all of these arguments are placeholders

#define debug(x) Serial.print(x) //define debug as serial print

#define debugln(x) Serial.println(x) //define debugln as serial println

#define debugLCD(x) //lcd print undefined, therefore it will not be compiled

#define clearLCD() //lcd clear undefined, therefore it will not be compiled

#define SCLCD() //set cursor undefined, therefore it will not be compiled

#else //if DEBUG == 0 then lcd debugging data will be printed, serial debugging data will not

#define debug(x) //serial print undefined, therefore it will not be compiled

#define debugln(x) //serial println undefined, therefore it will not be compiled

#define debugLCD(x) lcd.print(x) //define debugLCD as lcd print

#define clearLCD() lcd.clear() //define lcd clear as clearLCD

#define SCLCD() lcd.setCursor(0, 0) //define set cursor LCD as lcd set cursor to 0, 0 on the display

#endif //end if definitions


// GLOBAL VARIABLES:                    
/********************************************************************************************************************************************************************************/

File dataFile; //the file we will be using to create the filename and write to

OneWire oneWire(ONE_WIRE_BUS); //used for the DS18B20 sensor

DallasTemperature sensors(&oneWire); //used for the DS18B20 sensor

LiquidCrystal lcd(2, 3, 4, 5, 6, 7); //set pins on the lcd

tmElements_t tm; //time elements

const short CS = 10; //chip select pin for the micro SD card adapter module

const short logInterval = 10; //the interval of which the data is written to the file

const float sampleInterval = 1.25; //the interval of which the samples will be taken

static unsigned long logTime = totalTime() + logInterval; //the total log time of the data logger

static float sampleTime = totalTime() + sampleInterval; //the total sample time of the data logger

const short userDelay = 2000; //delay for user opimisation

static short numSamples = 0; //the number of samples taken

static float averageT1; //the average of the sum of samples of temperature sensor 1, divided by the number of samples

static float averageT2; //the average of the sum of samples of temperature sensor 2, divided by the number of samples

static float totalT1 = 0; //the total of the sum of samples of temperature sensor 1

static float totalT2 = 0; //the total of the sum of samples of temperature sensor 2


// INITIALISATION FUNCTION:
/********************************************************************************************************************************************************************************/

void initialisation() { //used in setup to initialise the RTC and SD card

  delay(userDelay); //delay for user optimisation

  while (!Serial); //wait for serial port to connect

  Serial.begin(115200); //begins serial communication

  lcd.begin(16, 2); //begins LCD communication

  SCLCD(); //sets the cursor of the LCD to 0, 0

  debugln(); //prints a new line to format serial monitor data in case of zero bytes printed during serial connection

  debugln("Initialising RTC & SD card..."); //prints that the RTC and SD are being initialised to the serial monitor

  debugLCD("Initialising..."); //prints that the RTC and SD are being initialised to the LCD 

  delay(userDelay); //delay for user optimisation

  clearLCD(); //clears the LCD


  if (!SD.begin(CS)) { //checks if SD card is available and prints error message if not

    debugln("SD card failed"); //prints that the SD card has failed to the serial monitor

    debugLCD("SD card failed"); //prints that the SD card has failed to the LCD 

    while (1) {

      //create an endless loop with nothing to do if file does not open

    } //end while loop


  } //end if


  else {  //prints message that the SD has been initialised

    debugln("SD initialised"); //prints message that the SD card has been initialised to the serial monitor

    debugLCD("SD initialised"); //prints message that the SD card has been initialised to the LCD

    delay(userDelay); //delay for user optimisation

  }  //end else


  clearLCD(); //clears the LCD


  if (RTC.read(tm)) { //prints message that the RTC has been initialised

    debugln("RTC initialised"); //prints that the RTC has been initialised to the serial monitor

    debugLCD("RTC initialised"); //prints that the RTC has been initialised to the LCD 

    delay(userDelay); //delay for user optimisation

  } //end RTC if


  else { //prints debugging message if RTC fails to initialise 

    debugln("RTC failed"); //prints RTC failed message to the serial monitor

    debugLCD("RTC failed"); //prints RTC failed message to the LCD 

    while (1) {

      //create an endless loop with nothing to do if file does not open

    } //end while loop


  } //end else 


  clearLCD(); //clears the LCD

} //end initialisation()


// CREATE FILE AND NAME() FUNCTION:
/*******************************************************************************************************************************************************************************/

void createFileAndName() { //creates file and names the file according to the date

  char fileName[13]; //buffer to store file name

  sprintf(fileName, "%02d-%02d-%d.txt", tm.Day, tm.Month, year());  //formats and concatenates the RTC data to create the file name


  if (!SD.exists(fileName)) { //creates file if the file is not already created

    debug("Creating file..."); //prints that the file is being created to the serial monitor

    debug("File name: "); //prints the beginning of the sentence to the serial monitor

    debugln(fileName); //prints the file name to the serial monitor

  } //end SD if


  else { //appends data to already existing file

    debug("The file: "); //prints the beginning of the sentence to the file name

    debug(fileName); //prints file name to the serial monitor

    debugln(" already exists. Opening file..."); //prints message to the serial monitor

  } //end else


  debugLCD(fileName); //prints the file name to the top left of the LCD


// SD CARD FIRST WRITE- HEADER:
/********************************************************************************************************************************************************************************/
  dataFile = SD.open(fileName); //opens the dataFile named after the current date on the RTC


  if (dataFile) { //if the dataFile opens it will print the datalogging header

    dataFile.println(); // Helps format the data if more than one logging process has been utilised in the same file.

    dataFile.println("TEMPERATURE DATA LOGGER"); //prints the datalogging header to the dataFile

    dataFile.close(); //closes the data file to avoid data corruption

    debugln("TEMPERATURE DATA LOGGER"); //prints datalogging header to the serial monitor

  } //end dataFile if


  else { //if the file does not open 

    debugln("Error opening and writing to file"); //prints error message to the serial monitor

    clearLCD(); //clears the CLCD to print the error message

    debugLCD("File write error"); //prints error message to the LCD

    while (1) {

      //create an endless loop with nothing to do if file does not open

    } //end while loop


  }  //end else


} //end createFileAndName()


// GET AVERAGE READING() FUNCTION:
/********************************************************************************************************************************************************************************/

void getAverageReading() { //prints and logs average temperature data to the file 

  lcd.setCursor(0, 1); //sets the cursor below the file name printed at the lop left of the LCD 


// RTC TIME INFO PRINT TO LCD:
/********************************************************************************************************************************************************************************/
 
  char displayTime[9]; //placeholder for displaying real time

  RTC.read(tm); //reads the time from the RTC

  sprintf(displayTime, "%02d:%02d:%02d", tm.Hour, tm.Minute, tm.Second); //concatenates and formats the data of the real time clock

  debugLCD(displayTime); //prints the real time to the LCD


// CALCULATION FOR T2: 
/********************************************************************************************************************************************************************************/
  
  int tempReading = analogRead(T2Pin); //request to read the T2 pin (analog pin A1)

  double tempK = log(10000.0 * ((1024.0 / tempReading - 1))); //temperature in Kelvin from analog pin reading

  tempK = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * tempK * tempK)) * tempK); //redeclaration of tempK to convert pin reading to Kelvin

  double T2 = tempK - 273.15; //T2 calculation result in degrees celsius from Kelvin

/********************************************************************************************************************************************************************************/


  getT1(); //gets the temperature from the request within setup

  requestT1(); //requests temperature in the loop, which will be used in the next loop iteration


// CHARACTER ARRAYS: 
/********************************************************************************************************************************************************************************/
  
  char averageData[63]; //character buffer for sprintf()

  char charLogTime[8]; //placeholder for log time character

  char charAverageT1[9]; //placeholder for averageT1 character

  char charAverageT2[9]; //placeholder for averageT2 character


// DTOSTRF FUNCTIONS: 
/********************************************************************************************************************************************************************************/ 

  dtostrf(logTime, 7, 0, charLogTime); //converts int to char and used to format the logTime data within the lcd screen and the average data character buffer

  dtostrf(averageT1, 8, 2, charAverageT1); //converts float to char as sprintf cannot use float values

  dtostrf(averageT2, 8, 2, charAverageT2); //converts float to char as sprintf cannot use float values


// SPRINTF TO CONCATENATE/FORMAT LOGGED DATA:
/********************************************************************************************************************************************************************************/

  sprintf(averageData, "%02d:%02d:%02d  time(s):%s  T1(ºC):%s  T2(ºC):%s", tm.Hour, tm.Minute, tm.Second, charLogTime, charAverageT1, charAverageT2);


//TAKE SAMPLE:
/********************************************************************************************************************************************************************************/

  if (totalTime() > sampleTime) { //increments the following every 1.25 seconds

    numSamples += 1; //increments the number of samples by 1

    totalT1 += getT1(); //increments the total of T1 by the sample reading

    totalT2 += T2; //increments the total of T2 by the sample reading

    sampleTime += sampleInterval; //increments the sample time by the sample interval

  }  //end total time if


// AVERAGE TEMPERATURE READINGS:
/********************************************************************************************************************************************************************************/

  averageT1 = totalT1 / numSamples;  //calculate total of T1

  averageT2 = totalT2 / numSamples;  //calculate total of T2


// OPEN FILE:
/********************************************************************************************************************************************************************************/

  char fileName[13]; //buffer to store file name

  sprintf(fileName, "%02d-%02d-%d.txt", tm.Day, tm.Month, year());  //formats and concatenates the RTC data to create the file name

  dataFile = SD.open(fileName, FILE_WRITE); //opens file to write to


  if (!SD.exists(fileName)) { //creates file if the file is not already created

    createFileAndName(); //re-runs the createFileAndName() user defined function 

  } //end SD if

  else {

    //do nothing

  }


// WRITE AND PRINT DATA TO SD & LCD:
/********************************************************************************************************************************************************************************/

  if (dataFile) {  //checks if the file is available

    if (totalTime() > logTime) {  //prints the data within the logging interval

      dataFile.println(averageData);  //prints the average data to the dataFile

      debugln(averageData); //prints the average data to the serial monitor

      lcd.setCursor(8, 1); //sets the cursor so the LCD displays the time at the bottom right of the LCD 

      debugLCD(charLogTime); //prints the log time to the LCD 

      debugLCD("s"); //prints s to format the printed log time on the LCD 

      logTime += logInterval; //increments the log time by the log interval

    }  //end total time if


    if (numSamples > 7) {  //resets the number of samples and the total temperature readings

      numSamples = 0; //resets the number of samples back to 0

      totalT1 = 0; //resets the total of T1 back to 0

      totalT2 = 0; //resets the total of T2 back to 0

    }  //end numSamples if


  }  //end dataFile if


  else {  //if the file does not open

    debugln("Error opening and writing to file"); //prints error message to the serial monitor

    clearLCD(); //clears the LCD to print the error message

    debugLCD("File write error"); //prints error message to the LCD

    while (1) {

      //create an endless loop with nothing to do if file does not open
    
    }  //end while loop


  }  //end else


  dataFile.close(); //closes the data file at the end to avoid file corruption 

}  //end getAverageReading()


// SETUP() FUNCTION
/********************************************************************************************************************************************************************************/

void setup() {  //runs once at the start of the program

  initialisation(); //runs initialisation() user defined function in setup

  createFileAndName();  //runs createFileAndName() user defined function in setup

  requestT1();  //requests data before first loop iteration to print data at the log interval

  sensors.setWaitForConversion(false);

}  //end setup


// LOOP() FUNCTION
/********************************************************************************************************************************************************************************/

void loop() {  //loops getAverageReading() user defined function and displays info on the 16x2 LCD screen

  getAverageReading(); //runs the getAverageReading() user defined function

}  //end loop


// END OF PROGRAM
/********************************************************************************************************************************************************************************/

there is the mkdir() function to create a directory and open() can include the path with the directories in the filename

That is where I'm confused. I tried it out and, as the file name is an sprint function, if I try use that, it says that TempData was not declared in this scope. However, if I try and use quotation marks, the directory is created but then the file name is "fileName.txt". I'm pretty sure there is a way around this, but I've been running off of 8 hours of sleep and caffeine for the past 3 days, so I just can't be bothered playing around with the syntax for ages- especially considering I'm new to arduino and programming.

show us a simple code you used for that...

here is an example in wokwi

click to see the code
#include <SD.h>
#define CS_PIN 10

void createDirectoryAndFile(const char *dirName) {
  // Create a directory
  Serial.print(dirName);
  if (SD.mkdir(dirName)) {
    Serial.println(" Directory created successfully");
  } else {
    Serial.println(" => Error creating directory");
    return;
  }
}

void dumpFile(const char * path) {
  File file = SD.open(path, FILE_READ);
  if (file) {
    Serial.print("\n\nContent of: "); Serial.println(path);
    Serial.println("----------------------");
    while (file.available()) Serial.write(file.read());
    Serial.println("----------------------");
    file.close();
  } else {
    Serial.print("error reading "); Serial.println(path);
  }
}

void setup() {
  Serial.begin(115200);

  Serial.print("Initializing SD card... ");

  if (!SD.begin(CS_PIN)) {
    Serial.println("Card initialization failed!");
    while (true);
  }
  Serial.println("initialization done.");

  createDirectoryAndFile("DIR1");
  createDirectoryAndFile("DIR2");

  File file = SD.open("DIR1/log1.txt", FILE_WRITE);
  if (file) {
    file.println("This content is in file DIR1/log1.txt");
    file.close();
    Serial.println("Done writing in DIR1/log1.txt.");
  } else {
    Serial.println("error creating DIR1/log1.txt.");
  }

  file = SD.open("DIR2/log1.txt", FILE_WRITE);
  if (file) {
    file.println("This content is in file DIR2/log1.txt");
    file.close();
    Serial.println("Done writing in DIR2/log1.txt.");
  } else {
    Serial.println("error creating DIR2/log1.txt.");
  }

  dumpFile("DIR1/log1.txt");
  dumpFile("DIR2/log1.txt");
}


void loop() {}

The issue I'm having is, as the file name is not constant and the directory name is, whenever I try and open the file in the directory:

dataFile = SD.open(directoryName/fileName, FILE_WRITE);

it says "invalid operands of types 'char [9]' and 'char [13]' to binary 'operator/'

I use the sprintf for the file name:

char fileName[13]; //buffer to store file name
  sprintf(fileName, "%02d-%02d-%d.txt", tm.Day, tm.Month, year());  //formats and concatenates the RTC data to create the file name

and use a character array for the directory name:

char directoryName[9] = "TempData";

I'm unable to use quotation marks in making the directory name (or I'm just unaware of the syntax) as if I try "TempData"/fileName, it gives the binary operator error.

Would it be possible to use pointers in the SD.open function? I'm not sure

build the full path in a c-string buffer, something like that

char directoryName[] = "TempData";
char fileName[13]; //buffer to store file name
sprintf(fileName, "%02d-%02d-%d.txt", tm.Day, tm.Month, year());  //formats and concatenates the RTC data to create the file name

char fullPath[50]; //adjust 50 to your needs
strlcpy(fullPath, directoryName, sizeof fullPath);
strlcat(fullPath, "/", sizeof fullPath);
strlcat(fullPath, fileName, sizeof fullPath);
File dataFile = SD.open(fullPath, FILE_WRITE);

try this to see the result

void setup() {
  Serial.begin(115200);

  char directoryName[] = "TempData";
  char fileName[13]; //buffer to store file name
  sprintf(fileName, "%02d-%02d-%d.txt", 5, 2, 24);

  char fullPath[50]; //adjust 50 to your needs
  strlcpy(fullPath, directoryName, sizeof fullPath);
  strlcat(fullPath, "/", sizeof fullPath);
  strlcat(fullPath, fileName, sizeof fullPath);

  Serial.println(fullPath);
}

void loop() {}

you should see in the serial monitor

TempData/05-02-24.txt

The comments in your code are cute. But be careful with that. That character at the end has a meaning. If one of those comments with a line continuation gets next to a line of real code it will cause it to be commented out. It's a confusing issue to hunt down.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.