#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
// === Pin Definitions ===
#define ONE_WIRE_BUS 2
#define BUTTON_UP 3
#define BUTTON_DOWN 4
#define FORCE_DEFROST_BTN 5
#define RELAY_PIN 7 // Compressor
#define DEFROST_PIN 8 // Defrost heater
#define FAN_PIN 9 // Evaporator fan
// === LCD Setup ===
LiquidCrystal_I2C lcd(0x27, 16, 4); // 16x4 LCD
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// === EEPROM Addresses ===
#define EEPROM_TEMP_ADDR 0
#define EEPROM_DIFF_ADDR 1
// === Variables ===
int setTemp;
int setDiff;
bool coolingOn = false;
bool defrosting = false;
bool forceDefrostRequested = false;
bool adjustDiff = false;
unsigned long startupTime = 0;
const int startupDelay = 3000; // 3 seconds
unsigned long compressorOnStart = 0;
unsigned long totalCompressorOnTime = 0;
unsigned long defrostStartTime = 0;
const unsigned long DEFROST_INTERVAL = 2UL * 60UL * 1000UL; // 2 min
const unsigned long DEFROST_DURATION = 30UL * 1000UL; // 30 sec
unsigned long lastDebounce = 0;
const int debounceDelay = 200;
// === Save Only If Changed ===
void saveToEEPROM(byte address, byte value) {
if (EEPROM.read(address) != value) {
EEPROM.write(address, value);
}
}
// === Time Formatter (mm:ss) ===
String formatTime(unsigned long ms) {
unsigned long totalSeconds = ms / 1000;
int minutes = totalSeconds / 60;
int seconds = totalSeconds % 60;
char buffer[6];
sprintf(buffer, "%02d:%02d", minutes, seconds);
return String(buffer);
}
// === Setup ===
void setup() {
lcd.init();
lcd.backlight();
sensors.begin();
pinMode(BUTTON_UP, INPUT_PULLUP);
pinMode(BUTTON_DOWN, INPUT_PULLUP);
pinMode(FORCE_DEFROST_BTN, INPUT_PULLUP);
pinMode(RELAY_PIN, OUTPUT);
pinMode(DEFROST_PIN, OUTPUT);
pinMode(FAN_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
digitalWrite(DEFROST_PIN, LOW);
digitalWrite(FAN_PIN, LOW);
setTemp = EEPROM.read(EEPROM_TEMP_ADDR);
if (setTemp < -20 || setTemp > 50) setTemp = 5;
setDiff = EEPROM.read(EEPROM_DIFF_ADDR);
if (setDiff < 1 || setDiff > 10) setDiff = 2;
startupTime = millis();
}
// === Loop ===
void loop() {
// --- Button Input ---
if (millis() - lastDebounce > debounceDelay) {
bool upPressed = digitalRead(BUTTON_UP) == LOW;
bool downPressed = digitalRead(BUTTON_DOWN) == LOW;
if (upPressed && downPressed) {
adjustDiff = !adjustDiff;
delay(500); // hold to switch mode
} else if (upPressed) {
if (adjustDiff) {
setDiff++;
if (setDiff > 10) setDiff = 10;
saveToEEPROM(EEPROM_DIFF_ADDR, setDiff);
} else {
setTemp++;
if (setTemp > 50) setTemp = 50;
saveToEEPROM(EEPROM_TEMP_ADDR, setTemp);
}
lastDebounce = millis();
} else if (downPressed) {
if (adjustDiff) {
if (setDiff > 1) setDiff--;
saveToEEPROM(EEPROM_DIFF_ADDR, setDiff);
} else {
setTemp--;
if (setTemp < -20) setTemp = -20;
saveToEEPROM(EEPROM_TEMP_ADDR, setTemp);
}
lastDebounce = millis();
}
}
// --- Read Temperature ---
sensors.requestTemperatures();
float currentTemp = sensors.getTempCByIndex(0);
// === Sensor Error Check ===
if (currentTemp == -127.00) {
displaySensorError();
delay(1000); // Retry delay
return;
}
// --- Force Defrost Button ---
if (digitalRead(FORCE_DEFROST_BTN) == LOW) {
forceDefrostRequested = true;
delay(200); // debounce
}
// --- Defrost Active ---
if (defrosting) {
if (millis() - defrostStartTime >= DEFROST_DURATION) {
defrosting = false;
forceDefrostRequested = false;
digitalWrite(DEFROST_PIN, LOW);
startupTime = millis(); // Enforce compressor startup delay
}
digitalWrite(RELAY_PIN, LOW); // Compressor OFF
digitalWrite(FAN_PIN, LOW); // Fan OFF
coolingOn = false;
displayLCD(currentTemp, true);
delay(500);
return;
}
// --- Auto or Manual Defrost Trigger ---
if (totalCompressorOnTime >= DEFROST_INTERVAL || forceDefrostRequested) {
defrosting = true;
defrostStartTime = millis();
digitalWrite(DEFROST_PIN, HIGH);
digitalWrite(RELAY_PIN, LOW);
digitalWrite(FAN_PIN, LOW);
coolingOn = false;
totalCompressorOnTime = 0;
displayLCD(currentTemp, true);
delay(500);
return;
}
// --- Cooling Logic ---
if (millis() - startupTime > startupDelay) {
if (!coolingOn && currentTemp > (setTemp + setDiff)) {
digitalWrite(FAN_PIN, HIGH);
delay(5000);
digitalWrite(RELAY_PIN, HIGH);
coolingOn = true;
compressorOnStart = millis();
} else if (coolingOn && currentTemp <= setTemp) {
digitalWrite(RELAY_PIN, LOW);
digitalWrite(FAN_PIN, LOW);
coolingOn = false;
totalCompressorOnTime += millis() - compressorOnStart;
}
}
// --- Accumulate Runtime ---
if (coolingOn) {
totalCompressorOnTime += millis() - compressorOnStart;
compressorOnStart = millis(); // reset
}
displayLCD(currentTemp, false);
delay(500);
}
// === LCD Display ===
void displayLCD(float currentTemp, bool showDefrost) {
lcd.clear();
// Line 0: Temperature
lcd.setCursor(0, 0);
lcd.print("Temp: ");
lcd.print(currentTemp, 1);
lcd.print((char)223);
lcd.print("C");
// Line 1: Set Temp & Diff
lcd.setCursor(0, 1);
lcd.print("Set:");
lcd.print(setTemp);
lcd.print((char)223);
lcd.print("C Df:");
lcd.print(setDiff);
// Line 2: System Status
lcd.setCursor(0, 2);
if (showDefrost) {
lcd.print("Status: DEFROSTING");
} else {
lcd.print("Status: ");
lcd.print(coolingOn ? "COOLING ON " : "COOLING OFF");
}
// Line 3: Runtime Display
lcd.setCursor(0, 3);
if (showDefrost) {
unsigned long defrostElapsed = millis() - defrostStartTime;
lcd.print("Defrost: ");
lcd.print(formatTime(defrostElapsed));
} else {
unsigned long activeCompTime = coolingOn ? (millis() - compressorOnStart) : 0;
unsigned long totalTime = totalCompressorOnTime + activeCompTime;
lcd.print("Comp: ");
lcd.print(formatTime(totalTime));
}
}
// === Sensor Error Display ===
void displaySensorError() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ERROR: SENSOR");
lcd.setCursor(0, 1);
lcd.print("Check DS18B20");
lcd.setCursor(0, 2);
lcd.print("No signal");
lcd.setCursor(0, 3);
lcd.print("Cooling DISABLED");
}