/*
* Auto-Refillable Psychrometer Controller
* Designed for Arduino Uno R3
*
* Hardware:
* - Arduino Uno R3
* - 5V IR Water Sensor (Top - Wetting Sock Tank) on A0
* - 5V IR Water Sensor (Bottom - Reservoir) on A1
* - 3-32V DC-DC Solid State Relay on D8 (controlling 12V pump)
*/
#define PIN_TOP_SENSOR A0
#define PIN_BOTTOM_SENSOR A1
#define PIN_RELAY 8
#define PIN_LED 13 // Use Uno's built-in LED
// Uncomment the next line to use a 10-sec delay for testing instead of 1 hour
#define TEST_MODE
#ifdef TEST_MODE
const unsigned long CONFIRMATION_DELAY_MS = 10000UL; // 10 seconds
#else
const unsigned long CONFIRMATION_DELAY_MS = 3600000UL; // 1 hour
#endif
const float VOLTAGE_THRESHOLD = 2.5;
// Voltage > 2.5V means NO WATER (sensor exposed to air)
// Voltage < 2.5V means WATER DETECTED (sensor submerged)
unsigned long emptyStartTime = 0;
unsigned long pumpStartTime = 0;
bool isCountingDown = false;
bool isPumping = false;
bool isDoubleFillWaiting = false;
unsigned long fillStopTime = 0;
bool hasDoneDoubleFill = false;
void setup() {
Serial.begin(9600);
pinMode(PIN_RELAY, OUTPUT);
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_RELAY, LOW); // Ensure pump is OFF on startup
Serial.println("Psychrometer Controller Initialized.");
}
float getVoltage(int pin) {
int adcValue = analogRead(pin);
return (adcValue * 5.0) / 1023.0;
}
void loop() {
float topVoltage = getVoltage(PIN_TOP_SENSOR);
float bottomVoltage = getVoltage(PIN_BOTTOM_SENSOR);
bool topNoWater = (topVoltage > VOLTAGE_THRESHOLD);
bool bottomNoWater = (bottomVoltage > VOLTAGE_THRESHOLD);
// Safety Interlock: If reservoir is empty, stop everything.
if (bottomNoWater) {
if (isPumping) {
Serial.println("WARNING: Reservoir empty! Stopping pump.");
digitalWrite(PIN_RELAY, LOW);
isPumping = false;
unsigned long fillDuration = millis() - pumpStartTime;
Serial.print("EVENT: FILL_HALTED (Reservoir empty) | Duration: ");
Serial.print(fillDuration / 1000.0, 1);
Serial.println(" s");
}
// Also reset countdown so we don't start immediately if reservoir gets
// refilled but sock is still empty
if (isCountingDown) {
Serial.println("Reservoir empty. Pausing/Resetting timer.");
isCountingDown = false;
}
isDoubleFillWaiting = false;
} else {
// --- Normal Operation ---
// 1. Check if top tank is full while pumping
if (isPumping) {
if (!topNoWater) { // Top sensor < 2.0V (water detected)
Serial.println("Top tank filled! Stopping pump.");
digitalWrite(PIN_RELAY, LOW);
isPumping = false;
unsigned long fillDuration = millis() - pumpStartTime;
Serial.print("EVENT: FILL_COMPLETE | Duration: ");
Serial.print(fillDuration / 1000.0, 1);
Serial.println(" s");
isCountingDown = false; // Reset state
if (!hasDoneDoubleFill) {
isDoubleFillWaiting = true;
fillStopTime = millis();
Serial.println(
"Waiting 5s to see if water settles (double fill check)...");
} else {
hasDoneDoubleFill = false; // Cycle complete
}
}
} else if (isDoubleFillWaiting) {
if (topNoWater) {
Serial.println(
"Water settled and sensor is dry! Starting double fill.");
digitalWrite(PIN_RELAY, HIGH);
isPumping = true;
pumpStartTime = millis();
isDoubleFillWaiting = false;
hasDoneDoubleFill = true;
} else {
if (millis() - fillStopTime >= 5000) {
Serial.println("Water level stable. Fill cycle complete.");
isDoubleFillWaiting = false;
hasDoneDoubleFill = false; // Cycle complete
}
}
}
// 2. Wait for top tank to be empty for the confirmation period
else {
if (topNoWater) {
if (!isCountingDown) {
Serial.println(
"Top tank empty detected. Starting confirmation timer...");
isCountingDown = true;
emptyStartTime = millis();
} else {
unsigned long elapsedTime = millis() - emptyStartTime;
if (elapsedTime >= CONFIRMATION_DELAY_MS) {
Serial.println("Timer finished! Starting pump.");
digitalWrite(PIN_RELAY, HIGH);
isPumping = true;
pumpStartTime = millis();
isCountingDown = false;
}
}
}
// 3. Reset countdown if water sloshes or is detected again before timer
// finishes
else {
if (isCountingDown) {
Serial.println(
"Water detected before timer finished! Resetting timer.");
isCountingDown = false;
}
}
}
}
// --- LED Status Indicator Logic ---
unsigned long currentMillis = millis();
bool ledState = false;
if (bottomNoWater) {
// Constant ON with short OFF flash every 5s = water reservoir empty
int cycle = currentMillis % 5000;
ledState = (cycle > 100);
} else if (isPumping) {
// Quicker steady flash (like a broken turn signal) = filling
// Pattern: 200ms ON, 200ms OFF
int cycle = currentMillis % 400;
ledState = (cycle < 200);
} else if (isCountingDown) {
// Steady flashing (like a turn signal) = pending filling
// Pattern: 500ms ON, 500ms OFF
int cycle = currentMillis % 1000;
ledState = (cycle < 500);
} else {
// Short flash every 5s = good
int cycle = currentMillis % 5000;
ledState = (cycle < 100);
}
digitalWrite(PIN_LED, ledState ? HIGH : LOW);
// Debug output every 5 seconds
static unsigned long lastDebugTime = 0;
if (millis() - lastDebugTime >= 5000) {
lastDebugTime = millis();
Serial.print("Top (Sock): ");
Serial.print(topVoltage);
Serial.print("V | ");
Serial.print("Bottom (Res): ");
Serial.print(bottomVoltage);
Serial.print("V | ");
if (bottomNoWater) {
Serial.println("Status: HALTED (Reservoir Empty)");
} else if (isPumping) {
Serial.println("Status: PUMPING");
} else if (isDoubleFillWaiting) {
unsigned long elapsed = millis() - fillStopTime;
unsigned long remain = (elapsed < 5000) ? (5000 - elapsed) / 1000 : 0;
Serial.print("Status: DOUBLE FILL WAIT (");
Serial.print(remain);
Serial.println("s left)");
} else if (isCountingDown) {
unsigned long elapsed = millis() - emptyStartTime;
unsigned long remain = (elapsed < CONFIRMATION_DELAY_MS)
? (CONFIRMATION_DELAY_MS - elapsed) / 1000
: 0;
Serial.print("Status: WAITING (");
Serial.print(remain);
Serial.println("s left)");
} else {
Serial.println("Status: IDLE");
}
}
delay(100);
}
Pump Relay
Reservoir Sensor
Tank Sensor