MicroPython: HC-SR04 Ultrasonic Sensor with ESP32 and ESP8266 (Measure distance)

In this guide, you’ll learn how to use the HC-SR04 Ultrasonic Sensor with the ESP32 and ESP8266 to get the distance to an object using MicroPython firmware. This tutorial covers how to wire the sensor to the ESP32 and ESP8266 boards and provides a simple MicroPython script to get the distance to an object and display it on an OLED display.

MicroPython HC-SR04 Ultrasonic Sensor with ESP32 ESP8266 NodeMCU Measure distance

Prerequisites

To follow this tutorial you need MicroPython firmware installed in your ESP32 or ESP8266 boards. You also need an IDE to write and upload the code to your board. We suggest using Thonny IDE or uPyCraft IDE:

Or, if you’re familiar with VS Code, you may want to use the PyMakr extension:

Learn more about MicroPython: MicroPython Programming with ESP32 and ESP8266 eBook.

You might also like reading other HC-SR04 Ultrasonic guides:

Introducing the HC-SR04 Ultrasonic Sensor

The HC-SR04 ultrasonic sensor uses sonar to determine the distance to an object. This sensor reads from 2cm to 400cm (0.8inch to 157inch) with an accuracy of 0.3cm (0.1inches), which is good for most hobbyist projects. In addition, this particular module comes with ultrasonic transmitter and receiver modules.

The following picture shows the HC-SR04 ultrasonic sensor.

HC-SR04 Ultrasonic Sensor Module Distance Measurement Component Part Front

The next picture shows the other side of the sensor.

HC-SR04 Ultrasonic Sensor Module Distance Measurement Component Part Back

Where to Buy HC-SR04 Ultrasonic Sensor?

You can check the Ultrasonic Sensor HC-SR04 on Maker Advisor to find the best price:

HC-SR04 Ultrasonic Sensor Technical Data

The following table shows the key features and specs of the HC-SR04 ultrasonic sensor. For more information, you should consult the sensor’s datasheet.

Power Supply5V DC
Working Current15 mA
Working Frequency40 kHz
Maximum Range4 meters
Minimum Range2 cm
Measuring Angle15º
Resolution0.3 cm
Trigger Input Signal10uS TTL pulse
Echo Output SignalTTL pulse proportional to the distance range
Dimensions45mm x 20mm x 15mm

HC-SR04 Ultrasonic Sensor Pinout

Here’s the pinout of the HC-SR04 Ultrasonic Sensor.

VCCPowers the sensor (5V)
TrigTrigger Input Pin
EchoEcho Output Pin
GNDCommon GND

How Does the HC-SR04 Ultrasonic Sensor Work?

The ultrasonic sensor uses sonar to determine the distance to an object. Here’s how it works:

  1. The ultrasound transmitter (trig pin) emits a high-frequency sound (40 kHz).
  2. The sound travels through the air. If it finds an object, it bounces back to the module.
  3. The ultrasound receiver (echo pin) receives the reflected sound (echo).
How Ultrasonic Sensor Works

Taking into account the sound’s velocity in the air and the travel time (time passed since the transmission and reception of the signal) we can calculate the distance to an object. Here’s the formula:

distance to an object = ((speed of sound in the air)*time)/2
  • speed of sound in the air at 20ºC (68ºF) = 343m/s

Parts Required

ESP8266 NodeMCU Board HC-SR04 Ultrasonic Sensor Module Arduino Demonstration

To complete this tutorial you need the following parts:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Schematic – ESP32 with HC-SR04 Ultrasonic Sensor

Wire the HC-SR04 ultrasonic sensor to the ESP32 as shown in the following schematic diagram. We’re connecting the Trig pin to GPIO 5 and the Echo pin to GPIO 18, but you can use any other suitable pins. Go to the next section if you’re using an ESP8266 board.

ESP32 Wiring Ultrasonic Sensor Diagram
Ultrasonic SensorESP32
VCCVIN
TrigGPIO 5
EchoGPIO 18
GNDGND

Recommended reading: ESP32 Pinout Reference Guide

