#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
// ===== Pin Definitions =====
#define ONE_WIRE_BUS 2
#define BTN_UP 3
#define BTN_DOWN 4
#define BTN_FORCE_DEFROST 5
#define RELAY_COMP 7
#define RELAY_DEFROST 8
#define FAN_PIN 9
// ===== LCD =====
LiquidCrystal_I2C lcd(0x27, 16, 4);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// ===== EEPROM =====
#define ADDR_SET_TEMP 0
#define ADDR_SET_DIFF 1
// ===== User Settings =====
int setTemp = 5;
int setDiff = 2;
// ===== State Variables =====
bool adjustDiff = false;
bool cooling = false;
bool defrosting = false;
// ===== Timing =====
unsigned long lastTempRead = 0;
unsigned long compressorStart = 0;
unsigned long minCompressorOffStart = 0;
unsigned long totalCompressorRun = 0;
unsigned long defrostStart = 0;
const unsigned long TEMP_READ_INTERVAL = 1000;
const unsigned long MIN_OFF_TIME = 120000; // 2 minutes
const unsigned long MIN_ON_TIME = 60000; // 1 minute
const unsigned long DEFROST_INTERVAL = 6UL * 60UL * 60UL * 1000UL; // 6 hours
const unsigned long DEFROST_DURATION = 10UL * 60UL * 1000UL; // 10 minutes
unsigned long lastAutoDefrostCheck = 0;
// ===== Button Debounce =====
unsigned long lastBtnCheck = 0;
const unsigned long BTN_DELAY = 200;
// ===== Utilities =====
void saveEEPROM(byte addr, byte val) {
if (EEPROM.read(addr) != val) EEPROM.write(addr, val);
}
String fmtTime(unsigned long ms) {
unsigned long s = ms / 1000;
int m = s / 60;
int sec = s % 60;
char b[7];
sprintf(b, "%02d:%02d", m, sec);
return String(b);
}
// ===== Setup =====
void setup() {
lcd.init();
lcd.backlight();
sensors.begin();
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
pinMode(BTN_FORCE_DEFROST, INPUT_PULLUP);
pinMode(RELAY_COMP, OUTPUT);
pinMode(RELAY_DEFROST, OUTPUT);
pinMode(FAN_PIN, OUTPUT);
digitalWrite(RELAY_COMP, LOW);
digitalWrite(RELAY_DEFROST, LOW);
digitalWrite(FAN_PIN, LOW);
// Load EEPROM
setTemp = EEPROM.read(ADDR_SET_TEMP);
if (setTemp < -20 || setTemp > 50) setTemp = 5;
setDiff = EEPROM.read(ADDR_SET_DIFF);
if (setDiff < 1 || setDiff > 10) setDiff = 2;
minCompressorOffStart = millis(); // enforce initial off time
}
// ===== Display =====
void display(float temp) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Temp:");
lcd.print(temp, 1);
lcd.print((char)223);
lcd.print("C");
lcd.setCursor(0, 1);
lcd.print("Set:");
lcd.print(setTemp);
lcd.print((char)223);
lcd.print(" Df:");
lcd.print(setDiff);
lcd.setCursor(0, 2);
if (defrosting) lcd.print("Status: DEFROST ");
else lcd.print(cooling ? "Status: COOLING" : "Status: IDLE ");
lcd.setCursor(0, 3);
lcd.print("Comp:");
lcd.print(fmtTime(totalCompressorRun));
}
// ===== Start & Stop Compressor =====
void startCompressor() {
compressorStart = millis();
cooling = true;
digitalWrite(FAN_PIN, HIGH);
digitalWrite(RELAY_COMP, HIGH);
}
void stopCompressor() {
cooling = false;
digitalWrite(RELAY_COMP, LOW);
digitalWrite(FAN_PIN, LOW);
totalCompressorRun += millis() - compressorStart;
minCompressorOffStart = millis();
}
// ===== Start Defrost =====
void startDefrost(bool manual) {
defrosting = true;
defrostStart = millis();
stopCompressor();
digitalWrite(RELAY_DEFROST, HIGH);
digitalWrite(FAN_PIN, LOW);
if (!manual) lastAutoDefrostCheck = millis();
}
// ===== Stop Defrost =====
void stopDefrost() {
defrosting = false;
digitalWrite(RELAY_DEFROST, LOW);
minCompressorOffStart = millis(); // allow safe compressor restart
}
// ===== Loop =====
void loop() {
// ---- BUTTONS ----
if (millis() - lastBtnCheck > BTN_DELAY) {
bool up = digitalRead(BTN_UP) == LOW;
bool down = digitalRead(BTN_DOWN) == LOW;
bool force = digitalRead(BTN_FORCE_DEFROST) == LOW;
if (up && down) { // toggle mode
adjustDiff = !adjustDiff;
} else if (up) {
if (adjustDiff) {
setDiff = constrain(setDiff + 1, 1, 10);
saveEEPROM(ADDR_SET_DIFF, setDiff);
} else {
setTemp = constrain(setTemp + 1, -20, 50);
saveEEPROM(ADDR_SET_TEMP, setTemp);
}
} else if (down) {
if (adjustDiff) {
setDiff = constrain(setDiff - 1, 1, 10);
saveEEPROM(ADDR_SET_DIFF, setDiff);
} else {
setTemp = constrain(setTemp - 1, -20, 50);
saveEEPROM(ADDR_SET_TEMP, setTemp);
}
}
if (force && !defrosting) startDefrost(true);
lastBtnCheck = millis();
}
// ---- TEMP READ ----
static float temp = 0;
if (millis() - lastTempRead > TEMP_READ_INTERVAL) {
sensors.requestTemperatures();
temp = sensors.getTempCByIndex(0);
lastTempRead = millis();
if (temp == -127) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("SENSOR ERROR!");
lcd.setCursor(0, 1);
lcd.print("Cooling disabled");
stopCompressor();
return;
}
}
// ---- DEFROST ACTIVE ----
if (defrosting) {
if (millis() - defrostStart >= DEFROST_DURATION) stopDefrost();
display(temp);
return;
}
// ---- AUTO DEFROST ----
if (millis() - lastAutoDefrostCheck >= DEFROST_INTERVAL) {
startDefrost(false);
return;
}
// ---- COOLING LOGIC ----
unsigned long now = millis();
if (!cooling) {
// Conditions to start cooling
if (temp > setTemp + setDiff &&
now - minCompressorOffStart >= MIN_OFF_TIME) {
startCompressor();
}
} else {
// Conditions to stop cooling
if (temp <= setTemp &&
now - compressorStart >= MIN_ON_TIME) {
stopCompressor();
}
}
// Update runtime when compressor is running
if (cooling) {
totalCompressorRun += now - compressorStart;
compressorStart = now;
}
display(temp);
}