/* _______________________________________________________________________________________________________________________________________________________________________
ㅤㅤ /\ \ / /\
ㅤ ㅤ|_\ \ 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: \
....................________/
-chipSelect 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 LINKING PIN D9 AND 5V \______________
-104pF CERAMIC CAPACITOR LIKNING POS AND NEG \
_____________________________________________/
// 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 <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 year() tmYearToCalendar(tm.Year) - 2000 //gives the year as a 2 digit integer
#define degrees 0xDF
// DEBUG STATE:
/********************************************************************************************************************************************************************************/
// with this define constant, the user has the ability to either send debugging information to the serial monitor or to the LCD. The serial monitor sends more detailed debugging
// information, at a cost of decreased storage; the LCD prints vague error messages, but allows the debugging info to be viewed remotely and increases program storage
#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 prin
#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
// GENERAL DATALOGGER GLOBAL VARIABLES:
/********************************************************************************************************************************************************************************/
File dataFile; //the file we will be using to create the filename and write to
LiquidCrystal lcd(A0, A1, A2, A3, A4, A5); //set pins on the lcd
tmElements_t tm; //time elements
const short chipSelect = 10; //chip select pin for the micro SD card adapter module
const short buzzer = 8; //pin for buzzer
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 take
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
// TEMPERATURE DATALOGGER GLOBAL VARIABLES:
/********************************************************************************************************************************************************************************/
OneWire oneWire(ONE_WIRE_BUS); //data communication
DallasTemperature sensors(&oneWire); //used for the DS18B20 sensor
static float averageT1; //the average of the sum of samples of temperature sensor 1, divided by the number of samples
static float totalT1 = 0; //the total of the sum of samples of temperature sensor 1
// INITIALISATION FUNCTION:
/********************************************************************************************************************************************************************************/
void initialisation() { //used in setup to initialise the RTC and SD card
sensors.setWaitForConversion(false);
pinMode(buzzer, OUTPUT);
delay(userDelay); //delay for user optimisation
Serial.begin(115200); //begins serial communication
lcd.begin(16, 2); //begins LCD communication
SD.begin();
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
clearLCD(); //clears the LCD
if (!SD.begin(chipSelect)) { //checks if SD card is available and prints error message if not
debugln(F("SD card failed. Check hardware")); //prints that the SD card has failed to the serial monitor
debugLCD(F("SD card failed")); //prints that the SD card has failed to the LCD
while (1) {
digitalWrite(buzzer, HIGH);
delay(100);
digitalWrite(buzzer, LOW);
delay(userDelay);
} //end while (1) loop
} //end if (!SD.begin(chipSelect))
clearLCD(); //clears the LCD
if (!RTC.read(tm)) { //prints message that the RTC has been initialised
debugln(F("RTC failed. Check hardware")); //prints RTC failed message to the serial monitor
debugLCD(F("RTC failed")); //prints RTC failed message to the LCD
while (1) {
digitalWrite(buzzer, HIGH);
delay(100);
digitalWrite(buzzer, LOW);
delay(userDelay);
} //end while (1) loop
} //end if (RTC.read(tm))
clearLCD(); //clears the LCD
SCLCD(); //set cursor of LCD to (0, 0)
sensors.requestTemperatures(); //requests data before first loop iteration to print data at the log interval
} //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
char directoryName[9] = "TempData";
sprintf(fileName, "%02d-%02d-%d.txt", tm.Day, tm.Month, year()); //formats and concatenates the RTC data to create the file name
char fullPath[25]; //adjust 50 to your needs
strlcpy(fullPath, directoryName, sizeof fullPath);
strlcat(fullPath, "/", sizeof fullPath);
strlcat(fullPath, fileName, sizeof fullPath);
if (!SD.exists(fullPath)) { //creates file if the file is not already created
SD.mkdir(directoryName);
debugln(fileName); //prints the file name to the serial monitor
} //end if (!SD.exists(fileName))
else { //appends data to already existing file
debug(F("The file: ")); //prints the beginning of the sentence to the file name
debug(fileName); //prints file name to the serial monitor
debugln(F(" already exists. Opening file...")); //prints message to the serial monitor
} //end else if (!SD.exists(fileName))
// SD CARD FIRST WRITE- HEADER:
/********************************************************************************************************************************************************************************/
dataFile = SD.open(fullPath, FILE_WRITE); //opens the dataFile named after the current date on the RTC
if (!dataFile) { //if the dataFile opens it will print the datalogging header
debugln(F("Error opening and writing header to file")); //prints error message to the serial monitor
clearLCD(); //clears the CLCD to print the error message
debugLCD(F("File write error")); //prints error message to the LCD
while (1) {
digitalWrite(buzzer, HIGH);
delay(100);
digitalWrite(buzzer, LOW);
delay(userDelay);
} //end while (1) loop
} //end if (!dataFile)
dataFile.println(); // Helps format the data if more than one logging process has been utilised that day.
dataFile.println(F("TEMPERATURE DATA LOGGER")); //prints the datalogging header to the dataFile
dataFile.println();
dataFile.close(); //closes the data file to avoid data corruption
} //end createFileAndName()
// GET AVERAGE READING() FUNCTION:
/********************************************************************************************************************************************************************************/
void getAverageReading() { //prints and logs average temperature data to the 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
char directoryName[9] = "TempData";
char fullPath[25]; //adjust 50 to your needs
strlcpy(fullPath, directoryName, sizeof fullPath);
strlcat(fullPath, "/", sizeof fullPath);
strlcat(fullPath, fileName, sizeof fullPath);
if(tm.Hour == 0 && tm.Minute == 0 && tm.Second == 0) {
createFileAndName();
delay(750);
}
// 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
/********************************************************************************************************************************************************************************/
SCLCD();
sensors.getTempCByIndex(0); //gets the temperature from the request within setup
debugLCD(sensors.getTempCByIndex(0));
debugLCD("\337C");
sensors.requestTemperatures(); //requests temperature in the loop, which will be used in the next loop iteration
// CHARACTER ARRAYS:
/********************************************************************************************************************************************************************************/
char averageData[50]; //character buffer for sprintf()
char charLogTime[8]; //placeholder for log time character
char charAverageT1[9]; //placeholder for averageT1 character
// DTOSTRF FUNCTIONS:
/********************************************************************************************************************************************************************************/
dtostrf(logTime, 7, 0, charLogTime); //converts integer to character array and used to format the logTime data within the lcd screen and the average data character buffer
dtostrf(averageT1, 8, 2, charAverageT1); //converts floating point number to a character array as sprintf cannot use float values
// SPRINTF TO CONCATENATE/FORMAT LOGGED DATA:
/********************************************************************************************************************************************************************************/
sprintf(averageData, "TIME: %02d:%02d:%02d LOG:%ss T1:%sºC", tm.Hour, tm.Minute, tm.Second, charLogTime, charAverageT1);
// OPEN FILE:
/********************************************************************************************************************************************************************************/
dataFile = SD.open(fullPath, FILE_WRITE); //opens file to write to
// TAKE SAMPLE:
/********************************************************************************************************************************************************************************/
if (totalTime() > sampleTime) { //increments the following every 1.25 seconds
numSamples += 1; //increments the number of samples by 1
totalT1 += sensors.getTempCByIndex(0); //increments the total of T1 by the sample reading
averageT1 = totalT1 / numSamples; //calculate total of T1
if (sensors.getTempCByIndex(0) <= -10 || sensors.getTempCByIndex(0) >= 70) { //highlights faulty readings so that they can be discarded
dataFile.print(F("*")); //prints to the file
debug(F("*")); //prints to the serial monitor
lcd.setCursor(15, 0); //sets LCD sursor
debugLCD(F("*")); //prints to the LCD
} //end if
debug(numSamples);
debug(" ");
debug(sampleTime);
debug("s");
debug(" ");
debug("T1: ");
debug(sensors.getTempCByIndex(0));
debug("ºC Total T1: ");
debug(totalT1);
sensors.requestTemperatures();
sampleTime += sampleInterval; //increments the sample time by the sample interval
} //end if (totalTime() > sampleTime)
// AVERAGE TEMPERATURE READINGS:
/********************************************************************************************************************************************************************************/
averageT1 = totalT1 / numSamples; //calculate total of T1
// WRITE AND PRINT DATA TO SD & LCD:
/********************************************************************************************************************************************************************************/
lcd.setCursor(0, 1);
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(F("s")); //prints s to format the printed log time on the LCD
logTime += logInterval; //increments the log time by the log interva
} //end if (totalTime() > logTime)
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
} //end if (numSamples > 7)
} //end if (dataFile)
else { //if the file does not open
debugln(F("Error opening and writing to file")); //prints error message to the serial monitor
clearLCD(); //clears the LCD to print the error message
debugLCD(F("File write error")); //prints error message to the LCD
while (1) {
digitalWrite(buzzer, HIGH);
delay(100);
digitalWrite(buzzer, LOW);
delay(userDelay);
} //end while (1) loop
} //end else if (dataFile)
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 createFileDirAndName() user defined function in setup
} //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
/********************************************************************************************************************************************************************************/
I'm using an SD card, which uses the SPI library. I still need to alter the code a little (the description etc as I'm only using one temp sensor now) but it should work apart from that