Overview
In this project, we will build an Ultrasonic Range Finder using HC-SR04 Ultrasonic Distance Sensor and Raspberry Pi Pico & display the measured distance in an SSD1306 0.96″ I2C OLED Display.
Ultrasonic Sensor HC-SR04 is a sensor that can measure distance. It emits ultrasound at 40 000 Hz (40kHz) which travels through the air and if there is an object or obstacle in its path It will bounce back to the module. Considering the travel time and the speed of the sound you can calculate the distance.
Components Required
In this guide, I used Elecrow Raspberry Pi Pico Starter Kit to test different Modules. You can buy the kit and perform some other operations as well. From this kit, you can use the following components.
1. Raspberry Pi Pico Board – 1
2. SSD1306 OLED Display – 1
3. Ultrasonic Sensor HC-SR04 – 1
4. Breadboard – 1
5. Jumper Wires – 10
6. Micro-USB Cable – 1
Ultrasonic Sensor HC-SR04
The HC-SR04 ultrasonic sensor uses sonar to determine the distance to an object as bats do. It offers excellent non-contact range detection with high accuracy and stable readings in an easy-to-use package.
It can measure distances from 2cm to 400 cm or 1” to 13 feet. Its operation is not affected by sunlight or black material like sharp rangefinders are (although acoustically soft materials like cloth can be difficult to detect). It comes complete with an ultrasonic transmitter and a receiver module.
Specifications
The specifications of the ultrasonic distance sensor HC-SR04 are below:
- Minimum measuring range – 2 cm
- Maximum measuring range: 400 cm or 4 meters
- Accuracy : 3 mm
- Operating Voltage: +5V
- Operating Current: 15mA
- Working Frequency: 40 kHz
- Trigger Input signal: 10us pulse
- Measuring angle: 15 degrees
Pins:
- VCC: +5VDC
- Trig: Trigger (INPUT)
- Echo: Echo (OUTPUT)
- GND: GND
How does the Ultrasonic Sensor HC-SR04 work?
Ultrasonic sensors emit short, high-frequency sound pulses at regular intervals. These propagate in the air at the velocity of sound. If they strike an object, then they are reflected back as echo signals to the sensor, which itself computes the distance to the target based on the time span between emitting the signal and receiving the echo.
We will have to convert this time into cm to calculate the distance traveled. We will use the following equation to calculate the distance.
The ultrasonic wave is basically a sound wave that travels at a speed of 340 m/s (0.034 cm/s). The ultrasonic sensor is measuring the time it takes to hit the object and then come back but we need only the time that it takes to hit the object. So, we will divide it by 2.
SSD1306 OLED Display
This is a 0.96-inch blue OLED display module. The display module can be interfaced with any microcontroller using SPI/IIC protocols. It is having a resolution of 128×64. The package includes a display board, a display, and 4 pin male header pre-soldered to the board.
OLED (Organic Light-Emitting Diode) is a self-light-emitting technology composed of a thin, multi-layered organic film placed between an anode and cathode. In contrast to LCD technology, OLED does not require a backlight. OLED possesses high application potential for virtually all types of displays and is regarded as the ultimate technology for the next generation of flat-panel displays.
A detailed tutorial on OLED Display & Raspberry Pi Pico interfacing is explained in the previous article. You can go through the article in detail to learn more about the OLED Display.
Ultrasonic Range Finder using HC-SR04 & Raspberry Pi Pico
Now let us interface the Ultrasonic Sensor HC-SR04 with Raspberry Pi Pico & 0.96″ OLED Display. The connection diagram is very simple.
The SSD1306 OLED Display is an I2C Module. Therefore connect its SDA & SCL Pin to GP0 & GP1 of Raspberry Pi Pico. Similarly, connect the echo & trig pin of Ultrasonic Sensor to GP2 & GP3 of Raspberry Pi Pico. Both the OLED & HCSR-04 can be powered via Power 5V & GND pin of Raspberry Pi Pico.
MicroPython Code/Program
The code is divided into 2 parts. The 1st one is ssd1306.py and the other is main.py. The OLED Display doesn’t work directly as it requires SSD1306 Module library.
ssd1306.py
Copy the following code and save it as ssd1306.py in Raspberry Pi Pico.
|
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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces from micropython import const 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) # Subclassing FrameBuffer provides support for graphics primitives # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html class SSD1306(framebuf.FrameBuffer): def __init__(self, width, height, external_vcc): self.width = width self.height = height self.external_vcc = external_vcc self.pages = self.height // 8 self.buffer = bytearray(self.pages * self.width) super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB) 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.width > 2 * self.height 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 poweron(self): self.write_cmd(SET_DISP | 0x01) 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_data(self.buffer) 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) self.write_list = [b"\x40", None] # Co=0, D/C#=1 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_data(self, buf): self.write_list[1] = buf self.i2c.writevto(self.addr, self.write_list) 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 import time self.res(1) time.sleep_ms(1) self.res(0) time.sleep_ms(10) self.res(1) super().__init__(width, height, external_vcc) def write_cmd(self, cmd): self.spi.init(baudrate=self.rate, polarity=0, phase=0) self.cs(1) self.dc(0) self.cs(0) self.spi.write(bytearray([cmd])) self.cs(1) def write_data(self, buf): self.spi.init(baudrate=self.rate, polarity=0, phase=0) self.cs(1) self.dc(1) self.cs(0) self.spi.write(buf) self.cs(1) |
main.py
Copy the following code and save it as main.py in Raspberry Pi Pico.
|
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 |
from machine import Pin, I2C from ssd1306 import SSD1306_I2C import utime trigger = Pin(3, Pin.OUT) echo = Pin(2, Pin.IN) def ultrasonnic(): timepassed=0 trigger.low() utime.sleep_us(2) trigger.high() utime.sleep_us(10) trigger.low() while echo.value() == 0: signaloff = utime.ticks_us() while echo.value() == 1: signalon = utime.ticks_us() timepassed = signalon - signaloff return timepassed WIDTH = 128 # oled display width HEIGHT = 64 # oled display height i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=200000) # Init I2C using pins GP0 & GP1 print("I2C Address : "+hex(i2c.scan()[0]).upper()) # Display device address print("I2C Configuration: "+str(i2c)) # Display I2C config oled = SSD1306_I2C(WIDTH, HEIGHT, i2c) # Init oled display while True: oled.fill(0) measured_time = ultrasonnic() distance_cm = (measured_time * 0.0343) / 2 distance_cm = round(distance_cm,2) oled.text("Distance",20,15) oled.text(str(distance_cm)+" cm",20,35) oled.show() utime.sleep(1) |
Now click on the Run button to run all the libraries and the main files.
The OLED Display will print the distance value of the obstacle that the Ultrasonic Sensor detects. You can bring your hand near or close to the Ultrasonic Sensor to get the distance reading.












