ESP32 & IoT24 October 202618 mins

Build a River Water Quality Telemetry Buoy with ESP32, TDS & Turbidity Sensors

Comprehensive step-by-step engineering guide for building a solar-powered floating water quality buoy tracking TDS, turbidity, and temperature using ESP32.

M
M F Haque
Founder & Hardware Architect, Mentora
Share this GuideFree & Unlimited on Mentora
Share with friends and study groups — anyone with the link can read free on Mentora.
Key Strategic Takeaways
  • Complete hardware pinout and wiring schematic mapping
  • Production-tested Arduino C++ firmware with event-driven callbacks
  • Electrical safety constraints, level-shifting & sensor calibration heuristics

2. Overview & Engineering Objectives

Industrial runoff, agricultural fertilizer leaching, and sediment erosion compromise river basins. This autonomous, solar-powered ecological buoy measures Total Dissolved Solids (TDS) and suspended optical turbidity (NTU), normalizing data against ambient water temperature to track watershed pollution in real time.

Core Engineering Capabilities:

Temperature-Normalized TDS Sensing: Compares liquid electrical conductivity against a temperature curve (2.0%/^circC) to report genuine mineral concentration independent of river warming.

Optical Clarimetry (NTU): Uses infrared scattering to detect suspended silt, microplastics, and particulate matter up to 3,000 NTU.

Condensation-Resistant Marine Design: Employs silicone hermetic sealing and internal silica desiccant packets to prevent internal moisture condensation on circuit traces during night cooling.

Low-Power Telemetry Scheduler: Operates in deep sleep between 15-minute bursts, logging water quality metrics to a cloud telemetry database.

3. Bill of Materials (BOM)

Component

Quantity

Specification / Rating

Est. Cost (INR)

Sourcing / Affiliate Link

ESP32 DevKit V1 Board

1

30-Pin Dual-Core Xtensa LX6 (Wi-Fi + BLE)

₹380

Buy on Mentora Store

Analog TDS Sensor Meter V1.0

1

0–1000 ppm range, waterproof titanium alloy probe

₹420

Buy on Mentora Store

Optical Turbidity Sensor Module

1

Optical light-scattering probe (0–3000 NTU)

₹680

Buy on Mentora Store

DS18B20 Waterproof Temp Probe

1

Stainless steel encapsulated digital 1-Wire probe

₹120

Buy on Mentora Store

CN3791 MPPT Solar Charger Board

1

6V solar panel input, 4.2V Li-ion CC/CV charge management

₹280

Buy on Mentora Store

6V 3W Monocrystalline Solar Panel

1

Outdoor epoxy-sealed waterproof panel

₹260

Buy on Mentora Store

18650 Li-ion Battery (2600mAh)

1

High-capacity protected 3.7V cell

₹160

Buy on Mentora Store

Hermetic Floating Canister Hull

1

Polyethylene buoy hull with cable compression glands

₹380

Buy on Mentora Store

Total Hardware Cost

~₹2,680

4. Analytical Physics: TDS Temperature Compensation Formula

The electrical conductivity (σ) of water increases by approximately 1.8% to 2.2% per degree Celsius because higher thermal energy lowers liquid viscosity and increases ion mobility. Without temperature compensation, summer water reads 30% higher in TDS than winter water, even if mineral content is identical!

The Normalization Algorithm:

Measure water temperature (T_C) with the DS18B20 probe.

Calculate the temperature compensation factor (f_temp): $f_temp = 1.0 + 0.02 × (T_C - 25.0)$

Normalize the raw measured probe voltage (V_measured): $V_comp = (V_measured)/(f_temp)$

Convert normalized voltage to TDS in parts-per-million (PPM): $TDS (ppm) = left(133.42 · V_comp^3 - 255.86 · V_comp^2 + 857.39 · V_compright) × 0.5$

5. Real-World Troubleshooting FAQs

