HW-038 Water Level Sensor with an Arduino Uno
A cheap analog water sensor whose output voltage rises as more of its exposed traces go under water.

On an Arduino Uno the HW-038 takes its power from D7 instead of the 5V pin, so the sketch switches it on for 10 ms per reading and the traces are not corroded by constant DC. Its S output goes to A0, where the Uno's 10-bit counts are mapped to a level through a five-point calibration table.
Wiring the HW-038 Water Level Sensor to the Arduino Uno
Pin by pin.
| HW-038 Water Level Sensor | Arduino Uno |
|---|---|
| VCC | D7 |
| GND | GND |
| S | A0 |
- The module pulls S to GND through only 100 Ω, so in very conductive (salty) water + can draw tens of milliamps from the pin that powers it. An Uno pin is rated 20 mA (40 mA absolute maximum): for salt water, switch + through a transistor.
What to know on the Arduino Uno
- The sensor's transistor feeds S through 100 Ω to ground. At the sketch's full-immersion count of 641 (about 3.1 V on the Uno's 5 V reference), that alone is about 31 mA out of D7, above the 20 mA Arduino recommends per pin though under the 40 mA absolute maximum. The 10 ms pulses keep it brief; for deep immersion, switch + through a transistor instead.Last Minute Engineers HW-038 teardown; Arduino UNO R3 tech specs; Microchip ATmega328P datasheet DS40002061B, p. 308
- Powering + from D7 only while reading means the traces carry DC for about 10 ms a second instead of continuously, which is what slows their electrolytic corrosion.Last Minute Engineers HW-038 teardown
- The calibration counts (335 at 0 % to 641 at 100 %) belong to the Uno's 10-bit ADC on 5 V and to tap water; a 3.3 V board or other water gives other numbers, so measure your own five points.Microchip ATmega328P datasheet DS40002061B, p. 2; Last Minute Engineers HW-038 teardown
HW-038 Water Level Sensor code for the Arduino Uno
Compiled for arduino:avr:uno on 2026-10-29.
// HW-038 Water Level Sensor on Arduino Uno: wiring and a free simulation at https://www.tinkered.ai/components/water-level-sensor-hw-038/arduino-uno
const int POWER_PIN = 7; // HW-038 +: powered only to read (the traces corrode under constant DC)
const int SIGNAL_PIN = A0; // HW-038 S
const int DRY_BELOW = 100; // under this the traces are dry
// what this sensor reads in this water at 0, 25, 50, 75 and 100 % of its sensing length (tap water, 5 V)
const int CAL[5] = { 335, 487, 565, 611, 641 };
int readSensor() {
digitalWrite(POWER_PIN, HIGH);
delay(10); // let S settle
int raw = analogRead(SIGNAL_PIN);
digitalWrite(POWER_PIN, LOW);
return raw;
}
int levelPercent(int raw) {
if (raw < DRY_BELOW) return 0;
if (raw <= CAL[0]) return 1;
for (int i = 1; i < 5; i++) {
if (raw <= CAL[i]) return 25 * (i - 1) + (long)(raw - CAL[i - 1]) * 25 / (CAL[i] - CAL[i - 1]);
}
return 100;
}
void setup() {
Serial.begin(9600);
pinMode(POWER_PIN, OUTPUT);
digitalWrite(POWER_PIN, LOW);
Serial.println("HW-038 ready");
}
void loop() {
int raw = readSensor();
Serial.print("raw ");
Serial.print(raw);
Serial.print(" level ");
Serial.print(levelPercent(raw));
Serial.println(" %");
delay(1000);
}
HW-038 Water Level Sensor on the Arduino Uno: questions
Why is the HW-038 powered from D7 instead of 5V on the Uno?
So the sketch can switch it on only while reading. Constant DC through wet traces corrodes them; the sketch powers it for 10 ms per reading.
Is it safe to power the HW-038 from an Uno pin?
For short readings it works, but fully immersed it can draw around 31 mA from D7, more than the Uno's recommended 20 mA per pin. Keep the pulses short as the sketch does, or switch + through a transistor.
Why are my HW-038 readings different from the sketch's calibration?
The counts depend on the water's minerals and on the Uno's 5 V reference. Record the readings at 0, 25, 50, 75 and 100 % in your own water and replace the CAL table.
Keep going
This guide as plain Markdown. /components/water-level-sensor-hw-038/arduino-uno.md