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
/********************************************************************************************************************************************************************************/