Schematic – ESP8266 with HC-SR04 Ultrasonic Sensor

Wire the HC-SR04 ultrasonic sensor to the ESP8266 as shown in the following schematic diagram. We’re connecting the Trig pin to GPIO 12 and the Echo pin to GPIO 14, but you can use any other suitable pins.

ESP32 Wiring Ultrasonic Sensor Diagram
Ultrasonic SensorESP8266
VCCVIN
TrigGPIO 12 (D6)
EchoGPIO 14 (D5)
GNDGND

Recommended reading: ESP8266 Pinout Reference Guide

HC-SR04 MicroPython Library

There are multiple ways to get the distance to an object using the HC-SR04 and the ESP32/ESP8266 boards using MicroPython firmware. We’ll use this HC-SR04 MicroPython Library that makes it straightforward to interface the sensor and get measurements.

The library we’ll use isn’t part of the standard MicroPython library by default. So, you need to upload the following library to your ESP32/ESP8266 board (save it with the name hcsr04.py).

import machine, time
from machine import Pin

__version__ = '0.2.0'
__author__ = 'Roberto Sánchez'
__license__ = "Apache License 2.0. https://www.apache.org/licenses/LICENSE-2.0"

class HCSR04:
    """
    Driver to use the untrasonic sensor HC-SR04.
    The sensor range is between 2cm and 4m.
    The timeouts received listening to echo pin are converted to OSError('Out of range')
    """
    # echo_timeout_us is based in chip range limit (400cm)
    def __init__(self, trigger_pin, echo_pin, echo_timeout_us=500*2*30):
        """
        trigger_pin: Output pin to send pulses
        echo_pin: Readonly pin to measure the distance. The pin should be protected with 1k resistor
        echo_timeout_us: Timeout in microseconds to listen to echo pin. 
        By default is based in sensor limit range (4m)
        """
        self.echo_timeout_us = echo_timeout_us
        # Init trigger pin (out)
        self.trigger = Pin(trigger_pin, mode=Pin.OUT, pull=None)
        self.trigger.value(0)

        # Init echo pin (in)
        self.echo = Pin(echo_pin, mode=Pin.IN, pull=None)

    def _send_pulse_and_wait(self):
        """
        Send the pulse to trigger and listen on echo pin.
        We use the method `machine.time_pulse_us()` to get the microseconds until the echo is received.
        """
        self.trigger.value(0) # Stabilize the sensor
        time.sleep_us(5)
        self.trigger.value(1)
        # Send a 10us pulse.
        time.sleep_us(10)
        self.trigger.value(0)
        try:
            pulse_time = machine.time_pulse_us(self.echo, 1, self.echo_timeout_us)
            return pulse_time
        except OSError as ex:
            if ex.args[0] == 110: # 110 = ETIMEDOUT
                raise OSError('Out of range')
            raise ex

    def distance_mm(self):
        """
        Get the distance in milimeters without floating point operations.
        """
        pulse_time = self._send_pulse_and_wait()

        # To calculate the distance we get the pulse_time and divide it by 2 
        # (the pulse walk the distance twice) and by 29.1 becasue
        # the sound speed on air (343.2 m/s), that It's equivalent to
        # 0.34320 mm/us that is 1mm each 2.91us
        # pulse_time // 2 // 2.91 -> pulse_time // 5.82 -> pulse_time * 100 // 582 
        mm = pulse_time * 100 // 582
        return mm

    def distance_cm(self):
        """
        Get the distance in centimeters with floating point operations.
        It returns a float
        """
        pulse_time = self._send_pulse_and_wait()

        # To calculate the distance we get the pulse_time and divide it by 2 
        # (the pulse walk the distance twice) and by 29.1 becasue
        # the sound speed on air (343.2 m/s), that It's equivalent to
        # 0.034320 cm/us that is 1cm each 29.1us
        cms = (pulse_time / 2) / 29.1
        return cms

View raw code

Follow the next set of instructions for the IDE you’re using:

  • A. Upload HC-SR04 library with uPyCraft IDE
  • B. Upload HC-SR04 library with Thonny IDE

