Hello, I am new to Arduino and was writing a Arduino script and got the following when compiling:
Sketch uses 31006 bytes (96%) of program storage space. Maximum is 32256 bytes.
Global variables use 1827 bytes (89%) of dynamic memory, leaving 221 bytes for local variables. Maximum is 2048 bytes.
I saw from a different post that for memory you should keep it below 70%-80% for dynamic memory. I am hoping someone can give me tips on how to optimize my code so it doesn't go over that limit.
Full code:
#include <Wire.h>
#include <QMC5883LCompass.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_TSL2591.h>
#include <math.h> // For atan2() and degrees()
#include "Arduino.h"
#include "MHZCO2.h"
#include "Adafruit_BME680.h"
#include <SPI.h>
#include <SoftwareSerial.h>
#include <SD.h>
//O2
// Define RX and TX pins for SoftwareSerial
const uint8_t pinRX = 5; // Arduino RX <- Sensor TXD (P3-2)
const uint8_t pinTX = 6; // Arduino TX -> Sensor RXD (P3-3)
SoftwareSerial sensorSerial(pinRX, pinTX); // RX, TX
// Packet structure as per US1010 datasheet
typedef struct {
uint8_t header;
uint8_t len;
uint8_t cmd;
uint8_t grData[20];
uint8_t checksum;
} O2Sensor_t;
struct O2data {
float o2percentage;
float flowrate;
};
O2Sensor_t snsPacket;
// State machine variables
uint8_t state = 0;
uint8_t idx = 0;
uint8_t numBytes = 0;
//END O2
// SD
const uint8_t chipSelect = 10;
// Light Sensor
Adafruit_TSL2591 tsl = Adafruit_TSL2591(0x29); // Corrected I2C address for TSL2591
#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10
#define SEALEVELPRESSURE_HPA (1027.25)
Adafruit_BME680 bme680(&Wire); // I2C
// Magnetometer
QMC5883LCompass compass;
// Use #define for memory efficiency
#define MQ4Pin A0 // Methane
#define MQ8Pin A1 // Hydrogen
#define MQ7Pin A2 // Carbon Monoxide
#define MQ137Pin A3 // Ammonia
#define UVON 7
#define AON 8
#define Ozone 4
//co2 sensor vars
// adjust to calibrate.
#define MAX_CONCENTRATION 5000 // Max CO2 concentration in PPM (adjust as needed)
//
// interrupt variables
volatile uint32_t period;
volatile uint32_t width;
//co2 funcs
void IRQ()
{
static uint32_t lastPeriod = 0;
static uint32_t start = 0;
uint32_t now = micros();
if (digitalRead(3) == HIGH)
{
period = now - lastPeriod;
lastPeriod = now; // <-- FIX: update lastPeriod
start = now;
}
else
{
width = micros() - start;
}
}
float CO2_PPM()
{
noInterrupts();
uint32_t TimeHigh = width; // microseconds
uint32_t TimePeriod = period; // microseconds
interrupts();
float concentration = (MAX_CONCENTRATION * (TimeHigh - 2000)) / (TimePeriod - 4000);
return concentration;
}
// end of co2 funcs
//bme func:
void printValues() {
if (! bme680.performReading()) {
Serial.println(F("Failed to perform reading :("));
return;
}
Serial.print(F("Temperature = "));
Serial.print(bme680.temperature);
Serial.println(F(" *C"));
Serial.print(F("Pressure = "));
Serial.print(bme680.pressure / 100.0);
Serial.println(" hPa");
Serial.print(F("Humidity = "));
Serial.print(bme680.humidity);
Serial.println(" %");
Serial.print(F("Gas = "));
Serial.print(bme680.gas_resistance / 1000.0);
Serial.println(F(" KOhms"));
Serial.print(F("IAQ = "));
Serial.print(calculateIAQ((bme680.gas_resistance/1000), 90));
Serial.println(F(" %(relative to 50% being good)"));
Serial.print(F("Approx. Altitude = "));
Serial.print(bme680.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(F(" m"));
Serial.println();
}
//end of bme functions
void setup() {
Serial.begin(9600);
//delay(1000);
//Serial.begin(9600);
//while (!Serial) {} // Wait for serial monitor (useful for some boards)
pinMode(UVON, OUTPUT);
pinMode(AON, OUTPUT);
pinMode(Ozone, OUTPUT);
Serial.println(F("Initializing sensors..."));
// Initialize magnetometer
Wire.begin();
compass.init(); // Initialize the QMC5883L magnetometer
Serial.println(F("QMC5883L magnetometer initialized."));
//bme 280
if (!bme680.begin()) {
Serial.println(F("Could not find a valid BME680 sensor, check wiring!"));
while (1);
}
// Set up oversampling and filter initialization
bme680.setTemperatureOversampling(BME680_OS_8X);
bme680.setHumidityOversampling(BME680_OS_2X);
bme680.setPressureOversampling(BME680_OS_4X);
bme680.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme680.setGasHeater(320, 150); // 320*C for 150 ms
Serial.println(F("BME680 initialized..."));
// Initialize light sensor
if (!tsl.begin()) {
Serial.println(F("TSL2591 not found! Check wiring or address."));
while (1);
}
configureLightSensor();
// co2
Serial.println(__FILE__);
Serial.print(F("MHZCO2_LIB_VERSION: "));
Serial.println(MHZCO2_LIB_VERSION);
Serial.println();
attachInterrupt(digitalPinToInterrupt(3), IRQ, CHANGE);
//end co2
//o2
sensorSerial.begin(9600);
// Populate our transmit packet template: [0x11, 0x01, 0x01, checksum]
snsPacket.header = 0x11; // Read command
snsPacket.len = 0x01; // Data length = 1 (only CMD)
snsPacket.cmd = 0x01; // Sub-command
// Checksum = 0xFF - (sum of header + len + cmd) + 1
uint8_t sum = snsPacket.header + snsPacket.len + snsPacket.cmd;
snsPacket.checksum = 0xFF - sum + 1;
delay(200); // Allow bootloader to finish
Serial.println(F("US1010 o2 sensor Setup complete."));
//end o2
//SD
Serial.print(F("Initializing SD card..."));
if (!SD.begin(chipSelect)) {
Serial.println(F("initialization failed. Things to check:"));
Serial.println(F("1. is a card inserted?"));
Serial.println(F("2. is your wiring correct?"));
Serial.println(F("3. did you change the chipSelect pin to match your shield or module?"));
Serial.println(F("Note: press reset button on the board and reopen this Serial Monitor after fixing your issue!"));
while (true);
}
Serial.println(F("SD card initialized."));
//end of SD
// Preheat gas sensors
digitalWrite(AON, HIGH);
digitalWrite(Ozone, HIGH);
Serial.println(F("Preheating MQ sensors..."));
//delay(20000);
Serial.println(F("preheating..."));
for (int i = 300;i>=0;i--){
delay(1000);
if (! bme680.performReading()) {
Serial.println(F("Failed to perform reading - bme680 :("));
}
else
Serial.print(F("BME succesfully made reading #"));
Serial.println((i));
}
//delay(20000);
Serial.println(F("Sensor readings in ppm:"));
}
void loop() {
// Multiplex Ammonia sensor and UV sensor
digitalWrite(AON, HIGH);
delay(30);
float ammoniaVoltage = analogRead(MQ137Pin) * (5.0 / 1023.0);
digitalWrite(AON, LOW);
delay(30);
digitalWrite(UVON, HIGH);
delay(30);
uint16_t UVvoltage = analogRead(MQ137Pin);
delay(10);
uint16_t UVvoltage2 = analogRead(MQ137Pin);
delay(10);
uint16_t UVvoltage3 = analogRead(MQ137Pin);
float ave = (UVvoltage + UVvoltage2 + UVvoltage3) / 3.0;
digitalWrite(UVON, LOW);
delay(30);
digitalWrite(Ozone, HIGH);
float ozoneVoltage = analogRead(MQ137Pin) * (5.0 / 1023.0);
digitalWrite(Ozone, LOW);
// Gas Sensors
float methaneVoltage = analogRead(MQ4Pin) * (5.0 / 1023.0);
float hydrogenVoltage = analogRead(MQ8Pin) * (5.0 / 1023.0);
float carbonMonoxideVoltage = analogRead(MQ7Pin) * (5.0 / 1023.0);
// Convert sensor readings to ppm
float methanePPM = methaneVoltage * 0.5;
float hydrogenPPM = hydrogenVoltage * 0.5;
float carbonMonoxidePPM = carbonMonoxideVoltage * 1.0;
float ammoniaPPM = ammoniaVoltage * 0.5;
float ozonePPM = ozoneVoltage * 0.05;
// Light Sensor
uint16_t lux = tsl.getLuminosity(TSL2591_VISIBLE);
uint16_t broadband = tsl.getLuminosity(TSL2591_FULLSPECTRUM);
uint16_t infrared = tsl.getLuminosity(TSL2591_INFRARED);
// Magnetometer
compass.read(); // Read the QMC5883L sensor data
// Retrieve the X, Y, and Z values
int16_t x = compass.getX();
int16_t y = compass.getY();
int16_t z = compass.getZ();
// Calculate the heading (angle in degrees)
float heading = atan2(y, x);
// Convert from radians to degrees
heading = degrees(heading);
// Normalize the heading to be between 0 and 360
if (heading < 0) {
heading += 360;
}
//o2 sensor
// Transmit the read-command packet for o2 sensor
sensorSerial.write(snsPacket.header);
sensorSerial.write(snsPacket.len);
sensorSerial.write(snsPacket.cmd);
sensorSerial.write(snsPacket.checksum);
// Wait for o2 sensor to reply
delay(100);
O2data O2Sensordata;
// Read & process all available bytes through state machine
while (sensorSerial.available()) {
uint8_t ch = sensorSerial.read();
switch (state) {
case 0:
if (ch == 0x16) { // Response header
snsPacket.header = ch;
state = 1;
}
break;
case 1:
snsPacket.len = ch; // Length byte
state = 2;
break;
case 2:
snsPacket.cmd = ch; // Command echo
if (snsPacket.len == 1) {
state = 4; // Skip data bytes
} else {
numBytes = snsPacket.len - 1;
idx = 0;
state = 3;
}
break;
case 3:
snsPacket.grData[idx++] = ch; // Data bytes
if (--numBytes == 0) {
state = 4;
}
break;
case 4:
snsPacket.checksum = ch; // Final checksum byte
O2Sensordata = CheckPacket();
if (O2Sensordata.flowrate != 1234567.75){
// Process and display data
Serial.print(F("O₂ Concentration: "));
Serial.print(O2Sensordata.o2percentage);
Serial.println(" %");
Serial.print(F("Flow Rate: "));
Serial.print(O2Sensordata.flowrate);
Serial.println(F(" L/min"));
}
state = 0; // Reset for next packet
break;
}
}
//end o2
// Print results
Serial.println(F("-----------------------------------------------------------------"));
Serial.println(F(" Gas Sensor Readings "));
Serial.println(F("-----------------------------------------------------------------\n"));
Serial.print(F("Methane (MQ4) : "));
Serial.print(methanePPM);
Serial.println(F(" ppm"));
Serial.print(F("Hydrogen (MQ8) : "));
Serial.print(hydrogenPPM);
Serial.println(F(" ppm"));
Serial.print(F("Carbon Monoxide (MQ7): "));
Serial.print(carbonMonoxidePPM);
Serial.println(F(" ppm"));
Serial.print(F("Ammonia (MQ137) : "));
Serial.print(ammoniaPPM);
Serial.println(F(" ppm"));
Serial.print(F("Ozone (MQ131) : "));
Serial.print(ozonePPM);
Serial.println(F(" ppm"));
Serial.print(F("CO2 : "));
Serial.print(CO2_PPM(), 1);
Serial.println(F(" ppm\n"));
/*
Serial.print(F("O₂ Concentration: "));
Serial.print(O2Sensordata.o2percentage);
Serial.println(" %");
Serial.print(F("Flow Rate: "));
Serial.print(O2Sensordata.flowrate);
Serial.println(F(" L/min"));
*/
Serial.println(F("-----------------------------------------------------------------"));
Serial.println(F(" Light Sensor Readings "));
Serial.println(F("-----------------------------------------------------------------\n"));
Serial.print(F("Full Spectrum : "));
Serial.print(broadband);
Serial.println(F(" (broadband)"));
Serial.print(F("Infrared raw : "));
Serial.println(infrared);
Serial.print(F("Infrared μW/cm² : "));
Serial.println(infrared/154.1);
Serial.print(F("Lux : "));
Serial.print(lux);
Serial.println(F(" lx"));
Serial.print(F("UV index : "));
Serial.println(getUVIndex(ave-120));
Serial.print(F("UV : "));
Serial.print(ave-120);
Serial.println(F(" Analog\n"));
Serial.println(F("-----------------------------------------------------------------"));
Serial.println(F(" Magnetometer Readings "));
Serial.println(F("-----------------------------------------------------------------\n"));
// Output the magnetometer data (X, Y, Z axis)
Serial.print(F("Magnetic Field X : "));
Serial.print(x);
Serial.println(F(" µT"));
Serial.print(F("Magnetic Field Y : "));
Serial.print(y);
Serial.println(F(" µT"));
Serial.print(F("Magnetic Field Z : "));
Serial.print(z);
Serial.println(F(" µT"));
Serial.print(F("Heading (°): "));
Serial.println(heading);
Serial.println(F("-----------------------------------------------------------------"));
Serial.println(F(" BME 680 Readings "));
Serial.println(F("-----------------------------------------------------------------\n"));
printValues();
Serial.println(F(">>>>>>>New_Readings>>>>>>>\n"));
delay(5000);
}
void configureLightSensor() {
tsl.setGain(TSL2591_GAIN_MED);
tsl.setTiming(TSL2591_INTEGRATIONTIME_100MS);
Serial.println(F("TSL2591 configured:"));
Serial.print(F("Gain : Medium\n"));
Serial.print(F("Integration Time : 100ms\n"));
}
O2data CheckPacket() {
static O2data O2returndata;
// Print raw data bytes
Serial.print(F("Raw Data: "));
for (uint8_t i = 0; i < (snsPacket.len - 1); i++) {
if (snsPacket.grData[i] < 0x10) Serial.print('0');
Serial.print(snsPacket.grData[i], HEX);
Serial.print(' ');
}
// Recompute checksum to verify integrity
uint8_t sum = snsPacket.header + snsPacket.len + snsPacket.cmd;
for (uint8_t i = 0; i < (snsPacket.len - 1); i++) {
sum += snsPacket.grData[i];
}
sum = 0xFF - sum + 1;
if (sum == snsPacket.checksum) {
Serial.println(F("[Checksum OK]"));
} else {
Serial.print(F("[Checksum Error: Expected "));
if (sum < 0x10) Serial.print('0');
Serial.print(sum, HEX);
Serial.print(F(", Received "));
if (snsPacket.checksum < 0x10) Serial.print('0');
Serial.print(snsPacket.checksum, HEX);
Serial.println(']');
O2returndata.flowrate = 1234567.8;
return O2returndata;
}
// Parse and display sensor data
if (snsPacket.len >= 9) {
// Oxygen Concentration (D1 and D2)
uint16_t o2_raw = (uint16_t(snsPacket.grData[0]) << 8) | uint16_t(snsPacket.grData[1]);
float o2_concentration = o2_raw / 10.0;
// Flow Rate (D3 and D4)
uint16_t flow_raw = (uint16_t(snsPacket.grData[2]) << 8) | uint16_t(snsPacket.grData[3]);
float flow_rate = flow_raw / 10.0;
// Temperature (D5 and D6)
uint16_t temp_raw = (uint16_t(snsPacket.grData[4]) << 8) | uint16_t(snsPacket.grData[5]);
float temperature = temp_raw / 10.0;
/*
// Display the parsed values
Serial.print(F("O₂ Concentration: "));
Serial.print(o2_concentration);
Serial.println(" %");
Serial.print(F("Flow Rate: "));
Serial.print(flow_rate);
Serial.println(F(" L/min"));
Serial.print(F("Gas Temperature: "));
Serial.print(temperature);
Serial.println(F(" °C"));
*/
O2returndata.o2percentage = o2_concentration;
O2returndata.flowrate = flow_rate;
return O2returndata;
}
/*
Serial.println(); // Blank line between packets
*/
}
int getUVIndex(int analogValue) {
// For values less than 10, return UV index 0
if (analogValue < 10) {
return 0;
}
// For values between defined thresholds, return corresponding UV index
else if (analogValue < 46) {
return 0; // Still considered UV index 0 for values between 10-45
}
else if (analogValue < 65) {
return 1;
}
else if (analogValue < 83) {
return 2;
}
else if (analogValue < 103) {
return 3;
}
else if (analogValue < 124) {
return 4;
}
else if (analogValue < 142) {
return 5;
}
else if (analogValue < 162) {
return 6;
}
else if (analogValue < 180) {
return 7;
}
else if (analogValue < 200) {
return 8;
}
else if (analogValue < 221) {
return 9;
}
else if (analogValue < 240) {
return 10;
}
else {
return 11; // For values 240 and above, return UV index 11+
}
}
// Simple approach to get a relative air quality reading
float calculateIAQ(float gasResistance, float baselineResistance) {
// Higher values indicate better air quality (0-100 scale, with 100 being excellent)
float ratio = gasResistance / baselineResistance;
float iaq = constrain(ratio * 50.0, 0.0, 100.0);
return iaq;
}
I also have an SD card hooked up to it and was wondering, if I could tell the Arduino that I have supplied it some extra storage and it can utilize the SD card for its own uses because this would fix the problem easily in my opinion. Although I don't know if that would work. Thanks to anyone who can help me.