The HC-SR04 is a 5 V part on a 3.3 V board, and the D1 mini makes the power side easy: its 5V pin gives the sensor USB 5 V directly. The only extra parts are two resistors that bring the 5 V Echo pulse down to a level the ESP8266 can take.
Trig goes on D5 and Echo on D6, keeping the I2C pins free for a display shield and well clear of the boot pins. The sketch takes five pings and prints the median, which is what you want for a water tank or parking sensor.
Wiring
| Wemos D1 mini | HC-SR04 ultrasonic sensor | Wire | Notes |
|---|---|---|---|
| 5V | VCC | red | The classic HC-SR04 needs 5 V. The 5V pin carries USB 5 V. |
| D5 | Trig | purple | A 3.3 V trigger pulse is enough. |
| D6 | Echo | green | Via 1 kΩ / 2 kΩ divider — Echo is a 5 V pulse: 1 kΩ from Echo to D6, 2 kΩ from D6 to GND → about 3.3 V. |
| GND | GND | black |
5 V (HC-SR04P / RCWL-1601: 3–5.5 V) part, 3.3 V board. The classic HC-SR04 needs 5 V and its ECHO pin outputs 5 V. On a 3.3 V board (ESP32, Pico) send ECHO through a 1 kΩ / 2 kΩ divider. A 3.3 V trigger signal is enough for TRIG.
Board quirks
- The ESP8266’s pins are rated for 3.6 V. Echo from a 5 V HC-SR04 must go through the divider; “it worked without one” only means it hasn’t failed yet.
- D5 and D6 are used here so D1/D2 stay free for an I2C display shield, and D3/D4/D8 (boot pins) stay untouched.
- The 5V pin is only 5 V while the board runs from USB or a 5 V supply. On a single Li-ion cell it is 3.0–4.2 V and the classic HC-SR04 gets unreliable — use the HC-SR04P / RCWL-1601, which runs from 3V3 and needs no divider.
pulseIn()busy-waits, so it has a 25 ms timeout here. A pulseIn with no timeout and no echo would hold the CPU for a full second, long enough to upset Wi-Fi once you add it.- The median of five pings throws out the stray long echo that a single reading lets through — useful for tank levels where the walls bounce sound around.
Code
// HC-SR04 on a Wemos D1 mini (ESP8266).
// VCC → 5V, Trig → D5 (GPIO14), Echo → D6 (GPIO12) through a 1 kΩ / 2 kΩ divider, GND → GND.
// Prints the median of 5 pings, which throws away the odd bad echo.
const uint8_t TRIG_PIN = D5;
const uint8_t ECHO_PIN = D6;
const unsigned long TIMEOUT_US = 25000; // about 4 m there and back; pulseIn() returns 0 after this
float pingCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long us = pulseIn(ECHO_PIN, HIGH, TIMEOUT_US);
return us ? us * 0.0343f / 2.0f : -1.0f;
}
void setup() {
Serial.begin(115200);
Serial.println();
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.println("HC-SR04: Trig D5, Echo D6 (through the divider)");
}
void loop() {
float r[5];
for (int i = 0; i < 5; i++) {
r[i] = pingCm();
delay(60); // lets the last echo die out, and lets the ESP8266's system tasks run
}
for (int i = 1; i < 5; i++) { // insertion sort, then take the middle value
for (int j = i; j > 0 && r[j] < r[j - 1]; j--) {
float t = r[j];
r[j] = r[j - 1];
r[j - 1] = t;
}
}
if (r[2] < 0) Serial.println("Out of range (no echo)");
else Serial.printf("Distance: %.1f cm\n", r[2]);
delay(500);
}// HC-SR04 on a Wemos D1 mini (ESP8266).
// VCC → 5V, Trig → D5 (GPIO14), Echo → D6 (GPIO12) through a 1 kΩ / 2 kΩ divider, GND → GND.
// Prints the median of 5 pings, which throws away the odd bad echo.
const uint8_t TRIG_PIN = D5;
const uint8_t ECHO_PIN = D6;
const unsigned long TIMEOUT_US = 25000; // about 4 m there and back; pulseIn() returns 0 after this
float pingCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long us = pulseIn(ECHO_PIN, HIGH, TIMEOUT_US);
return us ? us * 0.0343f / 2.0f : -1.0f;
}
void setup() {
Serial.begin(115200);
Serial.println();
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.println("HC-SR04: Trig D5, Echo D6 (through the divider)");
}
void loop() {
float r[5];
for (int i = 0; i < 5; i++) {
r[i] = pingCm();
delay(60); // lets the last echo die out, and lets the ESP8266's system tasks run
}
for (int i = 1; i < 5; i++) { // insertion sort, then take the middle value
for (int j = i; j > 0 && r[j] < r[j - 1]; j--) {
float t = r[j];
r[j] = r[j - 1];
r[j - 1] = t;
}
}
if (r[2] < 0) Serial.println("Out of range (no echo)");
else Serial.printf("Distance: %.1f cm\n", r[2]);
delay(500);
}Expected serial output
HC-SR04: Trig D5, Echo D6 (through the divider)
Distance: 42.7 cm
Distance: 42.6 cm
Out of range (no echo)
Distance: 118.3 cmHC-SR04: Trig D5, Echo D6 (through the divider) Distance: 42.7 cm Distance: 42.6 cm Out of range (no echo) Distance: 118.3 cm
Common errors
Always “Out of range” or 0
VCC on 3V3 instead of 5V, or the divider is the wrong way round (1 kΩ on the sensor side, 2 kΩ to GND). Full fix.
Readings jump around
Soft or angled targets absorb the ping. Aim at something flat and hard; in a tank, mount the sensor square to the water surface.