Before moving to the next section, make sure you have MicroPython firmware flashed into your ESP32 or ESP8266 boards. You can follow one of the next tutorials:

A. Upload HC-SR04 library with uPyCraft IDE

This section shows how to upload a library using uPyCraft IDE. If you’re using Thonny IDE, read the next section.

First, make sure you have a connection between the IDE and your board. Go to Tools > Serial and select the COM port. Go to Tools > Board and select the board. Then, click on the Connect button.

1. Create a new file by pressing the New File button (1).

2. Copy the HC-SR04 library code into that file. The HC-SR04 library code can be found here.

3. After copying the code, save the file by pressing the Save button (2).

Install HC-SR04 Library MicroPython Upycraft IDE

4. Call this new file hcsr04.py and press ok.

Save HC-SR04 Library Upycraft IDE

5. Click the Download and Run (3) button.

After this, the file should be on the device folder with the name hcsr04.py, as highlighted in the following figure.

Now, you can use the library functionalities in your code by importing the library.

B. Upload HC-SR04 library with Thonny IDE

If you’re using Thonny IDE, follow the next steps:

1. Copy the library code to a new file. The HC-SR04 library code can be found here.

2. Go to File > Save as…

Thonny IDE ESP32 ESP8266 MicroPython Save file library to device save as

3. Select save to “MicroPython device“:

Thonny IDE ESP32 ESP8266 MicroPython Save file library to device select

4. Name your file as hcsr04.py and press the OK button:

HC-SR04 library new MicroPython file Thonny IDE

And that’s it. The library was uploaded to your board. To make sure that it was uploaded successfully, go to File > Save as… and select the MicroPython device. Your file should be listed there:

HC-SR04 library MicroPython file created Thonny IDE

After uploading the library to your board, you can use the library functionalities in your code by importing the library.

Code – HC-SR04 Ultrasonic Sensor

After uploading the library to the ESP32 or ESP8266, copy the following code to the main.py or boot.py file. It simply prints the distance to the closest object every second (example adapted from the library page).

# Complete project details at https://RandomNerdTutorials.com/micropython-hc-sr04-ultrasonic-esp32-esp8266/
from hcsr04 import HCSR04
from time import sleep

# ESP32
sensor = HCSR04(trigger_pin=5, echo_pin=18, echo_timeout_us=10000)

# ESP8266
#sensor = HCSR04(trigger_pin=12, echo_pin=14, echo_timeout_us=10000)

while True:
    distance = sensor.distance_cm()
    print('Distance:', distance, 'cm')
    sleep(1)

View raw code

How the Code Works

First, you need to import the necessary libraries: import the HCSR04 class from the hcsr04 library. Additionally, you also need to import the time library to add delays to our code.

from hcsr04 import HCSR04
from time import sleep

Then, create an HCSR04 object called sensor that refers to the HCSR04 sensor. Pass as arguments the trigger pin, the echo pin, and the timeout (maximum travel time of the sound wave—when the sensor is probably out of range).

sensor = HCSR04(trigger_pin=5, echo_pin=18, echo_timeout_us=10000)

If you’re using the ESP8266, comment the previous line and uncomment the following to use different pins:

sensor = HCSR04(trigger_pin=12, echo_pin=14, echo_timeout_us=10000)

To get the distance in cm, you just need to call the distance_cm method on the sensor object. Save the result in the distance variable.

distance = sensor.distance_cm()

The library also provides a method to get the distance in millimeters without floating point. You just need to call:

distance = sensor.distance_mm()

Print the distance on the Micropython shell.

print('Distance:', distance, 'cm')

In the end, we add a delay of one second (the distance is updated every second):

sleep(1)

Demonstration

After uploading the code to your board, press the RST button to run the code. The distance to the closest object should be printed on the shell.

ESP32 ESP8266 with HC-SR04 MicroPython Demonstration

Display Distance (HCSR04) on OLED Display

Now that you know how to get the distance to the closest object using an HC-SR04 ultrasonic sensor, we’ll display the sensor readings on an OLED display.

