// Smart Onion Storage Controller - ESP32 (Arduino IDE)
// - DHT22 temp/humidity
// - MQ analog gas sensor
// - I2C LCD 16x2
// - 2 relays: fan and mist sprayer
// - SMS alert via SIM800 (Serial1)
// Maintains temperature between 0 - 5 C and humidity 65 - 70% with hysteresis
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "DHT.h"
// ---------------- USER CONFIG - EDIT IF NEEDED ----------------
#define PHONE_NUMBER "+918302308101" // SMS alerts go to this number (your requested)
const int GAS_THRESHOLD = 800; // tune this after measuring baseline (0-4095)
const unsigned long ALERT_COOLDOWN_MS = 10UL * 60UL * 1000UL; // 10 min between SMS alerts
// ---------------- END USER CONFIG --------------------------------
// Pins
#define DHTPIN 4
#define DHTTYPE DHT22
#define MQ_PIN 34 // analog input (ADC)
#define RELAY_FAN 26 // controls fan
#define RELAY_MIST 27 // controls mist sprayer
// GSM Serial (Serial1 on ESP32)
#define GSM_SERIAL Serial1
#define GSM_TX_PIN 17 // ESP32 Tx -> GSM Rx
#define GSM_RX_PIN 16 // ESP32 Rx <- GSM Tx
#define GSM_BAUD 9600
// I2C LCD (common address 0x27). Adjust if your backpack uses 0x3F.
LiquidCrystal_I2C lcd(0x27, 16, 2);
DHT dht(DHTPIN, DHTTYPE);
// Function prototypes (so we can call from setup)
String sendGSMCommand(String cmd, unsigned long timeout = 2000);
void sendAlertSMS(float tempC, float hum, int gasRaw);
// Control setpoints and hysteresis
const float TEMP_TARGET_LOW = 0.0; // lower bound (°C)
const float TEMP_TARGET_HIGH = 5.0; // upper bound (°C)
// We'll use hysteresis band of 0.8°C (adjust if necessary)
const float TEMP_HYST = 0.8;
// Humidity target range (65% - 70%)
const float HUM_LOW = 65.0;
const float HUM_HIGH = 70.0;
const float HUM_HYST = 3.0; // hysteresis to prevent frequent toggles
// State variables
bool fanState = false;
bool mistState = false;
bool alertSent = false;
unsigned long lastAlertMs = 0;
unsigned long lastReadMs = 0;
const unsigned long READ_INTERVAL_MS = 3000; // read sensors every 3 seconds
void setup() {
Serial.begin(115200);
delay(100);
// Initialize GSM serial
GSM_SERIAL.begin(GSM_BAUD, SERIAL_8N1, GSM_RX_PIN, GSM_TX_PIN);
// Pins for relays
pinMode(RELAY_FAN, OUTPUT);
pinMode(RELAY_MIST, OUTPUT);
// Assuming relay module is active HIGH to turn on. If your module is active LOW invert the logic.
digitalWrite(RELAY_FAN, LOW); // OFF
digitalWrite(RELAY_MIST, LOW); // OFF
// LCD and sensor init
lcd.init();
lcd.backlight();
dht.begin();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Onion Storage");
lcd.setCursor(0,1);
lcd.print("Booting...");
delay(1200);
lcd.clear();
// Basic GSM init commands
sendGSMCommand("AT", 1000);
delay(200);
sendGSMCommand("ATE0", 500); // disable echo
delay(200);
sendGSMCommand("AT+CMGF=1", 1000); // SMS text mode
delay(200);
}
void loop() {
unsigned long now = millis();
if (now - lastReadMs >= READ_INTERVAL_MS) {
lastReadMs = now;
readSensorsAndControl();
}
}
// Read sensors, display on LCD, control relays, and handle gas alert
void readSensorsAndControl() {
// Read DHT
float hum = dht.readHumidity();
float tempC = dht.readTemperature();
// If DHT failed, keep previous or show error
bool dhtOk = !(isnan(hum) || isnan(tempC));
// Read gas (MQ) analog value (0 - 4095)
int gasRaw = analogRead(MQ_PIN);
// Display on Serial
if (dhtOk) {
Serial.printf("Temp: %.2f C, Hum: %.2f %% | GasRaw: %d\n", tempC, hum, gasRaw);
} else {
Serial.printf("DHT read failed | GasRaw: %d\n", gasRaw);
}
// Update LCD (two lines)
char line1[17], line2[17];
if (dhtOk) {
// Example: "T: 3.2C H:67.4%"
snprintf(line1, sizeof(line1), "T:%5.1fC H:%4.1f%%", tempC, hum);
} else {
snprintf(line1, sizeof(line1), "T: --.-C H: --.-");
}
// Gas as raw number and percent (approx)
float gasPct = (gasRaw / 4095.0) * 100.0;
snprintf(line2, sizeof(line2), "Gas:%4d %3.0f%%", gasRaw, gasPct);
lcd.setCursor(0,0);
lcd.print(line1);
// clear remainder of line if needed
for (int i = strlen(line1); i < 16; ++i) lcd.print(' ');
lcd.setCursor(0,1);
lcd.print(line2);
for (int i = strlen(line2); i < 16; ++i) lcd.print(' ');
// CONTROL LOGIC:
// Temperature control using hysteresis:
if (dhtOk) {
// Fan control (we assume fan reduces temperature / increases ventilation)
if (!fanState) {
// currently OFF -> check if should turn ON
if (tempC > (TEMP_TARGET_HIGH + TEMP_HYST/2.0)) {
setFan(true);
}
} else {
// currently ON -> check if should turn OFF
if (tempC <= (TEMP_TARGET_HIGH - TEMP_HYST/2.0)) {
setFan(false);
}
}
// Mist control for humidity: Mist increases RH
if (!mistState) {
if (hum < (HUM_LOW - HUM_HYST/2.0)) {
setMist(true);
}
} else {
if (hum >= (HUM_HIGH + HUM_HYST/2.0)) {
setMist(false);
}
}
}
// GAS ALERT: if gas crosses threshold, send SMS (with cooldown)
if (gasRaw >= GAS_THRESHOLD) {
if (!alertSent || (millis() - lastAlertMs) >= ALERT_COOLDOWN_MS) {
sendAlertSMS(tempC, hum, gasRaw);
alertSent = true;
lastAlertMs = millis();
}
} else {
// Optionally reset alertSent if gas returns sufficiently low to allow future alerts
if (alertSent && (millis() - lastAlertMs) >= ALERT_COOLDOWN_MS) {
alertSent = false; // allow future alerts after cooldown
}
}
}
// Turn fan ON/OFF (update both relay and state)
void setFan(bool on) {
fanState = on;
// If your relay is active HIGH to turn on:
digitalWrite(RELAY_FAN, on ? HIGH : LOW);
Serial.printf("Fan turned %s\n", on ? "ON" : "OFF");
}
// Turn mist sprayer ON/OFF
void setMist(bool on) {
mistState = on;
digitalWrite(RELAY_MIST, on ? HIGH : LOW);
Serial.printf("Mist turned %s\n", on ? "ON" : "OFF");
}
// ---------------- GSM helper functions ----------------
// Send an AT command to GSM and return collected response (non-blocking collection for 'timeout' ms)
String sendGSMCommand(String cmd, unsigned long timeout) {
while (GSM_SERIAL.available()) GSM_SERIAL.read(); // flush
Serial.print("GSM -> "); Serial.println(cmd);
GSM_SERIAL.println(cmd);
unsigned long tStart = millis();
String response = "";
while (millis() - tStart < timeout) {
while (GSM_SERIAL.available()) {
char c = (char)GSM_SERIAL.read();
response += c;
}
}
Serial.print("GSM <- "); Serial.println(response);
return response;
}
// Compose and send SMS alert
void sendAlertSMS(float tempC, float hum, int gasRaw) {
// Compose text
char buf[200];
if (isnan(tempC) || isnan(hum)) {
snprintf(buf, sizeof(buf), "ONION ALERT! Gas:%d", gasRaw);
} else {
snprintf(buf, sizeof(buf), "ONION ALERT!\nT:%.1fC H:%.1f%%\nGas:%d", tempC, hum, gasRaw);
}
String msg = String(buf);
// Ensure SMS mode, then issue send
sendGSMCommand("AT+CMGF=1", 1000);
delay(200);
String cmd = String("AT+CMGS=\"") + PHONE_NUMBER + String("\"");
sendGSMCommand(cmd, 1000);
delay(200);
// send message body
GSM_SERIAL.print(msg);
delay(200);
GSM_SERIAL.write(0x1A); // Ctrl+Z to send
Serial.println("SMS send command issued.");
// Wait a bit and optionally read response
delay(5000);
String res = "";
while (GSM_SERIAL.available()) {
res += (char)GSM_SERIAL.read();
}
Serial.print("SMS response: "); Serial.println(res);
}