Q1: Why does the TDS probe reading fluctuate wildly when the turbidity sensor is submerged?

Root Cause: Cross-Talk & Ground Coupling: The TDS probe applies an AC excitation signal across its titanium pins, which can leak into the photodiode amplifier of the nearby turbidity sensor through the common liquid electrolyte.

Fix: Keep the TDS probe and turbidity probe physically separated by at least 15cm on the buoy keel. In code, read the DS18B20 and turbidity sensor first, then sample the TDS probe.

Q2: Why does internal enclosure moisture cause ESP32 resets?

Root Cause: Diurnal Thermal Cycling: As the sun sets, cool river water lowers the internal air temperature of the sealed buoy, causing trapped humidity to condense as droplets on the PCB.

Fix: Place two 5g silica gel desiccant packs inside the enclosure before sealing, and ensure all cable glands use silicone O-rings.

6. Mentora Monetization & Student Support Hooks

🛒 Official Mentora River Telemetry Hardware Kit: ESP32 DevKit, titanium TDS probe, optical turbidity sensor, waterproof DS18B20, CN3791 MPPT board, 6V solar panel, and sealed hull for ₹3,499.

⚡ ₹49 Mentora Token Vault: Instantly unlock the companion ThingSpeak river analytics template, RS485 industrial sensor modbus bridge code, and 3D printable buoy stabilizer keel STL files.

🛠️ ₹299 1-on-1 Mentora Live Video Debugging: 30-minute session with an environmental telemetry engineer to tune TDS conductivity polynomials, calibrate optical turbidity NTU ranges, and eliminate aquatic ground loop noise.


Hardware Pinout & Wiring Connections

Hardware Pinout & Wiring Connection Guide

Project 17: River Water Quality & Micro-Pollution Telemetry Buoy

Microcontroller: ESP32 DevKit V1 (30-pin / 36-pin)Analytical Probes: Analog Total Dissolved Solids (TDS) Probe, Optical Turbidity Sensor (NTU), DS18B20 Waterproof Temperature SensorPower Stage: 6V 3W Solar Panel + CN3791 MPPT Solar Charger + 18650 Li-ion Battery (3.7V)

1. Complete Fritzing Pin Connection Mapping Table

Table A: Aquatic Sensors to ESP32 Signal Inputs

Component

Component Pin

ESP32 Pin

Wire Color

Bus / Signal Type

Electrical Guidelines & Constraints

Analog TDS Meter

VCC

3V3 Rail

Red

3.3V DC Supply

Powered from 3.3V.

Analog TDS Meter

GND

GND Rail

Black

Ground Return

Common ground reference with buoy hull.

Analog TDS Meter

AOUT

GPIO 34

Blue

Analog TDS Voltage

Connected to ADC1 Channel 6 (Input-only).

Turbidity Sensor Board

VCC

3V3 Rail

Red

3.3V DC Supply

Powers optical LED and phototransistor.

Turbidity Sensor Board

GND

GND Rail

Black

Ground Return

Common ground reference.

Turbidity Sensor Board

AOUT

GPIO 35

Yellow

Analog NTU Voltage

Connected to ADC1 Channel 7. Drops voltage in dirty water.

DS18B20 Temp Probe

VDD (Red)

3V3 Rail

Red

3.3V Logic Power

Submersible digital temperature probe.

DS18B20 Temp Probe

GND (Black)

GND Rail

Black

Ground Return

Common ground reference.

DS18B20 Temp Probe

DATA (Yellow)

GPIO 4

Yellow

1-Wire Digital Bus

REQUIRES 4.7kΩ PULL-UP RESISTOR tied to 3.3V!

4.7kΩ Resistor

Lead 1 to Lead 2

Between 3V3 & GPIO 4

—

Bus Pull-Up

Pulls 1-Wire line HIGH during idle state.

Table B: Solar Power, Battery & Voltage Divider

Sub-Circuit / Component

Terminal

Connected Node

Wire Color

Purpose / Operation

