IoTSimulator

Arduino Flood Early Warning & Water Level Monitor with Ultrasonic

Build an automated flood warning station using an Arduino, an ultrasonic sensor to track rising water levels, a 10-segment LED bar graph, and an emergency audio siren.
Muhammad Ichsanul Fadhil
IoTSimulator Team
October 24, 2026
Arduino Flood Early Warning & Water Level Monitor with Ultrasonic

Live project track

Interactive hardware & logic preview

Have you ever watched storm water slowly creep up a street drain or riverbank during heavy monsoon rain? By the time muddy water spills across the pavement and enters doorways, it is often too late to move cars, secure electrical appliances, or evacuate families safely. Detecting rising water before it reaches road level gives emergency crews and neighborhood teams precious minutes to act.

Building an automated flood monitoring station provides early visibility without requiring someone to stand in dangerous floodwaters with a measuring stick. When mounted safely under a bridge or drainage roof, the system tracks the water level continuously, illuminating a tall visual column of light and sounding a piercing audio siren when water reaches the danger mark.

In this project, you will build an automated water level monitor and emergency warning station using an Arduino Uno, an HC-SR04 ultrasonic distance sensor, a 10-segment LED bar graph, and a piezo buzzer. The core concept you will master is: inverted clearance ranging — measuring the shrinking air gap above a rising liquid surface and mapping that physical distance into an intuitive multi-stage visual and audible alert ladder.

Before assembling the sensor probe and LED indicator bar, take a look at the system architecture diagram below. It illustrates how the ultrasonic distance sensor, multi-stage LED bar, active buzzer, and water basin interact in our automated alert station:

Inverted headspace distance analogy showing ultrasonic sensor measuring air gap above water
Inverted headspace distance analogy showing ultrasonic sensor measuring air gap above water

As visualized in the system diagram, rising flood water continuously compresses the air gap between the water surface and the sensor above. In the next section, we explore the physics of ultrasonic sound pulses and how echo timings translate into precise millimeter water levels.

Part 1 — Understand the Core Idea: Measuring the Empty Gap

To understand how an overhead sensor tracks water levels, imagine holding a tape measure from the ceiling down toward a bucket on the floor. When the bucket is completely empty, the distance from the ceiling to the bottom is large (say, 100 centimeters). But as water fills the bucket, the water surface moves closer and closer to your tape measure. When the water is 5 centimeters from the rim, the empty gap has shrunk to almost nothing!

Notice the relationship: a smaller distance reading means a fuller bucket. Our flood monitor uses this exact principle of inverted clearance:

  • Clearance Gap (Headspace): The distance measured by the ultrasonic sensor from its fixed mounting position down to the water surface.
  • Water Height Index: The calculation performed by the Arduino. When the clearance gap is large (100 cm), water is low and safe (0 LED segments lit). When the gap shrinks to 5 cm, water is dangerously high (all 10 LED segments lit).

The schematic below illustrates how high-frequency 40 kHz acoustic chirp pulses bounce off the rising water line and return to the receiver transducer:

Ultrasonic sound wave reflection mechanics diagram showing pulse timing and echo
Ultrasonic sound wave reflection mechanics diagram showing pulse timing and echo

Because the acoustic echo arrives faster as the water level rises, measuring the microseconds between pulse trigger and echo return gives our Arduino an immediate, millimeter-accurate gauge of flood threat.

How does the ultrasonic sensor measure this gap without touching the water? It uses high-frequency sound echoes:

  1. The Arduino sends a brief 10-microsecond electrical pulse to the sensor's TRIG pin.
  2. The transmitter cylinder emits a burst of 8 ultrasonic pulses at 40,000 Hz. These high-frequency acoustic waves travel downward through the air at roughly 340 meters per second.
  3. When the sound waves hit the flat surface of the water, they reflect back upward like an echo shouting off a canyon wall.
  4. The receiver cylinder detects the returning echo and keeps its ECHO pin HIGH for the exact round-trip flight duration. We calculate distance in centimeters using the speed of sound: Distance = (Duration × 0.034) / 2.

