Good afternoon,
I am currently working on the following project: an incubator which regulates the temperature and the oxygen concentration in a box using, respectively, a heater and a nytrogen tube tank with a valve (which reduces the concentration of oxygen in the box). These are controlled by a mosfet and a relay respectively. Whether we are turning the heater or the nytrogen flow on is determined by using data from a temperature and an oxygen sensor and implementing a PID feedback loop. The target temperature and oxygen concentrations can be finetuned using potentiometers. The data is plotted on an LCD, which also features an SD card, which I would love to make use of to log the acquired data.
The problem is that, as a iterated upon the code with the SD-card code, I have now exceeded the memory limit on the Arduino Uno card.
Do people of this forum see any potential iterations (other than getting an Arduino Mega) that could diminish the use of memory by the 600 bytes that are now excessive? Thanks in advance! If there are any questions regarding the project that would help you answer the question better, feel free to ask!
The code in question
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "DFRobot_OxygenSensor.h"
#include <PID_v1.h>
#include <SdFat.h>
#define SEALEVELPRESSURE_HPA (1013.25)
#define TFT_DC 9
#define TFT_CS 10
#define TFT_MISO 12
#define TFT_MOSI 11
#define TFT_CLK 13
#define TFT_RST 20
#define SD_CS 4 // SD card select pin
#define Oxygen_IICAddress ADDRESS_3
#define COLLECT_NUMBER 10
#define MG_PIN (A0)
#define BOOL_PIN (2)
#define DC_GAIN (8.5)
#define READ_SAMPLE_INTERVAL (50)
#define READ_SAMPLE_TIMES (5)
#define ZERO_POINT_VOLTAGE (0.350)
#define REACTION_VOLTGAE (0.030)
#define FAN_RELAY_PIN 6
#define O2_RELAY_PIN 8
float CO2Curve[3] = { 2.602, ZERO_POINT_VOLTAGE, (REACTION_VOLTGAE / (2.602 - 3)) };
DFRobot_OxygenSensor oxygen;
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);
Adafruit_BME280 bme;
SdFat SD;
File dataFile;
double tempSetpoint;
double o2Setpoint;
unsigned long previousMillis = 0;
const unsigned long updateInterval = 300;
double temperature;
double tempOutput;
double oxygenData;
double o2Output;
int co2_ppm;
double Kp_temp = 5, Ki_temp = 5, Kd_temp = 2;
double Kp_o2 = 1, Ki_o2 = 1, Kd_o2 = 1;
PID tempPID(&temperature, &tempOutput, &tempSetpoint, Kp_temp, Ki_temp, Kd_temp, DIRECT);
PID o2PID(&oxygenData, &o2Output, &o2Setpoint, Kp_o2, Ki_o2, Kd_o2, DIRECT);
void setup() {
Serial.begin(9600);
tempPID.SetOutputLimits(-10000, 10000);
tempPID.SetMode(AUTOMATIC);
o2PID.SetOutputLimits(-10000, 10000);
o2PID.SetMode(AUTOMATIC);
pinMode(FAN_RELAY_PIN, OUTPUT);
pinMode(O2_RELAY_PIN, OUTPUT);
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
pinMode(BOOL_PIN, INPUT);
digitalWrite(BOOL_PIN, HIGH);
if (!bme.begin(0x76)) {
Serial.println(F("a"));
while (1)
;
}
oxygen.begin(Oxygen_IICAddress);
tft.drawRect(tft.width() - 125, 5, 110, 80, ILI9341_WHITE);
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setTextSize(1);
tft.setCursor(tft.width() - 120, 10);
tft.print(F("OBJ TEMP"));
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setCursor(tft.width() - 120, 20);
tft.print(F("CURR TEMP"));
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setCursor(tft.width() - 120, 40);
tft.print(F("OBJ O2"));
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setCursor(tft.width() - 120, 50);
tft.print(F("CURR O2"));
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setCursor(tft.width() - 120, 70);
tft.print(F("CURR CO2"));
// Initialize SD card
if (!SD.begin(SD_CS, SD_SCK_MHZ(25))) { // SD card initialization with 25 MHz speed
Serial.println(F("b"));
while (true)
; // Halt if SD card fails to initialize
}
Serial.println(F("c"));
// Open the file for appending, or create it if it doesn't exist
dataFile = SD.open("dataset.csv", FILE_WRITE);
if (!dataFile) {
Serial.println(F("d"));
while (true)
; // Halt if the file cannot be opened
}
// Write the header to the file (only if the file is empty)
if (dataFile.size() == 0) {
dataFile.println(F("T,O2,CO2"));
}
dataFile.close();
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= updateInterval) {
tft.setTextColor(ILI9341_BLACK, ILI9341_BLACK);
tft.setCursor(tft.width() - 50, 10);
tft.print(tempSetpoint);
tft.setTextColor(ILI9341_BLACK, ILI9341_BLACK);
tft.setCursor(tft.width() - 50, 20);
tft.print(temperature);
if (tempOutput > 0) {
tft.setTextColor(ILI9341_GREEN, ILI9341_BLACK);
} else {
tft.setTextColor(ILI9341_RED, ILI9341_BLACK);
}
tft.setCursor(tft.width() - 120, 30);
tft.print(F("HEATING STATUS"));
tft.setTextColor(ILI9341_BLACK, ILI9341_BLACK);
tft.setCursor(tft.width() - 50, 40);
tft.print(o2Setpoint);
tft.setTextColor(ILI9341_BLACK, ILI9341_BLACK);
tft.setCursor(tft.width() - 50, 50);
tft.print(oxygenData);
tft.setTextColor(ILI9341_BLACK, ILI9341_BLACK);
tft.setCursor(tft.width() - 50, 70);
tft.print(co2_ppm);
if (o2Output < 0) {
tft.setTextColor(ILI9341_GREEN, ILI9341_BLACK);
} else {
tft.setTextColor(ILI9341_RED, ILI9341_BLACK);
}
tft.setCursor(tft.width() - 120, 60);
tft.print(F("O2 STATUS"));
tempSetpoint = ceil((analogRead(A3) / 1023 * 20 + 23) * 10) / 10;
temperature = bme.readTemperature();
int y_temp = (tft.height() - 20) - ((temperature - 18) / (45 - 18)) * (tft.height() - 20);
tempPID.Compute();
controlTemperature(tempOutput);
o2Setpoint = ceil((analogRead(A2) / 1023 * 25) * 10) / 10;
oxygenData = oxygen.getOxygenData(COLLECT_NUMBER);
int y_oxygen = (tft.height() - 20) - ((oxygenData) / (25)) * (tft.height() - 20);
o2PID.Compute();
controlO2(o2Output);
co2_ppm = MGGetPercentage(MGRead(MG_PIN), CO2Curve);
int y_co2 = (tft.height() - 20) - ((co2_ppm) / (1500)) * (tft.height() - 20);
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setCursor(tft.width() - 50, 10);
tft.print(tempSetpoint);
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setCursor(tft.width() - 50, 20);
tft.print(temperature);
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setCursor(tft.width() - 50, 40);
tft.print(o2Setpoint);
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setCursor(tft.width() - 50, 50);
tft.print(oxygenData);
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setCursor(tft.width() - 50, 70);
tft.print(co2_ppm);
dataFile = SD.open("dataset.csv", FILE_WRITE);
if (dataFile) {
// Log data to the file
dataFile.print(temperature);
dataFile.print(F(","));
dataFile.print(oxygenData);
dataFile.print(F(","));
dataFile.print(co2_ppm);
dataFile.close();
} else {
Serial.println(F("e"));
}
}
}
float MGRead(int mg_pin) {
int i;
float v = 0;
for (i = 0; i < READ_SAMPLE_TIMES; i++) {
v += analogRead(mg_pin);
delay(READ_SAMPLE_INTERVAL);
}
v = (v / READ_SAMPLE_TIMES) * 5 / 1024;
return v;
}
int MGGetPercentage(float volts, float *pcurve) {
if ((volts / DC_GAIN) >= ZERO_POINT_VOLTAGE) {
return pow(10, ((volts / DC_GAIN) - pcurve[1]) / pcurve[2] + pcurve[0]);
} else {
return pow(10, ((volts / DC_GAIN) - pcurve[1]) / pcurve[2] + pcurve[0]);
}
}
void controlTemperature(double output) {
if (output > 10) {
digitalWrite(FAN_RELAY_PIN, HIGH);
}
if (output < 10 && output > 0) {
analogWrite(FAN_RELAY_PIN, 128);
} else {
digitalWrite(FAN_RELAY_PIN, LOW);
}
}
void controlO2(double output) {
if (output < 0) {
digitalWrite(O2_RELAY_PIN, HIGH);
} else {
digitalWrite(O2_RELAY_PIN, LOW);
}
}