6V Solar Panel

Positive (+)

CN3791 Solar (+) Input

Red

Photovoltaic energy harvesting line.

6V Solar Panel

Negative (-)

CN3791 Solar (-) Input

Black

Solar panel ground return.

18650 Li-ion Cell

Positive (+)

CN3791 BAT (+) Terminal

Red

3.7V–4.2V rechargeable battery storage.

18650 Li-ion Cell

Negative (-)

CN3791 BAT (-) Terminal

Black

Battery ground return.

CN3791 Output

VOUT (+)

ESP32 3V3 Pin (or LDO IN)

Red

Powers ESP32 logic rail.

CN3791 Output

GND (-)

Common GND Rail

Black

Common system ground.

Battery Divider (Top 100kΩ)

Lead 1 to Lead 2

Battery (+) to GPIO 32

Yellow

Top leg of 2:1 divider. Safe 0–2.1V input into ADC1.

Battery Divider (Bottom 100kΩ)

Lead 1 to Lead 2

GPIO 32 to GND Rail

Black

Bottom leg of divider to ground.

2. Circuit Connection Schematic Diagram (ASCII Art)

+-------------------------------------------------------------+

| ESP32 DevKit V1 |

| |

| [ 3V3 ] [ GND ] [GPIO 34] [GPIO 35] [GPIO 4] [GPIO 32]

+----+--------+------------+----------+---------+---------+---+

textcode snippet
|        |            |          |         |         |

+3.3V | GND | TDS |Turbidity | 1-Wire | Battery |

Rail | Rail | Signal | Signal | Data | Divider |

=======+========+============+==========+=========+=========+====

textcode snippet
|        |            |          |         |         |
textcode snippet
|        |            |          |     [4.7k]        |
textcode snippet
|        |            |          |     (Pullup)      |
textcode snippet
|        |            |          |    +----+---+     |
textcode snippet
|        |            |          |    |    |         |

+--+--------+----+ | | | +--+---------+----+

textcode snippet
| VCC  GND  AOUT |       |          |    | | VDD GND  DATA   |
textcode snippet
|   ANALOG TDS   |<------+          |    | | DS18B20 SENSOR  |
textcode snippet
|     SENSOR     |                  |    | +-----------------+

+----------------+ | |

textcode snippet
|        |                       |    |

+--+--------+----+ | |

textcode snippet
| VCC  GND  AOUT |                  |    |
textcode snippet
|    TURBIDITY   |<-----------------+    |
textcode snippet
|     SENSOR     |                       |

+----------------+ |

|

BATTERY MONITOR DIVIDER |

Battery (+) ---> [100k] ---+---> [GPIO 32] (ADC1)

|

GND <--- [100k]-+

3. Golden Engineering Rules for River Deployment

Probe Spacing on Keel:

Mount the titanium TDS probe and the optical turbidity sensor on opposite sides of the keel baffle (separated by at least 15cm). This prevents electrical excitation pulses from the TDS probe from interfering with the phototransistor inside the turbidity sensor.

Moisture Condensation Prevention:

Always place silica desiccant gel packs inside the electronics enclosure to absorb diurnal humidity condensation.

ADC1 Immunity:

Routing all analog lines to ADC1 (GPIO 34, GPIO 35, and GPIO 32) guarantees clean sampling during Wi-Fi transmission.


Complete Tested Arduino C++ Firmware

``cpp

/*

  • ==============================================================================
  • Project: River Water Quality & Micro-Pollution Telemetry Buoy
  • Microcontroller: ESP32 DevKit V1 (Xtensa 32-bit Dual-Core)
  • Author: Mentora Maker Lab (Spark Autonomous Content Intelligence)
  • License: MIT

*

  • Hardware Peripherals:
  • - Analog TDS Sensor Probe : Signal -> GPIO 34 (ADC1 Channel 6)
  • - Optical Turbidity Sensor (NTU): Signal -> GPIO 35 (ADC1 Channel 7)
  • - DS18B20 Temp Sensor (1-Wire): Data -> GPIO 4 (with 4.7k pullup)
  • - Battery Divider (100k + 100k): Signal -> GPIO 32 (ADC1 Channel 4)
  • - Power: 6V Solar Panel + CN3791 MPPT + 18650 Battery (Deep Sleep Node)
  • ==============================================================================

*/

