The most effective water sensor for Arduino projects is the FC-37 trace module paired with an LM393 comparator, but relying on the digital output alone leaves you blind to slow, seeping leaks. For granular leak detection, reading the raw analog trace is superior. However, applying continuous DC voltage to the exposed nickel pads causes rapid electrolysis, destroying cheap sensors in weeks. By powering the sensor via a GPIO pin and cycling it on for just 10 milliseconds before reading the ADC, you can extend the sensor's operational life from weeks to years.
Project Overview & Difficulty Rating
According to the EPA's Fix a Leak program, a single undetected drip can waste thousands of gallons annually, making early electronic detection a high-ROI DIY project. This guide walks through building a robust analog leak detector, avoiding the common pitfalls that brick sensors and flood basements.
Hardware Spec Sheet & Parts List
Do not substitute the Nano v3 with an ESP32 for this specific 5V analog circuit without adding a voltage divider, as the ESP32's ADC pins are strictly 3.3V tolerant and will be damaged by the 5V reference.
| Component | Exact Variant / Spec | Qty | Notes |
|---|---|---|---|
| Microcontroller | Arduino Nano v3 (ATmega328P) | 1 | Ensure it has the CH340 or FT232RL USB-to-Serial chip for driver compatibility. |
| Water Sensor Pad | FC-37 (65mm x 20mm trace board) | 1 | Nickel-plated copper. Avoid un-plated copper which oxidizes instantly. |
| Comparator Module | LM393 Dual Comparator Breakout | 1 | Includes the 10k potentiometer for digital threshold tuning. |
| Piezo Buzzer | 5V Active Piezo (e.g., TDZ-12B05) | 1 | Active type requires only a DC HIGH signal, no PWM tone generation needed. |
| Wiring | 22 AWG Stranded Jumper Wires | 6 | Use stranded for breadboard flexibility; solid core can loosen in cheap sockets. |
Pin Mapping & Wiring Steps
Follow this exact pin mapping. We are bypassing the LM393's digital output (D0) and reading the raw analog signal (A0) from the sensor pad directly to capture the severity of the water bridge, not just a binary wet/dry state.
| Sensor / Module Pin | Arduino Nano v3 Pin | Wire Color (Suggested) | Function |
|---|---|---|---|
| FC-37 VCC (or +) | D4 (Configured as Output) | Red | Switched power to prevent electrolysis. |
| FC-37 GND (or -) | GND | Black | Common ground reference. |
| FC-37 A0 (Analog Out) | A0 | Yellow | Raw ADC voltage drop across water bridge. |
| Piezo Buzzer (+) | D5 | Orange | Digital HIGH triggers alarm. |
| Piezo Buzzer (-) | GND | Black | Common ground. |
Wiring Pro-Tip: Keep the analog wire (Yellow) away from the buzzer wire (Orange). The piezo buzzer generates high-frequency switching noise when triggered, which can couple into the high-impedance A0 trace and cause false positive ADC spikes. If routing them together is unavoidable, add a 0.1µF ceramic capacitor between A0 and GND at the Nano end.
- Mount the Sensor: Place the FC-37 pad flat in the drip pan or floor area. Angle it slightly (5 degrees) so water pools across the lower traces rather than running off the edge.
- Connect Power Lines: Wire the FC-37 VCC to Nano D4, and GND to Nano GND. Do not connect VCC to the 5V rail.
- Connect Analog Out: Wire the FC-37 analog pin directly to Nano A0. (Ignore the LM393 module for this specific analog build; the FC-37 has a built-in 10k pull-up resistor on most commercial variants, but if your raw pad lacks one, add a 10k external pull-up from A0 to 5V).
- Wire the Alarm: Connect the active buzzer positive to D5 and negative to GND.
- Verify Connections: Use a multimeter in continuity mode to verify GND is common across the Nano, sensor, and buzzer before applying USB power.
Compilable Code with Error Handling
The following code targets the Arduino Nano v3 (ATmega328P). It implements the GPIO power-cycling trick to save the sensor pads and includes bounds-checking to catch disconnected wires.
// Target Board: Arduino Nano v3 (ATmega328P)
// Project: Analog Water Leak Detector with Electrolysis Prevention
#define SENSOR_POWER_PIN 4
#define SENSOR_ANALOG_PIN A0
#define BUZZER_PIN 5
// Thresholds based on 10-bit ADC (0-1023)
// Dry = ~1023, Light Moisture = ~600, Submerged = ~200
#define LEAK_THRESHOLD 650
#define ERROR_THRESHOLD 1020 // If reading is near max, circuit is open
const unsigned long READ_INTERVAL = 2000; // Read every 2 seconds
unsigned long lastReadTime = 0;
void setup() {
Serial.begin(9600);
pinMode(SENSOR_POWER_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
// Ensure sensor is OFF and buzzer is quiet at boot
digitalWrite(SENSOR_POWER_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
Serial.println("System Initialized. Monitoring for leaks...");
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - lastReadTime >= READ_INTERVAL) {
lastReadTime = currentMillis;
// 1. Power ON the sensor to prevent continuous electrolysis
digitalWrite(SENSOR_POWER_PIN, HIGH);
// 2. Wait for ADC and RC circuit to settle (critical for stable reads)
delay(15);
// 3. Take multiple samples to filter out 60Hz mains noise
long total = 0;
for(int i = 0; i < 5; i++) {
total += analogRead(SENSOR_ANALOG_PIN);
delay(2);
}
int sensorValue = total / 5;
// 4. Power OFF the sensor immediately after reading
digitalWrite(SENSOR_POWER_PIN, LOW);
// 5. Error Handling & Logic
if (sensorValue >= ERROR_THRESHOLD) {
Serial.println("ERROR: Analog reading locked at 1023 - Check wiring");
triggerErrorBeep();
}
else if (sensorValue < LEAK_THRESHOLD) {
Serial.print("LEAK DETECTED! ADC Value: ");
Serial.println(sensorValue);
digitalWrite(BUZZER_PIN, HIGH);
}
else {
Serial.print("System Dry. ADC Value: ");
Serial.println(sensorValue);
digitalWrite(BUZZER_PIN, LOW);
}
}
}
void triggerErrorBeep() {
// Double beep to distinguish from continuous leak alarm
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
delay(100);
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
}
Debugging: "Sensor Reads 1023 Constantly"
The most common failure mode when building a water sensor for Arduino is a maxed-out ADC. If your serial monitor outputs the exact string: "ERROR: Analog reading locked at 1023 - Check wiring", the microcontroller is seeing an open circuit on the analog pin.
The First Three Things to Check:
- Floating Analog Pin (Most Likely): The yellow wire between the FC-37 A0 pin and the Nano A0 pin is loose or broken. A floating pin on the ATmega328P will often read near 1023 due to internal parasitic capacitance and the pull-up resistor on the sensor module. Reseat the wire and check continuity.
- VCC and GND Swapped: If you accidentally wired 5V to the sensor's GND pad and GND to the VCC pad, the internal protection diodes on the sensor module may clamp the analog output high. Swap the red and black wires and verify with a multimeter that the sensor VCC pin reads +5V relative to GND when D4 is HIGH.
- Sensor Pad Corrosion / Breakage: If the sensor was previously left powered continuously in a wet environment, electrolysis may have eaten completely through the nickel trace connecting the pad to the header pin. Visually inspect the header solder joints on the FC-37 for green/white crust. If broken, replace the sensor pad.
For deeper ADC noise issues, refer to the official Arduino analogRead documentation, which details how high-impedance sources require longer settling times before the internal sample-and-hold capacitor can charge fully.
Extending and Simplifying the Build
Depending on your deployment environment, you may need to alter the complexity of this circuit.
How to Simplify (Binary Detection Only):
If you do not care about leak severity and only need a simple "wet/dry" trigger, remove the analog wire. Connect the LM393 module's D0 (Digital Out) pin to Arduino D2. Use a small flathead screwdriver to turn the blue potentiometer on the LM393 until the onboard LED toggles exactly when a drop of water bridges the sensor pads. In your code, replace the analogRead() block with a simple digitalRead(2) and attach an interrupt. This eliminates ADC noise and calibration entirely.
How to Extend (IoT and Remote Alerts):
To make this a smart home device, swap the Arduino Nano v3 for an ESP32-DevKitC V4. Because the ESP32 operates at 3.3V logic, you must add a simple voltage divider (e.g., 2.2k and 3.3k resistors) on the analog line to step the 3.3V max down to the ESP32's 2.5V ADC safe zone. Integrate the PubSubClient MQTT library to publish the ADC values to a Home Assistant broker, allowing you to trigger smart water shut-off valves via Zigbee relays when a leak is confirmed.
Frequently Asked Questions
Can I leave the water sensor for Arduino powered on continuously?
No. Leaving 5V DC continuously applied to the exposed traces of an FC-37 sensor in the presence of even slight humidity will cause electrolysis. The positive pad will rapidly oxidize and dissolve into the water, destroying the sensor in a matter of weeks. Always use a GPIO pin to switch power on for only 10-20 milliseconds right before taking an analogRead(), as demonstrated in the code above.
What is the difference between the FC-37 and standard rain sensor modules?
While electrically identical (both use a resistive trace pattern and an LM393 comparator), the physical geometry differs. Rain sensors (like the YL-83) feature widely spaced, thick traces designed to catch large, fast-moving droplets while allowing wind to dry them quickly. The FC-37 features tightly packed, interlocking comb traces designed to detect thin films of standing water or slow capillary seepage on flat surfaces. Use the FC-37 for basement floor leaks; use the YL-83 for weather stations.
How do I calibrate the leak threshold for distilled vs tap water?
The LEAK_THRESHOLD in the code relies on the conductivity of the water bridging the gap. Tap water contains dissolved minerals (ions) that conduct electricity well, yielding a lower ADC value (e.g., 300-500). Distilled or RO (Reverse Osmosis) water is highly resistive and may only drop the ADC to 800-900. To calibrate, upload the code, open the Serial Monitor, and place a single drop of your target water source on the dry sensor. Note the ADC value printed, and set your LEAK_THRESHOLD to roughly 100 points above that baseline reading.