MicroPython ESP32 ESP8266 NodeMCU Board HC-SR04 Ultrasonic Sensor Module Arduino OLED Demonstration

Parts Required

Here’s a list with the parts required to complete this example:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Schematic Diagram

Add an I2C OLED display to the previous circuit. Follow the schematic diagram for the board you’re using.

ESP32

ESP32 with HC-SR04 and OLED Display Wiring Diagram

ESP8266

ESP8266 NodeMCU with HC-SR04 and OLED Display Wiring Diagram

Files

For this example, you need three files:

  • hcsr04.py: this is the file that contains all the methods to use the HC-SR04 sensor. That’s the file you’ve uploaded previously.
  • ssd1306.py: this is the library for the SSD1306 I2C OLED display. You should upload it to your board to be able to communicate and write to the display.
  • main.py: this is the main script to get the distance and display it on the OLED display.

ssd1306.py

Create a new file called ss1306.py and copy the following code. Then, upload it to your board.

# MicroPython SSD1306 OLED driver, I2C and SPI interfaces created by Adafruit

import time
import framebuf

# register definitions
SET_CONTRAST        = const(0x81)
SET_ENTIRE_ON       = const(0xa4)
SET_NORM_INV        = const(0xa6)
SET_DISP            = const(0xae)
SET_MEM_ADDR        = const(0x20)
SET_COL_ADDR        = const(0x21)
SET_PAGE_ADDR       = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP       = const(0xa0)
SET_MUX_RATIO       = const(0xa8)
SET_COM_OUT_DIR     = const(0xc0)
SET_DISP_OFFSET     = const(0xd3)
SET_COM_PIN_CFG     = const(0xda)
SET_DISP_CLK_DIV    = const(0xd5)
SET_PRECHARGE       = const(0xd9)
SET_VCOM_DESEL      = const(0xdb)
SET_CHARGE_PUMP     = const(0x8d)


class SSD1306:
    def __init__(self, width, height, external_vcc):
        self.width = width
        self.height = height
        self.external_vcc = external_vcc
        self.pages = self.height // 8
        # Note the subclass must initialize self.framebuf to a framebuffer.
        # This is necessary because the underlying data buffer is different
        # between I2C and SPI implementations (I2C needs an extra byte).
        self.poweron()
        self.init_display()

    def init_display(self):
        for cmd in (
            SET_DISP | 0x00, # off
            # address setting
            SET_MEM_ADDR, 0x00, # horizontal
            # resolution and layout
            SET_DISP_START_LINE | 0x00,
            SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
            SET_MUX_RATIO, self.height - 1,
            SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
            SET_DISP_OFFSET, 0x00,
            SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,
            # timing and driving scheme
            SET_DISP_CLK_DIV, 0x80,
            SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
            SET_VCOM_DESEL, 0x30, # 0.83*Vcc
            # display
            SET_CONTRAST, 0xff, # maximum
            SET_ENTIRE_ON, # output follows RAM contents
            SET_NORM_INV, # not inverted
            # charge pump
            SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
            SET_DISP | 0x01): # on
            self.write_cmd(cmd)
        self.fill(0)
        self.show()

    def poweroff(self):
        self.write_cmd(SET_DISP | 0x00)

    def contrast(self, contrast):
        self.write_cmd(SET_CONTRAST)
        self.write_cmd(contrast)

    def invert(self, invert):
        self.write_cmd(SET_NORM_INV | (invert & 1))

    def show(self):
        x0 = 0
        x1 = self.width - 1
        if self.width == 64:
            # displays with width of 64 pixels are shifted by 32
            x0 += 32
            x1 += 32
        self.write_cmd(SET_COL_ADDR)
        self.write_cmd(x0)
        self.write_cmd(x1)
        self.write_cmd(SET_PAGE_ADDR)
        self.write_cmd(0)
        self.write_cmd(self.pages - 1)
        self.write_framebuf()

    def fill(self, col):
        self.framebuf.fill(col)

    def pixel(self, x, y, col):
        self.framebuf.pixel(x, y, col)

    def scroll(self, dx, dy):
        self.framebuf.scroll(dx, dy)

    def text(self, string, x, y, col=1):
        self.framebuf.text(string, x, y, col)