Here is how our monitoring station classifies flood risk across the 10-segment LED bar graph:

Risk LevelClearance GapLED Bar Graph DisplayBuzzer Audio SirenField Assessment
Safe Baseline65 cm to 100 cm1 to 3 Green Segments (D2–D4)SilentNormal river flow; drainage capacity is wide open.
Moderate Caution35 cm to 65 cm4 to 7 Yellow Segments (D5–D8)SilentDrainage canal filling steadily; maintenance teams placed on standby.
Critical Flood ThreatUnder 25 cm8 to 10 Red Segments (D9–D11)Pulsing 1200 Hz SirenWater overflowing banks; immediate evacuation required!
Because sound waves reflect cleanly off calm water surfaces, ultrasonic sensors provide accurate water depth tracking without submerging any electrical wires in dirty water.

Part 2 — Build the Circuit: Wiring Sensor, LED Bus, and Siren

Before plugging wires into pins, let us understand how electrical power is routed across our station:

  • 5V (Positive Power Bus): Supplies regulated 5V power from the Arduino Uno to power the ultrasonic sensor's acoustic pulse transmitter and receiver amplifier circuit.
  • GND (Common Ground): Acts as the shared return line for all electrical current. All 10 LED segment cathodes must tie back to Arduino GND, along with the ultrasonic ground and buzzer ground, so all control voltages share the same 0V baseline.

To ensure every indicator LED and buzzer circuit is wired safely without drawing excessive current from the microcontroller, study the complete wiring layout below:

10-segment LED bar graph bus wiring diagram with common cathode ground bus
10-segment LED bar graph bus wiring diagram with common cathode ground bus

Follow the step-by-step wiring tables below to connect each component to your Arduino Uno:

Pin Connection Map
HC-SR04 Ultrasonic
VCC
→
Arduino Uno
5V
Explanation
Powers the ultrasonic pulse generator and amplifier.
HC-SR04 Ultrasonic
GND
→
Arduino Uno
GND.2
Explanation
Sensor ground return path.
HC-SR04 Ultrasonic
TRIG
→
Arduino Uno
D12
Explanation
Receives 10-microsecond trigger start pulses from Arduino.
HC-SR04 Ultrasonic
ECHO
→
Arduino Uno
D13
Explanation
Outputs HIGH pulse duration proportional to air gap clearance.
Pin Connection Map
10-Segment LED Bar Graph
Anodes A1 to A4 (Green)
→
Arduino Uno
D2 to D5
Explanation
Digital outputs D2 through D5 drive the green safe water level segments.
10-Segment LED Bar Graph
Anodes A5 to A7 (Yellow)
→
Arduino Uno
D6 to D8
Explanation
Digital outputs D6 through D8 drive the yellow warning segments.
10-Segment LED Bar Graph
Anodes A8 to A10 (Red)
→
Arduino Uno
D9 to D11
Explanation
Digital outputs D9 through D11 drive the red critical flood segments.
10-Segment LED Bar Graph
Cathodes C1 to C10
→
Arduino Uno
GND.1
Explanation
All 10 cathode pins are daisy-chained together into a shared ground return line.
Pin Connection Map
Piezo Siren Buzzer
Pin 1 (+)
→
Arduino Uno
A0
Explanation
Analog pin A0 configured as digital output to generate 1200 Hz siren tones.
Piezo Siren Buzzer
Pin 2 (-)
→
Arduino Uno
GND.3
Explanation
Completes buzzer audio return path to Arduino ground.

Why Did We Choose These Specific Pins?

Notice how our pin layout is neat and grouped logically:

  • Sequential Pins D2 through D11 for the LED Bar: Using ten consecutive digital pins allows us to control the entire 10-segment display with a simple for (int i = 0; i < 10; i++) loop. This keeps the code concise, fast, and easy to understand.
  • Digital Pins 12 and 13 for Ultrasonic Sonar: Pin 12 and Pin 13 are standard digital pins with low capacitance, ideal for cleanly generating microsecond-level trigger pulses and reading echo return edges.
  • Analog Pin A0 for the Piezo Buzzer: On the Arduino Uno, analog pins A0 through A5 can also function as general-purpose digital input/output pins. Using pin A0 for our buzzer siren keeps our digital side dedicated entirely to the 10-segment LED display.