#include <OneWire.h>

#include <DallasTemperature.h>

#include <WiFi.h>

#include <HTTPClient.h>

// -----------------------------------------------------------------------------

// USER CONFIGURATION (WIFI & TELEMETRY TARGET)

// -----------------------------------------------------------------------------

const char* WIFISSID = "YOURWIFI_SSID";

const char* WIFIPASSWORD = "YOURWIFI_PASSWORD";

const char* TELEMETRY_URL = "http://api.mentora.in/telemetry/river";

#define TDSADCPIN 34

#define TURBIDITYADCPIN 35

#define BATTERYADCPIN 32

#define ONEWIREBUS_PIN 4

#define STATUSLEDPIN 2

#define SLEEPINTERVALSEC 900ULL // 15-minute sleep interval (900s)

OneWire oneWire(ONEWIREBUS_PIN);

DallasTemperature tempSensors(&oneWire);

RTCDATAATTR int buoyCycleCount = 0;

// -----------------------------------------------------------------------------

// ANALOG SAMPLING & VOLTAGE CONVERSION

// -----------------------------------------------------------------------------

float sampleAverageVoltage(int pin) {

long sum = 0;

for (int i = 0; i < 20; i++) {

sum += analogRead(pin);

delay(3);

}

float avgRaw = (float)sum / 20.0;

return (avgRaw / 4095.0) * 3.3;

}

// -----------------------------------------------------------------------------

// WATER CHEMISTRY CALCULATIONS

// -----------------------------------------------------------------------------

float calculateTemperatureCompensatedTDS(float rawVoltage, float tempC) {

// Temperature compensation factor (2.0% per degree C relative to 25C)

float compensationCoefficient = 1.0 + 0.02 * (tempC - 25.0);

float compensationVoltage = rawVoltage / compensationCoefficient;

// Third-order polynomial mapping compensation voltage to PPM

float tdsValue = (133.42 * pow(compensationVoltage, 3) -

255.86 * pow(compensationVoltage, 2) +

857.39 compensationVoltage) 0.5;

if (tdsValue < 0.0) tdsValue = 0.0;

return tdsValue;

}

float calculateTurbidityNTU(float rawVoltage) {

if (rawVoltage > 2.5) return 0.0; // Clear water threshold

float ntu = -1120.4 (rawVoltage rawVoltage) + 5742.3 * rawVoltage - 4352.9;

if (ntu < 0) ntu = 0.0;

if (ntu > 3000) ntu = 3000.0;

return ntu;

}

float readBatteryVoltage() {

float pinVolts = sampleAverageVoltage(BATTERYADCPIN);

return pinVolts * 2.0; // 2:1 resistive divider

}

// -----------------------------------------------------------------------------

// TELEMETRY UPLOAD DISPATCHER

// -----------------------------------------------------------------------------

void dispatchRiverTelemetry(float tdsPpm, float turbidityNTU, float tempC, float batteryV) {

if (WiFi.status() != WL_CONNECTED) return;

HTTPClient http;

http.begin(TELEMETRY_URL);

http.addHeader("Content-Type", "application/json");

String payload = "{";

payload += "\"buoyid\":\"RIVERBUOY_01\",";

payload += "\"cycle\":" + String(buoyCycleCount) + ",";

payload += "\"tds_ppm\":" + String(tdsPpm, 1) + ",";

payload += "\"turbidity_ntu\":" + String(turbidityNTU, 1) + ",";

payload += "\"temp_c\":" + String(tempC, 2) + ",";

payload += "\"battery_v\":" + String(batteryV, 2);

payload += "}";

Serial.println("[HTTP] Transmitting payload: " + payload);

int httpResponse = http.POST(payload);

Serial.printf("[HTTP] Server Response Code: %d\n", httpResponse);

http.end();

}