class SSD1306_I2C(SSD1306):
    def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
        self.i2c = i2c
        self.addr = addr
        self.temp = bytearray(2)
        # Add an extra byte to the data buffer to hold an I2C data/command byte
        # to use hardware-compatible I2C transactions.  A memoryview of the
        # buffer is used to mask this byte from the framebuffer operations
        # (without a major memory hit as memoryview doesn't copy to a separate
        # buffer).
        self.buffer = bytearray(((height // 8) * width) + 1)
        self.buffer[0] = 0x40  # Set first byte of data buffer to Co=0, D/C=1
        self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height)
        super().__init__(width, height, external_vcc)

    def write_cmd(self, cmd):
        self.temp[0] = 0x80 # Co=1, D/C#=0
        self.temp[1] = cmd
        self.i2c.writeto(self.addr, self.temp)

    def write_framebuf(self):
        # Blast out the frame buffer using a single I2C transaction to support
        # hardware I2C interfaces.
        self.i2c.writeto(self.addr, self.buffer)

    def poweron(self):
        pass


class SSD1306_SPI(SSD1306):
    def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
        self.rate = 10 * 1024 * 1024
        dc.init(dc.OUT, value=0)
        res.init(res.OUT, value=0)
        cs.init(cs.OUT, value=1)
        self.spi = spi
        self.dc = dc
        self.res = res
        self.cs = cs
        self.buffer = bytearray((height // 8) * width)
        self.framebuf = framebuf.FrameBuffer1(self.buffer, width, height)
        super().__init__(width, height, external_vcc)

    def write_cmd(self, cmd):
        self.spi.init(baudrate=self.rate, polarity=0, phase=0)
        self.cs.high()
        self.dc.low()
        self.cs.low()
        self.spi.write(bytearray([cmd]))
        self.cs.high()

    def write_framebuf(self):
        self.spi.init(baudrate=self.rate, polarity=0, phase=0)
        self.cs.high()
        self.dc.high()
        self.cs.low()
        self.spi.write(self.buffer)
        self.cs.high()

    def poweron(self):
        self.res.high()
        time.sleep_ms(1)
        self.res.low()
        time.sleep_ms(10)
        self.res.high()

View raw code

hcsr04.py

Upload the hcsr04.py to your board.

import machine, time
from machine import Pin

__version__ = '0.2.0'
__author__ = 'Roberto Sánchez'
__license__ = "Apache License 2.0. https://www.apache.org/licenses/LICENSE-2.0"

class HCSR04:
    """
    Driver to use the untrasonic sensor HC-SR04.
    The sensor range is between 2cm and 4m.
    The timeouts received listening to echo pin are converted to OSError('Out of range')
    """
    # echo_timeout_us is based in chip range limit (400cm)
    def __init__(self, trigger_pin, echo_pin, echo_timeout_us=500*2*30):
        """
        trigger_pin: Output pin to send pulses
        echo_pin: Readonly pin to measure the distance. The pin should be protected with 1k resistor
        echo_timeout_us: Timeout in microseconds to listen to echo pin. 
        By default is based in sensor limit range (4m)
        """
        self.echo_timeout_us = echo_timeout_us
        # Init trigger pin (out)
        self.trigger = Pin(trigger_pin, mode=Pin.OUT, pull=None)
        self.trigger.value(0)

        # Init echo pin (in)
        self.echo = Pin(echo_pin, mode=Pin.IN, pull=None)

    def _send_pulse_and_wait(self):
        """
        Send the pulse to trigger and listen on echo pin.
        We use the method `machine.time_pulse_us()` to get the microseconds until the echo is received.
        """
        self.trigger.value(0) # Stabilize the sensor
        time.sleep_us(5)
        self.trigger.value(1)
        # Send a 10us pulse.
        time.sleep_us(10)
        self.trigger.value(0)
        try:
            pulse_time = machine.time_pulse_us(self.echo, 1, self.echo_timeout_us)
            return pulse_time
        except OSError as ex:
            if ex.args[0] == 110: # 110 = ETIMEDOUT
                raise OSError('Out of range')
            raise ex

    def distance_mm(self):
        """
        Get the distance in milimeters without floating point operations.
        """
        pulse_time = self._send_pulse_and_wait()

        # To calculate the distance we get the pulse_time and divide it by 2 
        # (the pulse walk the distance twice) and by 29.1 becasue
        # the sound speed on air (343.2 m/s), that It's equivalent to
        # 0.34320 mm/us that is 1mm each 2.91us
        # pulse_time // 2 // 2.91 -> pulse_time // 5.82 -> pulse_time * 100 // 582 
        mm = pulse_time * 100 // 582
        return mm

    def distance_cm(self):
        """
        Get the distance in centimeters with floating point operations.
        It returns a float
        """
        pulse_time = self._send_pulse_and_wait()

        # To calculate the distance we get the pulse_time and divide it by 2 
        # (the pulse walk the distance twice) and by 29.1 becasue
        # the sound speed on air (343.2 m/s), that It's equivalent to
        # 0.034320 cm/us that is 1cm each 29.1us
        cms = (pulse_time / 2) / 29.1
        return cms

View raw code

main.py

In the main.py file is where we’ll get the distance and display it on the OLED display.

# Complete project details at https://RandomNerdTutorials.com/micropython-hc-sr04-ultrasonic-esp32-esp8266/
from machine import Pin, I2C
import ssd1306
from hcsr04 import HCSR04
from time import sleep

# ESP32 Pin assignment 
i2c = I2C(scl=Pin(22), sda=Pin(21))
sensor = HCSR04(trigger_pin=5, echo_pin=18, echo_timeout_us=10000)

# ESP8266 Pin assignment
#i2c = I2C(scl=Pin(5), sda=Pin(4))
#sensor = HCSR04(trigger_pin=12, echo_pin=14, echo_timeout_us=10000)

oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)

while True:
  oled.fill(0)
  #oled.show()
  distance = sensor.distance_mm()
  print('Distance:', distance, 'mm')
  oled.text("Distance (mm)", 0, 15)
  oled.text(str(distance), 0, 35)
  oled.show()
  sleep(1)

View raw code

The code is straightforward to understand. To learn more about using the OLED display with the ESP32 and ESP8266 boards using MicroPython, refer to the next tutorials:

The code starts by importing the required libraries.

from machine import Pin, I2C
import ssd1306
from hcsr04 import HCSR04
from time import sleep

Set the pins for the OLED display and ultrasonic sensor.

i2c = I2C(scl=Pin(5), sda=Pin(4))
sensor = HCSR04(trigger_pin=12, echo_pin=14, echo_timeout_us=10000)

Define the OLED width and height and initialize the OLED display.

oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)

On the while loop is where we’ll get the distance and display it on the OLED.

First, clear the display in each iteration with oled.fill(0).

oled.fill(0)

Get the distance in mm and save it in the distance variable.

distance = sensor.distance_mm()

Print the distance in the console.

print('Distance:', distance, 'mm')

Display the distance on the display. Notice that you need to convert the distance to a string using the str() function.

oled.text("Distance (mm)", 0, 15)
oled.text(str(distance), 0, 35)

Finally, call oled.show() to actually display the text.

oled.show()

The distance is updated every second.

sleep(1)

Demonstration

Upload all the previous files to your ESP32 or ESP8266 board in the following order:

  1. ssd1306.py
  2. hcsr04.py
  3. main.py

If you don’t know how to upload code, you can read our getting started guides with uPyCraft IDE, Thonny IDE or VS Code + PyMakr:

After uploading the code, it should start running, and it will display the distance in mm on the OLED display, as shown in the following picture.

MicroPython ESP32 ESP8266 NodeMCU Board HC-SR04 Ultrasonic Sensor Module Arduino OLED Parts Required

Wrapping Up

We hope you’ve found this tutorial useful. We have more projects and tutorials with MicroPython with other popular sensors that you may like:

You can check all our MicroPython projects here.

If you want to learn more about programming the ESP32 and ESP8266 boards with MicroPython, get access to our eBook: MicroPython Programming with ESP32 and ESP8266.

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

25 thoughts on “MicroPython: HC-SR04 Ultrasonic Sensor with ESP32 and ESP8266 (Measure distance)”

      • Hello Sara,
        thanks a lot for the tutorial, it’s exactly what I’ve been looking for. If I understand it correctly, after the flashing and uploading to the esp32 board, the main code is run on the Esp32, the results are only printed in the Arduino IDE right? So it means we can use those results printed in the IDE to perform other calculations right?

        Now the main question, please is it possible to implement the same setup as above but making the Esp32 to wirelessly send those results to the IDE?
        So that the Esp32 with the sensor can be made independent and deployed where necessary, but without need for a physical connection to a computer. Please what do you think?

        Reply
  1. Hi Sara/Rui,
    Just a curiosity question about voltages related to the sensor and esp32. The esp32 works at 3.3v in/out on it’s pins, but the sensor takes a 5v input. Easy enough to understand to that point for me. It’s also easy to understand that the trigger pulse of 3.3v from the esp is enough of a high v to make the sensor pulse. When the sensor pulses, is it driven by 5v? Or the 3.3v trigger source? And, more importantly, is the returned signal on the echo pin at 5v or 3.3v? You two are an invaluable source of great information!! Thank you.

    Reply
  2. I was surprised you connected the 5V echo output directly to the ESP32/8266 input pins, which according to the datasheet are not 5V tolerant. Are you relying on undocumented 5V tolerance
    of the inputs? There seems to be a lot of debate about 5V tolerant inputs. Maybe you have some experience? (Or do you have an HC-SR04 with 3.3V output?)

    I see someone tested input protection on an ESP32, and found that GPIO 16-19,21-23 are
    5V tolerant (6.4V measured), but this could be specific to certain versions of the chip. The
    test was in the discussion in https://esp32.com/viewtopic.php?t=877

    Reply
  3. Great work, thanks! I made a garage parking sensor using this, adding after the distance reading line in main.py:

    if distance > 300:
    state = “Far Away”
    elif 150 < distance < 299:
    state = “Closer….”
    elif 50 < distance < 149:
    state = “Perfect!”
    elif 1 < distance < 49:
    state = “* Too Close *”
    else:
    state = “…”

    Reply
  4. Hi all, I would like to know the answer to the 5V/3.3V – related questions from Bryan and Carl Hage: Is it correct to drive the sensor with 5V and connect the input and output pins of the sensor directly with an ESP32, which runs on 3.3 IO?

    Reply
    • The ESP data sheets don’t specify maximum injection current or which pins are 5V tolerant. Depending on which ESP32 any 5V tolerance could be different. But we could assume maximum injection of .5ma from other microcontroller that does have this in the data sheet. If you connect the echo output to the GPIO input via a 4.7K (or 10K) resistor, then if the GPIO is not 5V tolerant, then the resistor should limit the current from 2V overvoltage to a safe level. If you are using some other processor (e.g. ESP32C3) or other GPIO, I would insert the resistor.

      You can also use 2 resistors to make a voltage divider to reduce the 5V to 3.3, but it’s not really necessary, as a 4.7K-10K resistor will work on 5V tolerant or not. But this still violates what is in the data sheet since it doesn’t specify max injection current.

      Apparently, Rui and Sara chose a 5V tolerant GPIO18, maybe by luck, since she says it worked fine. You could replicate the test in the link above in my prior post to check for 5V tolerant pins, e.g. connect an input to a variable power supply via 10K resistor, then measuring the voltage at the input, gradually increase the power supply until the input voltage stops rising. (Fiwidev measured 6.4V on 5V tolerant pins.)

      Reply
  5. Hi thanks for a great article. It seems that the input and output for trigger and echo has been reversed in the table at the “HC-SR04 Ultrasonic Sensor Pinout” section.

    Best regards,

    Kevin

    Reply
  6. It did not work for me. I made no changes to the library or provided code. Yet I keep getting the that distance is -0.03436426 cm

    any tips on fixing it?

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.