I'm trying to optimise my program so that it saves as much memory as possible but performs the same functions as I want to use an IR receiver and remote to remotely control it, however, I don't have enough dynamic memory. I'm new to Arduino, so any tips on optimising the code in any way to save dynamic memory, decrease compiler times etc, please let me know. Thank you!
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 LINKING PIN D9 AND 5V \______________
-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 <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 T2Pin A0 //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:
/********************************************************************************************************************************************************************************/
// 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(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 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 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
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. Check hardware"); //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 (1) loop
} //end if (!SD.begin(CS))
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 if (!SD.begin(CS))
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 if (RTC.read(tm))
else { //prints debugging message if RTC fails to initialise
debugln("RTC failed. Check hardware"); //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 (1) loop
} //end else if (RTC.read(tm))
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 if (!SD.exists(fileName))
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 if (!SD.exists(fileName))
debugLCD(fileName); //prints the file name to the top left of the LCD
// SD CARD FIRST WRITE- HEADER:
/********************************************************************************************************************************************************************************/
dataFile = SD.open(fileName, 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
dataFile.println(); // Helps format the data if more than one logging process has been utilised that day.
dataFile.println();
dataFile.println("TEMPERATURE DATA LOGGER"); //prints the datalogging header to the dataFile
dataFile.println();
dataFile.close(); //closes the data file to avoid data corruption
debugln("TEMPERATURE DATA LOGGER"); //prints datalogging header to the serial monitor
} //end if (dataFile)
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 (1) loop
} //end else if (dataFile)
} //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
/********************************************************************************************************************************************************************************/
sensors.getTempCByIndex(0); //gets the temperature from the request within setup
sensors.requestTemperatures(); //requests temperature in the loop, which will be used in the next loop iteration
// CHARACTER ARRAYS:
/********************************************************************************************************************************************************************************/
char averageData[65]; //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 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
dtostrf(averageT2, 8, 2, charAverageT2); //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 T2:%sºC", tm.Hour, tm.Minute, tm.Second, charLogTime, charAverageT1, charAverageT2);
// 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 createFileDirAndName() user defined function
} //end if (!SD.exists(fileName))
else {
//do nothing
} //end else if (!SD.exists(fileName))
// 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
totalT2 += T2; //increments the total of T2 by the sample reading
sampleTime += sampleInterval; //increments the sample time by the sample interval
averageT1 = totalT1 / numSamples; //calculate total of T1
averageT2 = totalT2 / numSamples; //calculate total of T2
if (sensors.getTempCByIndex(0) <= -10 || sensors.getTempCByIndex(0) >= 70 || T2 <= -10 || T2 >= 70) { //highlights faulty readings so that they can be discarded
dataFile.print("*"); //prints to the file
debug("*"); //prints to the serial monitor
lcd.setCursor(15, 0); //sets LCD sursor
debugLCD("*"); //prints to the LCD
} //end if (sensors.getTempCByIndex(0) || T2 <= -10 || sensors.getTempCByIndex(0) || T2 >= 70)
} //end if (totalTime() > sampleTime)
// AVERAGE TEMPERATURE READINGS:
/********************************************************************************************************************************************************************************/
averageT1 = totalT1 / numSamples; //calculate total of T1
averageT2 = totalT2 / numSamples; //calculate total of T2
// 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 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
totalT2 = 0; //resets the total of T2 back to 0
} //end if (numSamples > 7)
} //end if (dataFile)
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 (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
sensors.requestTemperatures(); //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
/********************************************************************************************************************************************************************************/