// Hi! Barb here: this is a quick and dirty implementation of a refrigeration compressor + evaporator fan(s) controller.
// I wrote this code to run it on an arduino nano 168 (previously: attiny45) that I was able to solder on the back of a 5V relay inside the refrigerator's
// control panel. This code relies on an external thermostat already doing its work and is not meant to manage internal
// temperature. It is written to make sure the compressor can get a minimum rest-time between cycles as to not overheat.
// This relay and microcontroller are connected in series with the main thermost's relay, so that they only function when it's calling.
// With this in mind, we assume the thermostat has just called for cooling every time the arduino is booted and leave the
// compressor running. After a set period of s (timerMAX) the compressor is switched off.
// In order to aid thermal transfer and maximize COP while using butane as refrigerant, the system needs to have a higher
// average temperature. Due to this reason, a fan is fitted between the fridge and freezer compartments to move air between
// the two. The fan is turned on after fanStartDelay to prevent rapid heating of the freezer and turned back on when the
// timer hits fanStopAdvance to allow for further freezer sub-cooling.

// updated: 05/03/24
// notes: tweaked timing and added fan startup delay + anticipated shut-off

// updated: 02/07/24
// notes: added temperature-based clog detection and provision for reversing valve

// updated: 04/07/24
// notes: moved from attiny45 to arduino nano 168 to increase memory and serial port

#include <OneWire.h>
#include <DallasTemperature.h>

bool disable = 0;               // bool to store compressor disable status
int timer = 0;                  // active countdown timer, between timerMax and 0
const int timerMAX = 1500;      // (countdown in seconds)
const int relayPin = 3;         // compressor relay output pin (active LOW)
const int fanPin = 2;           // fan MOSFET output pin (active HIGH)
const int alertLED = 4;         // Defrost ongoing warning LED
const int SENSOR = 5;           // digital Input connected to DS18B20 sensor's DQ pin, used to detect clogs in refrigeration cycle
//const int RValve = 3;           // Reversing Valve relay output (relay N.O.; valve normally not reversed)
const int fanStartDelay = 60;   // fan turn-on delay in seconds
const int fanStopAdvance = 30;  // fan turn-off advance in seconds (before the compressor stops, allows to sub-cool the freezer)
const int checkTimeDelay = 300; // after this amount of seconds we start checking condenser temperature
const int delayTime = 750;      // cycle delay in ms (should be 1s, but we need to correct for computing time)

OneWire oneWire(SENSOR);              // setup a oneWire instance
DallasTemperature sensors(&oneWire);  // pass oneWire to DallasTemperature library

float temperature = 0;          // var to store temperature in Celsius
float ambTemp = 0;              // var to store ambient temperature
int minTemp = 35;               // hot gas low-temp limit in celcius (indicative of clog!) usually 40 to 45C when actively running

void setup() {
  // put your setup code here, to run once:

  // firstly, we initialize the needed outputs
  pinMode(relayPin, OUTPUT);
  pinMode(fanPin, OUTPUT);
  pinMode(alertLED, OUTPUT);
  //pinMode(RValve, OUTPUT);
  // then we set them low to keep the compressor on and the fans off at boot
  // the reversing valve is in the non-reversed position by default
  digitalWrite(relayPin, LOW);
  digitalWrite(fanPin, LOW);
  digitalWrite(alertLED, LOW);
  //digitalWrite(RValve, LOW);

  // initialize serial
  Serial.begin(9600);
  Serial.println("serial initialized");
  
  // initialize the dallas temperature sensor
  sensors.begin();
  sensors.requestTemperatures();                      // send command to get temperatures
  ambTemp = sensors.getTempCByIndex(0);           // store ambTemp at setup
  Serial.println("condenser temperature sensor initialized");
  Serial.println("minimum condenser temperature:");
  Serial.println(minTemp);
  Serial.println("ambient temperature:");
  Serial.println(ambTemp);

  // lastly, we set the timer at maximum so that the loop starts counting down
  timer = timerMAX;
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.print("timer: ");
  Serial.println(timer);
  Serial.print("compressor status: ");
  if (disable == 0){
    Serial.println("ON");
  }
  else {
    Serial.println("OFF");
  }
  sensors.requestTemperatures();                      // send command to get temperatures
  temperature = sensors.getTempCByIndex(0);           // read temperature in Celsius
  Serial.print("condenser temperature: ");
  Serial.println(temperature);
  
  // fan control logic:
  // only enable fans after the compressor has been running for a while
  if (timer == (timerMAX - fanStartDelay) && disable == 0) {
    // enable fan(s) after set delay if the compressor is not disabled
    digitalWrite(fanPin, HIGH);
    Serial.println("fans enabled");
  }
  else if (timer == fanStopAdvance && disable == 0) {
    // disable when we are about to stop the compressor
    digitalWrite(fanPin, LOW);
    Serial.println("fans disabled");
  }
  else {
    // do nothing
  }

  // detect clogs by reading condenser temperature and automatically defrost to fix them
  if ((timer < (timerMAX - checkTimeDelay) && temperature < minTemp && disable == 0) || ((timer < timerMAX - fanStartDelay) && temperature < (ambTemp + 1))) {
    digitalWrite(fanPin, HIGH); // keep fans on to defrost and prevent thermostat from satsfying
    digitalWrite(relayPin, HIGH); // compressor off
    Serial.println("clog detected! requesting defrost...");
    Serial.println("compressor off, fans on");
    timer = timerMAX;
    while (timer > 0) {
      timer--;
      Serial.println("counting down, defrost");
      Serial.print("timer: ");
      Serial.println(timer);
      delay (500);
      digitalWrite(alertLED, HIGH); // turn on an LED on front panel to warn of current defrost
      delay (500);
      digitalWrite(alertLED, LOW); // turn off to cause blinking effect
    }
    digitalWrite(fanPin, LOW); // switch off fans
    disable = 1; // set disable bit to 1 to restart the compressor next cycle
    timer = 0; // zero the timer so that we immediately go into an off cycle (if disable == 0) or on cycle (if disable == 1)
    //(also prevents timer from going <0 in case of while cycle starting from a number that is not a multiple of 5)
  }

  // compressor control logic
  if (timer > 0 && disable == 0) {
    // when compressor is active, countdown moves at 1s/cycle
    timer--;
    digitalWrite(relayPin, LOW);
  }
  
  else if (timer > 0 && disable == 1) {
    // when compressor is off, countdown moves at (1/5)s/cycle (5 times faster!)
    timer = timer - 5;
    digitalWrite(relayPin, HIGH);
  }
  
  else if (timer == 0) {
    // reset countdown timer to max once 0 is reached
    Serial.println("countdown complete, resetting timer");
    timer = timerMAX;
    
    // relay control logic: warning! LOW = active!
    if (disable == 0) {
      disable = 1;
      Serial.println("disabled");
      digitalWrite(relayPin, HIGH);
    }
    else if (disable == 1) {
      disable = 0;
      Serial.println("enabled");
      digitalWrite(relayPin, LOW);  // turn compressor on at end of off cycle
      digitalWrite(fanPin, LOW);    // turn fans off at end of off cycle (in case defrost was triggered)
    }
  }
  
  else {
    // protect against underflow or bitflip: if no condition applies, reset to safe values
    timer = 0;
    disable = 0;
    digitalWrite(fanPin, LOW);
    Serial.println("error detected, fans & compressor disabled");
  }
  
  // now wait 1s so that we can roughly count time using the timer variable
  // this is a stopping delay, kinda bad, but we don't have any inputs, hence it shouldn't matter!
  delay(delayTime);
}
