OLED Distance Meter
Intermediate40 minA meter that shows the distance measured by an HC-SR04 on a 0.96" OLED screen, no computer needed: the distance in large text on top, and below it a bar graph of the last 128 readings scrolling to the right. Move your hand closer and further and watch the graph rise and fall. It teaches driving a screen over I2C, drawing text and lines with Adafruit GFX, keeping readings in a ring buffer, and timing with millis() instead of delay. Install the "Adafruit SSD1306" and "Adafruit GFX" libraries from the Library Manager.
Build it step by step in the appWhat you need
1 × Arduino Uno R3
1 × Breadboard 830 Points (MB-102)
1 × HC-SR04 Ultrasonic Distance Sensor
1 × 0.96" OLED Display (SSD1306, I2C)
6 × Jumper Wire - Male/Male
4 × Jumper Wire - Male/Female
Wiring steps
- 1
Connect the power rails (5V and GND)

Plug a red jumper wire into the 5V pin on the Arduino's power header and connect its other end to the breadboard's bottom red (+) rail. Plug a black wire into one of the GND pins on the same header and connect its other end to the bottom blue (−) rail. The Uno has a single 5V pin; these two rails share 5V and GND between the sensor and the screen.
- Arduino 5V → the bottom red (+) rail (red wire)
- Arduino GND → the bottom blue (−) rail (black wire)
- 2
Add and wire the HC-SR04

Plug the HC-SR04 upright into the top half of the breadboard, its "eyes" facing the way it will measure; its four legs go, from left to right, VCC in column 7, TRIG in 8, ECHO in 9 and GND in 10. Connect column 7 (VCC) to the bottom (+) rail with a red wire and column 10 (GND) to the bottom (−) rail with a black wire. Connect the Arduino's pin 7 to column 8 with a blue wire (TRIG_PIN 7) and its pin 8 to column 9 with a yellow wire (ECHO_PIN 8). The pin numbers and the column numbers differ; go by the columns.
- HC-SR04 VCC → column 7, top half
- HC-SR04 TRIG → column 8, top half
- HC-SR04 ECHO → column 9, top half
- HC-SR04 GND → column 10, top half
- column 7, top half → the bottom red (+) rail (red wire)
- column 10, top half → the bottom blue (−) rail (black wire)
- Arduino pin 7 → column 8, top half (blue wire)
- Arduino pin 8 → column 9, top half (yellow wire)
- 3
Connect the OLED

Do not plug the screen into the breadboard; push the female ends of male-female jumper wires onto its four pins. Connect VCC to the bottom (+) rail and GND to the bottom (−) rail. Connect SDA to the Arduino's A4 pin with a brown wire and SCL to A5 with a green wire. The pin order varies from screen to screen (on some VCC comes first): read the labels on the board before wiring. These screens also run on 5V; thanks to I2C all 128x64 pixels are driven over these two data wires.
- OLED screen GND → the bottom blue (−) rail (black wire)
- OLED screen VCC → the bottom red (+) rail (red wire)
- Arduino A4 → OLED screen SDA (brown wire)
- Arduino A5 → OLED screen SCL (green wire)
Arduino sketch
// Install from the Library Manager:
// "Adafruit SSD1306" and "Adafruit GFX"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// HC-SR04 pins
const int TRIG_PIN = 7;
const int ECHO_PIN = 8;
// Graph ceiling (cm): further sits on top
const int MAX_CM = 100;
// Graph area: the screen's bottom 40 rows
const int GRAPH_TOP = 24;
const int GRAPH_H = 40;
// Time between readings (ms)
const unsigned long MEASURE_EVERY = 100;
// 128x64 screen; I2C address mostly 0x3C
Adafruit_SSD1306 oled(128, 64, &Wire, -1);
// The last 128 readings, one per column
byte history[128];
int newest = 0;
unsigned long lastMeasure = 0;
void setup() {
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.begin(9600);
if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED not found");
// No screen: stop here
while (true) {}
}
oled.setTextColor(SSD1306_WHITE);
}
// Distance in cm; -1 when no echo comes
int measureCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long echo = pulseIn(ECHO_PIN, HIGH, 30000);
if (echo == 0) {
return -1;
}
return echo / 58;
}
// Distance on top, recent readings below
void draw(int cm) {
oled.clearDisplay();
oled.setTextSize(2);
oled.setCursor(0, 0);
if (cm < 0) {
oled.print("-- cm");
} else {
oled.print(cm);
oled.print(" cm");
}
// Oldest reading left, newest right
for (int x = 0; x < 128; x++) {
int h = history[(newest + 1 + x) % 128];
int bottom = GRAPH_TOP + GRAPH_H - 1;
oled.drawFastVLine(x, bottom - h, h + 1, SSD1306_WHITE);
}
oled.display();
}
void loop() {
if (millis() - lastMeasure < MEASURE_EVERY) {
return;
}
lastMeasure = millis();
int cm = measureCm();
// Unmeasured or too far: right on top
int shown = (cm < 0 || cm > MAX_CM) ? MAX_CM : cm;
newest = (newest + 1) % 128;
history[newest] = map(shown, 0, MAX_CM, 0, GRAPH_H - 1);
draw(cm);
if (cm < 0) {
Serial.println("Distance: no reading");
} else {
Serial.print("Distance: ");
Serial.print(cm);
Serial.println(" cm");
}
}
What the Serial Monitor shows
Distance: 87 cm Distance: 64 cm Distance: 31 cm Distance: 12 cm Distance: no reading
If a wire is loose
OLED not found
What you will see
The top of the screen shows the distance in large text (for example 25 cm), and below it a bar graph moves one step to the right every tenth of a second: the taller the bar, the further away the obstacle. Bring your hand closer and the bars shrink, move it away and they grow; readings over a metre or with no echo sit on top. If the screen stays dark, check the Serial Monitor: if it says "OLED not found", check that SDA is on A4, SCL on A5, and VCC/GND; your screen's address may be 0x3D, so change 0x3C in the code to 0x3D. If the distance always reads "--", check the TRIG and ECHO wires.
In the app, tick off every wire, match the parts to your inventory and pick up where you left off.
Build it step by step in the app