Part 3 — Program the Behavior: Complete Arduino Sketch

Here is the full, tested firmware for the flood early warning station. You can copy this code directly into your sketch or run it straight inside the IoTSimulator workspace:

C++ Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
const int trigPin = 12;
const int echoPin = 13;
const int buzzerPin = A0;
const int barPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

long readDistance() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH, 30000);
  if (duration == 0) return 100; // Timeout fallback: tank is empty / clear
  return (duration * 0.034) / 2;
}

void setup() {
  Serial.begin(9600);
  Serial.println("[FLOOD]: Early Warning Monitoring Station Initializing...");

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(buzzerPin, OUTPUT);

  for (int i = 0; i < 10; i++) {
    pinMode(barPins[i], OUTPUT);
    digitalWrite(barPins[i], LOW);
  }

  Serial.println("[FLOOD]: Ready. Monitoring River & Water Level Height.");
}

void loop() {
  long dist = readDistance();

  // As water rises closer to the sensor (dist shrinks from 100cm down to 5cm),
  // the waterLevel bar index climbs from 0 up to 10 illuminated segments.
  int waterLevel = map(constrain(dist, 5, 100), 100, 5, 0, 10);

  // Update 10-segment LED bar graph
  for (int i = 0; i < 10; i++) {
    if (i < waterLevel) {
      digitalWrite(barPins[i], HIGH);
    } else {
      digitalWrite(barPins[i], LOW);
    }
  }

  // Critical flood threshold: Level 8 or higher activates emergency siren
  if (waterLevel >= 8) {
    tone(buzzerPin, 1200, 100);
    Serial.print("[ALERT]: Critical Water Level! Clearance = ");
    Serial.print(dist);
    Serial.print("cm | Bar = ");
    Serial.print(waterLevel);
    Serial.println("/10 | EVACUATION SIREN ACTIVE!");
  } else {
    noTone(buzzerPin);
    Serial.print("[Status]: Water Clearance = ");
    Serial.print(dist);
    Serial.print("cm | Bar = ");
    Serial.print(waterLevel);
    Serial.println("/10 | Normal");
  }

  delay(200);
}

To understand how the firmware decides which LED segments to illuminate and when to pulse the audio alarm during each loop cycle, examine the danger threshold mapping below:

Three-tier flood hazard classification showing green, yellow, and red alarm zones
Three-tier flood hazard classification showing green, yellow, and red alarm zones

By partitioning the total basin height into clear green, yellow, and red safety zones, the Arduino provides instantaneous visual and acoustic awareness for rapid evacuation. Now let us examine the firmware logic step by step.

How the Code Works, Step by Step

Let us walk through each major section of the sketch so you understand how the microcontroller calculates danger levels.

1. Pin Array and the readDistance() Helper Function

Notice how we define the display pins using an array: const int barPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};. This array holds the pin numbers in order from lowest (segment 1) to highest (segment 10).

Inside readDistance(), we send the required 10-microsecond trigger pulse. We read the echo duration using pulseIn(echoPin, HIGH, 30000). The 30000 microsecond timeout prevents the Arduino from freezing if sound waves scatter. The math (duration * 0.034) / 2 calculates one-way distance in centimeters.

2. Inverting Distance with map() and constrain()

The most important line in the entire program is: int waterLevel = map(constrain(dist, 5, 100), 100, 5, 0, 10);

Let us break down what this line does:

  • constrain(dist, 5, 100) clamps the distance reading so it never drops below 5 cm or climbs above 100 cm. This protects our calculation from odd glitches if debris floats past.
  • map(..., 100, 5, 0, 10) maps the numbers in reverse! Notice the order: 100 cm corresponds to 0 segments, while 5 cm corresponds to all 10 segments. As distance decreases, waterLevel increases!