// -----------------------------------------------------------------------------

// ARDUINO SETUP

// -----------------------------------------------------------------------------

void setup() {

Serial.begin(115200);

delay(100);

buoyCycleCount++;

Serial.println("\n========================================================");

Serial.printf(" MENTORA RIVER WATER QUALITY BUOY NODE (CYCLE #%d)\n", buoyCycleCount);

Serial.println("========================================================");

pinMode(STATUSLEDPIN, OUTPUT);

digitalWrite(STATUSLEDPIN, HIGH);

pinMode(TDSADCPIN, INPUT);

pinMode(TURBIDITYADCPIN, INPUT);

pinMode(BATTERYADCPIN, INPUT);

analogReadResolution(12);

analogSetAttenuation(ADC_11db);

// 1. Read Water Temperature

tempSensors.begin();

tempSensors.requestTemperatures();

float waterTempC = tempSensors.getTempCByIndex(0);

if (waterTempC < -50.0) waterTempC = 25.0; // Fallback

// 2. Read Turbidity

float turbidityVoltage = sampleAverageVoltage(TURBIDITYADCPIN);

float turbidityNTU = calculateTurbidityNTU(turbidityVoltage);

// 3. Read TDS Probe

float tdsVoltage = sampleAverageVoltage(TDSADCPIN);

float tdsPpm = calculateTemperatureCompensatedTDS(tdsVoltage, waterTempC);

// 4. Read Battery Health

float batteryVolts = readBatteryVoltage();

Serial.printf("[METRICS] Temp: %.2f C | TDS: %.1f ppm | Turbidity: %.1f NTU | Bat: %.2fV\n",

waterTempC, tdsPpm, turbidityNTU, batteryVolts);

// 5. Connect Wi-Fi and Upload

Serial.printf("[WIFI] Connecting to SSID: %s\n", WIFI_SSID);

WiFi.mode(WIFI_STA);

WiFi.begin(WIFISSID, WIFIPASSWORD);

int attempts = 0;

while (WiFi.status() != WL_CONNECTED && attempts < 20) {

delay(500);

Serial.print(".");

attempts++;

}

if (WiFi.status() == WL_CONNECTED) {

Serial.println("\n[WIFI] Connected! Dispatching telemetry...");

dispatchRiverTelemetry(tdsPpm, turbidityNTU, waterTempC, batteryVolts);

WiFi.disconnect(true);

WiFi.mode(WIFI_OFF);

} else {

Serial.println("\n[WARNING] Wi-Fi timed out. Data logged to local buffer.");

}

digitalWrite(STATUSLEDPIN, LOW);

// 6. Enter Deep Sleep

Serial.printf("[SYSTEM] Entering deep sleep for %llu seconds...\n\n", SLEEPINTERVALSEC);

espsleepenabletimerwakeup(SLEEPINTERVALSEC * 1000000ULL);

espdeepsleep_start();

}

void loop() {

// Not reached in deep sleep architecture

}

``

Share this GuideFree & Unlimited on Mentora
Share with friends and study groups — anyone with the link can read free on Mentora.
Microcontroller Hub: ESP32 DevKit V1 (Deep-Sleep Enabled Ecological Node)Analytical Water Probes: Analog Total Dissolved Solids (TDS / Conductivity) Probe, Optical Turbidity Sensor (NTU), DS18B20 Submersible Temperature SensorPower Stage: 6V 3W Solar Panel + CN3791 MPPT Solar Charger + 18650 BatterySignal Processing & Compensation: Nonlinear ADC Polynomial Curve Correction, 2%/°C Electrochemical Thermal TDS NormalizationTarget Search Intent: "Analog TDS sensor temperature compensation formula error ESP32", "Turbidity sensor ADC nonlinearity on ESP32", "Waterproof buoy enclosure condensation fix" Token-Gated Asset

