Build a Flood Water Level Warning Radar with ESP32 & VL53L1X ToF Laser
Comprehensive engineering tutorial for building an autonomous flood warning station using ESP32, VL53L1X 4m laser ToF ranging, and waterproof ultrasonic redundancy.
- 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
Flash floods in urban culverts, river crossings, and storm drainage canals escalate within minutes. Mechanical float switches fail due to floating debris, rags, and silt fouling. This project builds a non-contact hydrological radar terminal that measures water surface displacement using an overhead 940nm laser Time-of-Flight sensor cross-validated by a waterproof ultrasonic transducer.
Core Engineering Capabilities:
Sub-Centimeter Laser Profiling: Measures liquid surface clearance up to 4.0 meters with millimetric resolution using photon time-of-flight calculations.
Specular Reflection & Ripple Compensation: Solves the physical challenge where choppy water scatters laser pulses by pairing the laser with a JSN-SR04T acoustic transceiver in a 2-out-of-2 consensus filter.
Optical Cover-Glass Crosstalk Cancellation: Calibrates out internal optical reflections caused by the protective quartz/acrylic enclosure window.
Autonomous Emergency Siren: Triggers an emergency relay and MQTT cloud dispatch when the water level rises into the "Red Danger Zone" (< 50cm bridge clearance).
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
VL53L1X ToF Sensor Module
1
940nm VCSEL laser ranging up to 4000mm (I2C)
₹520
Buy on Mentora Store
JSN-SR04T Ultrasonic Sensor
1
Waterproof sealed acoustic transducer (20–600cm)
₹480
Buy on Mentora Store
5V Optocoupled Relay Board
1
10A 250VAC / 30VDC rated, Active-LOW trigger
₹95
Buy on Mentora Store
12V High-Decibel Warning Siren
1
110dB outdoor industrial piezoelectric horn
₹340
Buy on Mentora Store
12V to 5V 3A DC Buck Converter
1
Step-down power regulator for ESP32 and logic
₹120
Buy on Mentora Store
Optical Quartz Window (20mm)
1
High-transmittance optical protective disc
₹140
Buy on Mentora Store
IP67 Waterproof Outdoor Enclosure
1
Weatherproof junction box with downward sensor horn
₹320
Buy on Mentora Store
Total Hardware Cost
~₹2,395
4. Optical Physics: Specular Water Reflection & Cover Glass Crosstalk
1. Specular Reflection vs Diffuse Scattering
Most solid targets (walls, soil) scatter light diffusely (Lambertian reflectance). Water behaves as a specular mirror:
When calm and mounted perfectly perpendicular, water reflects almost 100% of the beam back to the sensor.
When wind or turbulence generates ripples, the angled surface reflects the laser beam away from the receiving photodiode array (SPAD), causing the sensor to report RangeStatus = 4 (Phase Fail).
The Engineering Fix: Firmware detects RangeStatus == 4 and seamlessly falls back to the JSN-SR04T ultrasonic acoustic transducer, which reflects reliably off turbulent water ripples.
2. Optical Cover Glass Crosstalk (XTalk)
When placing a protective glass window in front of the VL53L1X, a fraction of the emitted 940nm laser light reflects internally inside the glass pane directly into the receiver array before ever reaching the target.
The Symptom: Sensor reads a constant false target distance of 0mm to 30mm.
The Solution: In setup(), run a baseline calibration with a clean target at 100mm and call:
sensor.setDistanceMode(VL53L1X::Long); // 4-meter range mode
sensor.setTimingBudgetInMs(50); // 50ms integration window
5. Dual-Sensor Redundancy & Alarm Decision Tree
1. Measure Laser Distance: D_laser (mm)
2. Measure Ultrasonic Distance: D_sonic (mm)
3. CONSENSUS LOGIC:
- If Dlaser is VALID and Dsonic is VALID:
-> Delta = abs(Dlaser - Dsonic)
-> If Delta < 80mm: WaterLevel = (Dlaser + D_sonic) / 2
-> Else: Prefer D_sonic (choppy water scatter artifact on laser)
- Else if only Dsonic is VALID: WaterLevel = D_sonic
- Else if only Dlaser is VALID: WaterLevel = D_laser
- Else: FAULT_ALARM (Sensor Obstructed)
4. THRESHOLD EVALUATION:
- Clearance > 1500mm: GREEN (Normal Flow)
- 800mm < Clearance <= 1500mm: YELLOW (Pre-Warning Alert)
- Clearance <= 500mm: 🚨 RED EMERGENCY -> Energize Siren Relay (GPIO 26)!
6. Real-World Troubleshooting FAQs
Q1: Why does the JSN-SR04T report erratic zero readings below 25cm?
Root Cause: Acoustic Blind Zone: The piezoelectric transducer needs ~1.2ms for mechanical ringing to damp out after transmission before switching to receive mode.
Fix: Mount the sensor module at least 30cm above the maximum possible flood overflow crest.
Q2: Why does the VL53L1X lock up on the I2C bus?
Root Cause: Connecting to an unshielded cable longer than 1 meter. I2C is an on-board protocol. For cable runs >30cm, place 2.2kΩ pull-up resistors on SDA (GPIO 21) and SCL (GPIO 22).
7. Mentora Monetization & Student Support Hooks
🛒 Official Mentora Flood Radar Kit: ESP32 DevKit, VL53L1X ToF module, JSN-SR04T waterproof sensor, 5V relay, 12V siren, and IP67 enclosure for ₹2,999.
⚡ ₹49 Mentora Token Vault: Instantly unlock the dual-sensor Kalman voting filter code, Telegram flash flood alert webhook script, and Grafana water level dashboard templates.
🛠️ ₹299 1-on-1 Mentora Live Video Debugging: 30-minute session to tune optical cover glass XTalk registers, calibrate acoustic temperature velocity offsets, and eliminate I2C bus reflections.
Hardware Pinout & Wiring Connections
Hardware Pinout & Wiring Connection Guide
Project 24: Flood Water Level Warning Radar with VL53L1X & Ultrasonic Redundancy
Microcontroller: ESP32 DevKit V1 (30-pin / 36-pin)Distance Sensors: STMicroelectronics VL53L1X Long-Range Laser ToF (I2C: 0x29), JSN-SR04T Waterproof Ultrasonic TransceiverActuator Stage: 5V Optocoupled Relay Module (12V 110dB Outdoor Storm Siren on GPIO 26)Power Stage: 12V 2A DC External Supply (Siren Power) + LM2596 Buck Converter (5V Logic & Sensor Rail)
1. Complete Fritzing Pin Connection Mapping Table
Table A: Dual Ranging Sensor Inputs & Relay Actuator to ESP32
Component
Component Pin
ESP32 DevKit Pin
Wire Color
Signal / Bus Type
Electrical Guidelines & Constraints
VL53L1X ToF
VIN / VCC
3V3 Rail
Red
3.3V DC Sensor Supply
Powered strictly from 3.3V.
VL53L1X ToF
GND
GND Rail
Black
Ground Return
Common system ground.
VL53L1X ToF
SDA
GPIO 21
Blue
I2C Hardware Data
Primary I2C SDA line.
VL53L1X ToF
SCL
GPIO 22
Yellow
I2C Hardware Clock
Primary I2C SCL line (400kHz fast mode).
VL53L1X ToF
XSHUT
3V3 Rail (or NC)
Orange
Hardware Enable
Tie HIGH for continuous operation.
JSN-SR04T Board
5V / VCC
5V Rail
Red (Thick)
5V Ultrasonic Supply
MUST CONNECT TO 5V. Ultrasonic transducer requires 5V pulses.
JSN-SR04T Board
GND
GND Rail
Black
Ground Return
Common system ground.
JSN-SR04T Board
TRIG
GPIO 5
Orange
Ultrasonic Trigger Pulse
10µs digital trigger output from ESP32.
JSN-SR04T Board
ECHO
GPIO 18
Yellow
Echo Return Pulse
5V echo pulse into ESP32 GPIO 18 (Input 5V tolerant for short pulses).
5V Relay Module
VCC
5V Rail
Red
Relay Coil Supply
Power from 5V rail.
5V Relay Module
GND
GND Rail
Black
System Ground
Common ground reference.
5V Relay Module
IN / SIG
GPIO 26
Yellow
Siren Trigger Signal
Active-LOW trigger. Driven LOW to sound 110dB siren.
Relay Terminals
COM
+12V Supply Rail
Red (Thick)
High-Current Supply
Connects to 12V positive rail.
Relay Terminals
NO
12V Siren Lead (+)
Red (Thick)
Switched Output
Feeds siren positive terminal when alarm fires.
2. Circuit Connection Schematic Diagram (ASCII Art)
+-------------------------------------------------------------+
| ESP32 DevKit V1 |
| |
| [ 5V ] [ 3V3 ] [ GND ] [G21] [G22] [G5] [G18] [G26] |
+----+-------+--------+---------+------+-------+-----+----+---+---+
| | | | | | | |+5V | +3.3V | GND | SDA | SCL | TRIG | ECHO| |
Rail | Rail | Rail | Line | Line | Pulse |Pulse| |
=======+=======+========+=========+======+=======+=====+====+=======
| | | | | | | || | | +-----+ | | | || | | | +--------+ | | || | | | | | | |+--+-------+--------+---+---+----+ | | |
| 5V 3V3 GND SDA SCL | | | || VL53L1X LASER ToF | | | |+--------------------------------+ | | |
| | | | |+--+----------------+------------------------+-----+----+
| 5V GND TRIG ECHO |
| JSN-SR04T WATERPROOF ULTRASONIC BOARD |
+-------------------------------------------------------+
| |
+----------------+-----------------------------------+
| | |v v v
+------------------------+ [IN]
| 5V RELAY MODULE |<---------------------------+| [ COM ] [ NO ] |
+------+---------+-------+
| |
+12V -+ +-> [12V Warning Siren (+)]
3. Golden Engineering Rules for Flood Radar Mounting
Acoustic Blind Zone Clearance:
Always mount the JSN-SR04T horn at least 30cm above the maximum flood crest level. Echoes returning within 20cm overlap the transmitted pulse and register as zero.
Quartz Window for Laser ToF:
When sealing the VL53L1X behind an optical window, use high-transmittance quartz or optical borosilicate glass and mount the sensor flush (<1.5mm) against the glass to prevent internal scatter.
Common Ground with 12V Siren:
Bridge the 12V power supply ground and the ESP32 logic ground together to avoid ground differential noise when the siren turns on.
Complete Tested Arduino C++ Firmware
``cpp
/*
- ==============================================================================
- Project: Flood Water Level Warning Radar with VL53L1X & Ultrasonic Redundancy
- Microcontroller: ESP32 DevKit V1 (Xtensa 32-bit Dual-Core 240MHz)
- Author: Mentora Maker Lab (Spark Autonomous Content Intelligence)
- License: MIT
*
- Hardware Peripherals:
- - STMicroelectronics VL53L1X Time-of-Flight Sensor:
- I2C Interface: SDA -> GPIO 21, SCL -> GPIO 22 (Address: 0x29)
- - JSN-SR04T Waterproof Ultrasonic Transducer:
- TRIG -> GPIO 5, ECHO -> GPIO 18
- - Flood Warning Siren Relay (Active-LOW) -> GPIO 26
- - Status LED Indicator -> GPIO 2
- ==============================================================================
*/
#include <Wire.h>
#include <VL53L1X.h>
#include <WiFi.h>
#include <HTTPClient.h>
// -----------------------------------------------------------------------------
// USER CONFIGURATION (WIFI & FLOOD WATER THRESHOLDS)
// -----------------------------------------------------------------------------
const char* WIFISSID = "YOURWIFI_SSID";
const char* WIFIPASSWORD = "YOURWIFI_PASSWORD";
const char* TELEMETRY_URL = "http://api.mentora.in/telemetry/flood";
#define TRIG_PIN 5
#define ECHO_PIN 18
#define SIRENRELAYPIN 26
#define STATUSLEDPIN 2
// Overhead Bridge Mount Reference (Distance from sensor down to canal bed)
const uint16t SENSORMOUNTHEIGHTMM = 3500; // Sensor mounted 3.5m above river bed
// Clearance Thresholds from Water Surface to Sensor
#define CLEARANCEEMERGENCYMM 500 // Water level within 50cm of overflow -> SIREN ON!
#define CLEARANCEWARNINGMM 1200 // Water level within 1.2m -> Yellow Alert
VL53L1X laserSensor;
// Telemetry State
uint16_t laserDistanceMM = 0;
uint16_t sonicDistanceMM = 0;
uint16_t validatedClearanceMM = 0;
uint16_t calculatedWaterDepth = 0;
bool isEmergencyAlarmActive = false;
unsigned long lastSampleTimestamp = 0;
const unsigned long SAMPLEINTERVALMS = 1000;
// -----------------------------------------------------------------------------
// JSN-SR04T ULTRASONIC SAMPLING (ACOUSTIC)
// -----------------------------------------------------------------------------
uint16_t readUltrasonicDistanceMM() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// Measure pulse duration (timeout 35ms ~ 6 meters)
long durationMicros = pulseIn(ECHO_PIN, HIGH, 35000);
if (durationMicros == 0) return 0; // Timeout / No Echo
// Speed of sound = 343 m/s = 0.343 mm/us -> Distance = (duration * 0.343) / 2
float distMM = (float)durationMicros * 0.1715;
return (uint16_t)distMM;
}
// -----------------------------------------------------------------------------
// DUAL-SENSOR FAULT ISOLATION & CONSENSUS VOTING
// -----------------------------------------------------------------------------
uint16t evaluateConsensusClearance(uint16t laserDist, uint16_t sonicDist) {
bool laserValid = (laserDist > 40 && laserDist < 4000);
bool sonicValid = (sonicDist > 200 && sonicDist < 5000);
// Case 1: Both sensors reporting valid distances
if (laserValid && sonicValid) {
int delta = abs((int)laserDist - (int)sonicDist);
// If consensus within 100mm, average both readings
if (delta < 100) {
return (laserDist + sonicDist) / 2;
}
// If discrepancy, prefer ultrasonic (handles water surface ripples better)
return sonicDist;
}
// Case 2: Laser failed (specular scatter) -> Fall back to acoustic
if (sonicValid) return sonicDist;
// Case 3: Ultrasonic failed -> Fall back to laser
if (laserValid) return laserDist;
return 0; // Fault condition (Both sensors obstructed)
}
// -----------------------------------------------------------------------------
// ARDUINO SETUP
// -----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n========================================================");
Serial.println(" MENTORA ESP32 DUAL-RADAR FLOOD EARLY WARNING TERMINAL ");
Serial.println("========================================================");
// 1. Boot glitch prevention for Siren Relay (Active-LOW)
digitalWrite(SIRENRELAYPIN, HIGH); // Pre-charge HIGH (Siren OFF)
pinMode(SIRENRELAYPIN, OUTPUT);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
digitalWrite(TRIG_PIN, LOW);
pinMode(STATUSLEDPIN, OUTPUT);
digitalWrite(STATUSLEDPIN, LOW);
// 2. Initialize VL53L1X Laser ToF
Wire.begin(21, 22);
Wire.setClock(400000); // 400 kHz
laserSensor.setBus(&Wire);
if (!laserSensor.init()) {
Serial.println("[WARNING] VL53L1X ToF sensor failed to initialize! Check I2C bus.");
} else {
laserSensor.setDistanceMode(VL53L1X::Long); // 4-meter ranging mode
laserSensor.setMeasurementTimingBudget(50000); // 50ms timing budget
laserSensor.startContinuous(50);
Serial.println("[OK] VL53L1X Long-Range Laser Radar online.");
}
// 3. Connect Wi-Fi
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! Monitoring station IP: " + WiFi.localIP().toString());
} else {
Serial.println("\n[WARNING] Wi-Fi timed out. Operating in standalone siren mode.");
}
Serial.println("[SYSTEM] Continuous hydrological observation active.\n");
lastSampleTimestamp = millis();
}
// -----------------------------------------------------------------------------
// ARDUINO MAIN LOOP
// -----------------------------------------------------------------------------
void loop() {
unsigned long now = millis();
if (now - lastSampleTimestamp >= SAMPLEINTERVALMS) {
lastSampleTimestamp = now;
// 1. Read Laser Distance
laserSensor.read();
laserDistanceMM = laserSensor.rangingdata.rangemm;
// 2. Read Ultrasonic Distance
sonicDistanceMM = readUltrasonicDistanceMM();
// 3. Evaluate Consensus
validatedClearanceMM = evaluateConsensusClearance(laserDistanceMM, sonicDistanceMM);
if (validatedClearanceMM > 0 && validatedClearanceMM <= SENSORMOUNTHEIGHT_MM) {
calculatedWaterDepth = SENSORMOUNTHEIGHT_MM - validatedClearanceMM;
} else {
calculatedWaterDepth = 0;
}
Serial.printf("[RADAR] Laser: %4d mm | Sonic: %4d mm | Clearance: %4d mm | Depth: %4d mm\n",
laserDistanceMM, sonicDistanceMM, validatedClearanceMM, calculatedWaterDepth);
// 4. Threshold & Siren Relay Control
if (validatedClearanceMM > 0 && validatedClearanceMM <= CLEARANCEEMERGENCYMM) {
if (!isEmergencyAlarmActive) {
Serial.println("🚨 [EMERGENCY] FLOOD WATER REACHED DANGER CREST! ENERGIZING SIREN 🚨");
digitalWrite(SIRENRELAYPIN, LOW); // Active-LOW: Turn on 110dB Siren!
isEmergencyAlarmActive = true;
}
digitalWrite(STATUSLEDPIN, HIGH);
} else {
if (isEmergencyAlarmActive) {
Serial.println("[STATUS] Water level receded below emergency crest. Siren silenced.");
digitalWrite(SIRENRELAYPIN, HIGH); // Turn off siren
isEmergencyAlarmActive = false;
}
digitalWrite(STATUSLEDPIN, LOW);
}
}
delay(20);
}
``
Build a Flood Water Level Warning Radar with ESP32 & VL53L1X ToF Laser Firmware & Schematics Vault
Download verified Arduino C++ firmware, pinout table, and BOM for Build a Flood Water Level Warning Radar with ESP32 & VL53L1X ToF Laser.
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.
#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.
Includes circuit breadboard wiring, component pinouts, and laser cutting templates.
Screen-share or show your breadboard to M. F. Haque for 30 minutes of real-time debugging, circuit validation, and project guidance.
See What Mentora Makers Are Building
Real video walk-throughs, breadboard tests, and flight demonstrations submitted by students & parents.
Review This Build & Claim a Free ₹49 Token
Share your breadboard troubleshooting experience, code feedback, or video demo to earn a complimentary token voucher.
Have a Reward Voucher?
Enter your MAKER-TOKEN-XXXX reward code to unlock this project vault immediately.
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!
Why does the JSN-SR04T report erratic zero readings below 25cm?
Root Cause: Acoustic Blind Zone: The piezoelectric transducer needs ~1.2ms for mechanical ringing to damp out after transmission before switching to receive mode. Fix: Mount the sensor module at least 30cm above the maximum possible flood overflow crest.
Why does the VL53L1X lock up on the I2C bus?
Root Cause: Connecting to an unshielded cable longer than 1 meter. I2C is an on-board protocol. For cable runs >30cm, place 2.2kΩ pull-up resistors on SDA (GPIO 21) and SCL (GPIO 22).
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.