RisalDash RisalDash

Project · Water quality · Automation

Water quality that acts on the reading

An aquarium, a hydroponic tank or a well share the same question: is this water still OK? Four probes answer it — pH, TDS, turbidity and temperature — but the raw numbers lie without temperature compensation, and a monitor that only shows a problem is half a build. This one scores the water Good / Fair / Poor and kicks the fresh-water pump in automatically when it breaches.

ProjectWater qualitypHTDSESP32
ESP32 water quality project: pH, TDS, turbidity, DS18B20 temperature and auto pump

The probes

ProbeMeasuresInterfaceWatch out
pH probe + boardacidity 0–14analogcalibrate with 4.0 / 6.86 buffers
TDS meterdissolved solids (ppm)analogreads ~2 %/°C off — compensate!
Turbiditycloudiness (NTU)analogkeep the optics clean
DS18B20water temperature1-Wirethe waterproof steel version

The DS18B20 wires over 1-Wire with a 4.7 k pull-up; the three analog probes take ADC pins (on the ESP32 use ADC1, GPIO32–39 — ADC2 dies when Wi-Fi is up):

DS18B20 wired to an ESP32 with a 4.7k pull-up on the data line
DS18B20 on 1-Wire — data needs the 4.7 k pull-up. Built in BoardLab.

The part everyone skips: temperature compensation

Conductivity rises with temperature, so a TDS probe in 30 °C water reads ~10 % higher than the same water at 25 °C. That's the difference between “fine” and a false alarm. One line fixes it — and the dashboard shows raw vs compensated side by side so you can see it working:

// Temperature compensation: a TDS/EC probe drifts ~2 %/°C — normalise to 25 °C.
tds = tdsRaw / (1.0f + 0.02f * (waterT - 25.0f));
ec  = tds * 2.0f;

// Quality score from all three probes (drinking-ish thresholds; tune per use).
int score = 0;
if (tds > 500 || ph < 6.5f || ph > 8.5f || turb > 50) score = 2;        // poor
else if (tds > 300 || ph < 6.8f || ph > 8.0f || turb > 25) score = 1;   // fair
if (score == 2 && quality != 2 && evlog) evlog->print("POOR water - pump + check filter");
quality = score;

// Auto-actuation: poor quality kicks the fresh-water pump in until it recovers.
if (score == 2) pump = true;
else if (score == 0) pump = false;

The dashboard

Presets do the heavy lifting — dash.sensor("tds"|"ph"|"turbidity"|"ds18b20", …) each expand into the right widget, unit and range; a badge gives the verdict and a toggle drives the pump relay:

dash.layout("Water", RICON_WATER);
dash.sensor("tds", &tds, &ec);           // TDS ppm gauge + EC metric (compensated)
dash.sensor("ph", &ph);                  // pH gauge 0..14
dash.sensor("turbidity", &turb);         // NTU gauge
dash.sensor("ds18b20", &waterT);         // water temperature
dash.badge("Quality", &quality).labels("Good", "Fair", "POOR");

dash.layout("Control", RICON_GAUGE);
dash.metric("TDS raw", &tdsRaw, "ppm");  // before compensation — see the difference
dash.led("Alert", &alarmOut);
dash.toggle("Fresh-water pump", &pump);
dash.beginAP("RisalDash-Water", "12345678");

The shipped example (examples/04_Templates/WaterQuality) runs on drifting fake probes, so the Good→Fair→POOR transitions, the log line and the auto-pump all fire with nothing in the water. Calibrate the real probes when they arrive; the sketch doesn't change.

Where this build fits

Aquarium — add a heater relay and you have the shipped AquariumController. Hydroponics — EC is the number your nutrients care about; it's already on the dashboard. Well / cistern — pair with the tank monitor for level + quality on one chip. Alerts can go to MQTT / Home Assistant, and every probe is readable by an AI agent via MCP.

Tune thresholds on fake probes today — calibrate the real ones on delivery day.