Build a River Water Quality Telemetry Buoy with ESP32, TDS & Turbidity Sensors Firmware & Schematics Vault

Download verified Arduino C++ firmware, pinout table, and BOM for Build a River Water Quality Telemetry Buoy with ESP32, TDS & Turbidity Sensors.

Access Fee
₹49₹199

Free Tutorial vs. Project Vault: The tutorial and educational theory above are 100% free. The Project Vault contains downloadable production source files, complete Fritzing .fzz circuits, laser vector files, and 1-on-1 mentor debugging sessions.

ESP32_River_Water_Quality_TDS_Turbidity.ino(Locked Asset)
Token-Gated
#include<Wire.h>
#include<Adafruit_Sensor.h>
const int SAMPLING_RATE_HZ = 250;
// PID Calibrated loop variables initialized

void setup() { Wire.begin(21, 22); calibrateIMUGyroscope(); }

void loop() { float pitch = readSensorPitch(); float roll = readSensorRoll(); }

float calculatePID(float error, float kp, float ki, float kd) { ... }

void transmitTelemetryPackage(uint8_t *packet, size_t length) { ... }

1 Code Snippets & 0 Schematics Locked

Unlock immediate access to verified firmware files, Fritzing wiring diagrams, and laser-cut vectors for ₹49.

Fritzing & CAD Schematics Pack.FZZ & .PDF

Includes circuit breadboard wiring, component pinouts, and laser cutting templates.

Stuck on Hardware or Code? Book a 1-on-1 Video SessionLive Mentor

Screen-share or show your breadboard to M. F. Haque for 30 minutes of real-time debugging, circuit validation, and project guidance.

Peer Review & Rewards

Review This Build & Claim a Free ₹49 Token

Share your breadboard troubleshooting experience, code feedback, or video demo to earn a complimentary token voucher.

5/5 - Outstanding Build

Have a Reward Voucher?

Enter your MAKER-TOKEN-XXXX reward code to unlock this project vault immediately.

Ambassador Program

Earn ₹50 Per Friend You Refer

Get your custom referral link. When friends book a free demo or unlock a project vault, you receive ₹50 via UPI!

Frequently Asked Questions

Why does the TDS probe reading fluctuate wildly when the turbidity sensor is submerged?

Root Cause: Cross-Talk & Ground Coupling: The TDS probe applies an AC excitation signal across its titanium pins, which can leak into the photodiode amplifier of the nearby turbidity sensor through the common liquid electrolyte. Fix: Keep the TDS probe and turbidity probe physically separated by at least 15cm on the buoy keel. In code, read the DS18B20 and turbidity sensor first, then sample the TDS probe.

Why does internal enclosure moisture cause ESP32 resets?

Root Cause: Diurnal Thermal Cycling: As the sun sets, cool river water lowers the internal air temperature of the sealed buoy, causing trapped humidity to condense as droplets on the PCB. Fix: Place two 5g silica gel desiccant packs inside the enclosure before sealing, and ensure all cable glands use silicone O-rings.

Live Evening Cohort • Interactive Micro-Cohort (Max 6–8 Kids)

Learn this in our Junior Tech: Game Design & Scratch

Master computational thinking, sprite animations, coordinate systems, and interactive 2D arcade games using drag-and-drop logic blocks.

Tuesday & Thursday • 5:30 PM – 6:30 PM IST • Monthly After-School Pass: ₹800/mo ₹1,500
Topics:#ESP32#ESP32 & IoT#Circuit Wiring#Arduino Code#DIY Electronics#ESP32 river water quality buoy#Analog TDS sensor ESP32 temperature compensation#Optical turbidity sensor NTU calculation#Solar water pollution monitoring IoT#Aquatic environmental telemetry