3. Lighting the Rising Bar Column

A simple loop updates all ten LEDs: for (int i = 0; i < 10; i++). If index i < waterLevel, that pin is written HIGH. Otherwise, it is written LOW. This creates a solid, rising column of light like mercury in a thermometer.

4. Critical Hazard Siren on Pin A0

If waterLevel >= 8 (meaning floodwater is within 25 cm of the sensor), the Arduino activates the emergency buzzer using tone(buzzerPin, 1200, 100). This generates a clear 1200 Hz tone that sounds every 200 milliseconds. When water recedes below level 8, noTone(buzzerPin) silences the alarm immediately.

Part 4 — Practical Testing in the Simulator

Follow these quick steps to test your flood monitoring station inside the IoTSimulator workspace:

  1. Test Normal Standby: Press the green Run button. With the interactive Water Tank Actor set to low water (around 10% to 20%), observe the LED bar graph: only 1 or 2 bottom green segments light up, and the buzzer is completely silent.
  2. Simulate Heavy Rainfall: Click on the interactive Water Tank Actor and drag its water level slider upward past 50%. Watch the LED bar graph illuminate upward through the yellow warning segments (pins D6, D7, and D8).
  3. Trigger Emergency Evacuation Level: Drag the water level slider past 85%. Notice the red LEDs (pins D9, D10, and D11) illuminate to full height, and listen to the urgent 1200 Hz audio siren ring continuously. The Serial Monitor streams [ALERT]: CRITICAL FLOOD LEVEL! messages.
  4. Simulate Flood Recession: Drag the tank slider back down to 30%. The red segments turn off, the audio siren immediately silences, and the system returns to calm green standby.

Part 5 — Troubleshooting Common Beginner Mistakes

If your physical build or simulation does not behave as expected, check these common issues:

  • LED bar graph does not light up or only one LED turns on: LED bar graph modules have distinct polarity. If you install the module backward (swapping anodes and cathodes), current will be blocked. Double check that all cathode pins on the ground side connect to GND and that each individual anode pin connects to its corresponding digital pin.
  • Ultrasonic sensor returns sporadic 0 cm or 100 cm readings over real water: Sound waves reflect off calm water like a flat mirror. However, if water is churning with heavy rapids or thick foam, the acoustic sound waves can scatter sideways instead of bouncing straight back. In real river installations, engineers mount the sensor inside a vertical PVC 'stilling tube' with small drainage holes at the bottom to calm surface ripples.
  • All LEDs appear dim when several are turned on: If multiple LEDs share one digital pin or are powered without proper current limiting, the microcontroller's internal voltage regulator can drop. In real builds, always place a 220-ohm or 330-ohm resistor in series with each LED anode line.
  • Buzzer does not make sound on pin A0: Make sure you declare pinMode(buzzerPin, OUTPUT); in setup(). Analog pins on the Arduino Uno work perfectly as digital output pins, but they must be initialized as outputs first.

Part 6 — Taking It Further: Next Engineering Steps

Once your basic flood monitor is running smoothly, consider expanding its capabilities with these practical additions:

  • Add a Solar-Powered Rechargeable Battery: Pair your Arduino Uno with a small 10W solar panel and a 3.7V 18650 lithium battery power bank so your flood monitor continues watching river channels even when severe storms knock out municipal power lines.
  • Connect an Automated Sump Pump Relay: Wire a 5V relay module to digital pin A1. When floodwater reaches level 7, the Arduino can automatically start a drainage sump pump to pump water out of the basement before it reaches living areas.
  • Add LoRa or Cellular Telemetry: Connect a LoRa radio transmitter module to broadcast river level telemetry across distances of up to 10 kilometers to a central city disaster response hub without relying on damaged local Wi-Fi or cellular networks.
Keywords
#Arduino #Flood Warning #Water Level Monitor #Ultrasonic Sensor #LED Bar Graph #Disaster Mitigation #Early Warning
Total word count: 2017 words

Project discussion

Questions, feedback, and community insights
No discussions yet. Be the first to start!