/*
* HydroTank Project - Combined Sketch
* Board: STM32 Nucleo-C031C6 (Wokwi simulation)
*
* Sensors:
* pH - potentiometer on A0
* EC - slide potentiometer on A1
* Temp - DS18B20 on D2 (4.7k pull-up to 3.3V)
* Level - HC-SR04 ultrasonic on D6 (TRIG) / D7 (ECHO)
*
* Actuators (all via relay modules):
* relay1 - Cooling - D4
* relay2 - Heater - D5
* relay3 - pH dosing pump - D8
* relay4 - Main pump - D9
*/
#include <OneWire.h>
#include <DallasTemperature.h>
// --- Analog sensors ---
const int phPin = A0;
const int ecPin = A1;
// --- DS18B20 temperature ---
const int tempPin = 2;
OneWire oneWire(tempPin);
DallasTemperature tempSensor(&oneWire);
// --- Actuators ---
const int coolingPin = 4; // relay1
const int heaterPin = 5; // relay2
const int dosingPumpPin = 8; // relay3
const int mainPumpPin = 9; // relay4
// --- Ultrasonic level sensor ---
const int trigPin = 6;
const int echoPin = 7;
// --- Non-blocking dosing pump state ---
bool dosingActive = false;
unsigned long dosingStartTime = 0;
unsigned long lastDoseTime = 0;
const unsigned long doseDurationMs = 1500; // how long pump runs per dose
const unsigned long doseCooldownMs = 10000; // wait between doses
// --- Water level thresholds (tune to your tank once calibrated) ---
const float lowLevelDistanceCM = 15.0; // closer than this = tank full-ish
const float highLevelDistanceCM = 25.0; // farther than this = tank getting low
void setup() {
Serial.begin(115200);
delay(1000);
analogReadResolution(12);
tempSensor.begin();
pinMode(coolingPin, OUTPUT);
pinMode(heaterPin, OUTPUT);
pinMode(dosingPumpPin, OUTPUT);
pinMode(mainPumpPin, OUTPUT);
digitalWrite(coolingPin, LOW);
digitalWrite(heaterPin, LOW);
digitalWrite(dosingPumpPin, LOW);
digitalWrite(mainPumpPin, LOW);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
Serial.println("HydroTank - Full System Test");
}
float readDistanceCM() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000);
if (duration == 0) return -1;
return duration * 0.0343 / 2.0;
}
// Non-blocking dosing check: call every loop with the current pH
void checkPHDosing(float currentPH) {
unsigned long now = millis();
if (dosingActive) {
if (now - dosingStartTime >= doseDurationMs) {
digitalWrite(dosingPumpPin, LOW);
dosingActive = false;
Serial.println("Dosing pump OFF");
}
return;
}
if (currentPH < 5.5 && (now - lastDoseTime > doseCooldownMs)) {
Serial.println("pH low -> dosing pump ON");
digitalWrite(dosingPumpPin, HIGH);
dosingActive = true;
dosingStartTime = now;
lastDoseTime = now;
}
}
void loop() {
// --- pH ---
int phRaw = analogRead(phPin);
float simulatedPH = map(phRaw, 0, 4095, 0, 1400) / 100.0;
// --- EC ---
int ecRaw = analogRead(ecPin);
float simulatedEC = map(ecRaw, 0, 4095, 0, 2000) / 100.0;
// --- Temperature ---
tempSensor.requestTemperatures();
float waterTemp = tempSensor.getTempCByIndex(0);
// --- Water level ---
float distance = readDistanceCM();
// --- Print all readings ---
Serial.print("pH: ");
Serial.print(simulatedPH, 2);
Serial.print(" | EC: ");
Serial.print(simulatedEC, 2);
Serial.print(" | Temp: ");
Serial.print(waterTemp, 2);
Serial.print(" C | Level dist: ");
if (distance < 0) {
Serial.print("no echo");
} else {
Serial.print(distance, 1);
Serial.print(" cm");
}
Serial.println();
// --- Temperature control logic ---
if (waterTemp > 25.0) {
Serial.println("ALERT: Water too hot -> cooling ON");
digitalWrite(coolingPin, HIGH);
digitalWrite(heaterPin, LOW);
} else if (waterTemp < 18.0) {
Serial.println("ALERT: Water too cold -> heater ON");
digitalWrite(heaterPin, HIGH);
digitalWrite(coolingPin, LOW);
} else {
digitalWrite(coolingPin, LOW);
digitalWrite(heaterPin, LOW);
}
// --- pH dosing pump logic (non-blocking) ---
checkPHDosing(simulatedPH);
// --- Main pump logic (placeholder: runs when level distance is high, i.e. tank running low) ---
// Adjust thresholds once you calibrate against your real tank's empty/full distances.
if (distance > highLevelDistanceCM) {
digitalWrite(mainPumpPin, HIGH);
} else if (distance < lowLevelDistanceCM) {
digitalWrite(mainPumpPin, LOW);
}
delay(1000);
}