A beginner's Arduino UNO kit about measuring distance with sound: Start here, then 9 lessons - read the HC-SR04, light green, yellow and red LEDs by distance, add a parking beeper, explore the sensor's limits, calibrate it, smooth the readings and show them on an OLED - ending in a Distance Lab that uses every part. Each lesson has a wiring diagram, code and troubleshooting. Wiring design-reviewed and code compiler-checked; the complete physical assembly is not bench-tested. A learning model only - not a vehicle safety device.
1×UNO R3 Development Board ATmega328P Microcontroller with USB Cable
1×HC-SR04 Ultrasonic Distance Sensor Module for Arduino
1×Active Buzzer Module 5V DC
1×RESISTOR ASSORTED 220-1K-10K
1×400 Tie Point Solderless Breadboard
1×Jumper Wire Assorted Pack 120 Pieces
1×0.96" OLED Display Module 128x64 I2C (White) (optional)
1×9V Battery Holder Case with 5.5x2.1mm DC Male Plug (optional)| Item | For | Note |
|---|---|---|
| A computer (Windows, macOS or Linux) where you can install the free Arduino IDE 2 - or a Chromebook with the Arduino Cloud Editor | All lessons | |
| A free USB-A port, or an adapter, for the kit's USB cable | All lessons | |
| An internet connection, to download the IDE, the libraries and the code | Start here, Lesson 8 | |
| A ruler or tape measure, and a book or flat piece of card | Lessons 1-9 | |
| A cushion or jumper, a bottle and a pencil, to test targets | Lessons 4 and 6 | |
| A spreadsheet program (Google Sheets, Excel, LibreOffice) | Lesson 5 - optional | |
| A 9V battery | Lesson 9 battery demo - optional | Not included. The holder is in the box. |
| A 4-pin header and soldering | Lesson 8 - only if your OLED has no pins fitted | Adult only |
| A multimeter | Optional | No lesson needs one. Useful for an adult checking 5V and GND. |
| # | Step and what you should see | Done | Date |
|---|---|---|---|
| 1 | Start here: install the Arduino IDE, select Arduino Uno and your port, and upload Blink - the L light blinks. | ||
| 2 | Lesson 1 - Measure Distance with Sound (30-45 min): the distance to a book in the Serial Monitor, checked against a ruler. | ||
| 3 | Lesson 2 - Traffic-Light Distance Indicator (30 min): green 50 cm+, yellow 20-49 cm, red under 20 cm; yellow blinks on no echo. | ||
| 4 | Lesson 3 - Parking Beeper (30 min): slow beeps under 20 cm, fast beeps under 10 cm. | ||
| 5 | Lesson 4 - Blind Zone & No Echo (25 min): counts of good readings, "too close" and "no echo". | ||
| 6 | Lesson 5 - Calibrate Your Sensor (40 min): type the ruler distance and get the average, error and spread. | ||
| 7 | Lesson 6 - Angles & Surfaces (40 min): how many of 20 pings come back from flat, soft, round and tilted targets. | ||
| 8 | Lesson 7 - Smoothing Noisy Readings (30 min): raw, average and median on the Serial Plotter. | ||
| 9 | Lesson 8 - OLED Distance Meter (45 min): install the Adafruit SSD1306 library; big numbers and a bar on the OLED. | ||
| 10 | Lesson 9 - Everything Together (45-60 min): LEDs, beeper, OLED and commands in one sketch; optional 9V battery demo. |
Every lesson uses this one pin plan, so parts wired in one lesson stay where they are for the next. A dot means the lesson uses that pin. Lesson 9 uses everything at once.
| UNO pin | Connects to | S | L1 | L2 | L3 | L4 | L5 | L6 | L7 | L8 | L9 | Note |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5V | + rail of the breadboard | ● | ● | ● | ● | ● | ● | ● | ● | ● | Powers the sensor, buzzer and OLED. | |
| GND | − rail of the breadboard | ● | ● | ● | ● | ● | ● | ● | ● | ● | Shared ground. Every part joins here. | |
| D0, D1 | Nothing - leave free | The USB connection uses these two pins. Wires here can stop uploads. | ||||||||||
| D5 ~ | Red LED, through 220 Ω | ● | ● | ● | ● | |||||||
| D6 ~ | Yellow LED, through 220 Ω | ● | ● | ● | ● | Blinks when there is no echo. | ||||||
| D7 | Green LED, through 220 Ω | ● | ● | ● | ● | |||||||
| D8 | Buzzer I/O (signal) | ● | ● | ● | Lesson 4 keeps it silent. | |||||||
| D9 ~ | HC-SR04 TRIG | ● | ● | ● | ● | ● | ● | ● | ● | ● | ||
| D10 ~ | HC-SR04 ECHO | ● | ● | ● | ● | ● | ● | ● | ● | ● | ||
| D13 | Built-in L light only | ● | Blink in Start here. Nothing is wired to it. | |||||||||
| A4 | OLED SDA (I2C data) | ● | ● | Also called SDA on the header by AREF - the same pin. | ||||||||
| A5 | OLED SCL (I2C clock) | ● | ● | Also called SCL on the header by AREF - the same pin. | ||||||||
| D2-D4, D11, D12, A0-A3 | Nothing - free | Spare pins for your own ideas. |
S = Start here · L1-L9 = Lessons. ~ marks a pin that can dim an LED or drive a servo (PWM). The same pins are in every lesson's diagram and code.
Follow these 7 steps before Lesson 1. They take about 30-45 minutes and prove that your computer, cable and UNO all work - so if something goes wrong later, you know it is the wiring, not the setup.
Lessons 1-7 need only the kit, a computer and things from home such as a book and a ruler. Lesson 8 adds the OLED and needs two free libraries (and an adult only if the OLED has no pins). Lesson 9's battery demo needs a 9V battery, which is not in the box.
| Something is missing or broken | Send us a photo with your order number on WhatsApp. |
| The download or install is blocked | School and work computers often block installs. Ask an adult or use a home computer. |
| No light at all | Push the plug fully into the UNO. Try another USB port, then another cable. |
| The Port menu is greyed out or empty | Some USB cables only charge and carry no data. Try the kit's cable in another port, or another data cable. |
| Windows shows "USB2.0-Serial" or an unknown device | Look at the small chip next to the UNO's USB socket. If it says CH340, the computer needs the free CH340 driver - ask us on WhatsApp and we'll send the link. |
| Mac: no new port appears | Do the same CH340 check. After installing a driver, you may need to allow it in System Settings → Privacy & Security. |
| Linux: "permission denied" on the port | Ask an adult to add your user to the dialout group (sudo usermod -a -G dialout $USER), then log out and back in. |
| "can't open device" or "Access is denied" | The port is busy or wrong. Close other Arduino windows, choose the port again (step 5) and try again. |
| "programmer is not responding" | The wrong board or port is selected, or the cable carries no data. Check step 5 and try another cable. |
| A red "Compilation error" | The example was changed by accident. Close it without saving and open Blink again from File → Examples. |
| "Done uploading" but L doesn't change | Your board may already have been running Blink. Do the change below (delay 200) and look for faster blinking. |
| UNO R3 + USB cable | 1 |
| 400-point breadboard | 1 |
| HC-SR04 distance sensor | 1 |
| Male-to-male jumper wires | 6 |
| A ruler or tape measure, and a book or flat card (yours) | 1 |
One "eye" of the sensor sends a short click of ultrasound; the other listens for the echo. The UNO starts it with a 10-microsecond pulse on TRIG.
The sensor then holds ECHO high for as long as the sound took to get there and back. pulseIn() measures that time in microseconds.
Sound needs about 58 microseconds for every centimetre of distance, there and back, so time ÷ 58 = centimetres.
Each line on the screen is the average of 5 pings. One ping can be off; the average of five is steadier. The second number says how many of the 5 echoes came back.
| Part / pin | Connect to | Note |
|---|---|---|
| UNO 5V | + rail (red line) | Powers the sensor |
| UNO GND | − rail (blue line) | Shared ground for everything |
| HC-SR04 VCC | + rail (5V) | Go by the labels printed on the sensor |
| HC-SR04 TRIG | UNO pin 9 | |
| HC-SR04 ECHO | UNO pin 10 | |
| HC-SR04 GND | − rail (GND) |
Easiest: scan the QR code above and press Copy or Download .ino on the lesson page, then paste it into the Arduino IDE and click Upload. The full listing is here for reading along.
/*
DIY KIT - Arduino Ultrasonic Distance & Parking Learning Kit
Lesson 1 of 9: Measure Distance with Sound
WIRING (unplug the USB cable before you wire)
HC-SR04 VCC -> 5V (+ rail)
HC-SR04 TRIG -> UNO pin 9
HC-SR04 ECHO -> UNO pin 10
HC-SR04 GND -> GND (- rail)
WHAT SHOULD HAPPEN
Open Tools > Serial Monitor and set it to 9600 baud. Twice a second a
new line shows the distance to whatever is in front of the sensor, and
how many of the 5 pings came back. Hold a book in front of the sensor
and compare the number with a ruler.
*/
const int TRIG_PIN = 9; // we send the "click" from here
const int ECHO_PIN = 10; // the sensor answers on this pin
const int SAMPLES = 5; // pings per line - try 1 and see the numbers jump
const unsigned long ECHO_TIMEOUT_US = 30000; // give up after 30 ms (about 5 m there and back)
const int MIN_CM = 2; // closer than this the sensor cannot measure
const int MAX_CM = 400; // further than this is out of range
int lineNumber = 0;
// One measurement in centimetres, or 0 if no echo came back.
long readOnceCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH); // a 10-microsecond pulse starts one measurement
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// pulseIn() times how long ECHO stays HIGH: the sound's trip there and back.
unsigned long us = pulseIn(ECHO_PIN, HIGH, ECHO_TIMEOUT_US);
if (us == 0) return 0; // no echo
long cm = us / 58; // sound needs about 58 microseconds per cm, there and back
if (cm < MIN_CM || cm > MAX_CM) return 0;
return cm;
}
void setup() {
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.begin(9600);
Serial.println("Lesson 1: Measure Distance with Sound");
Serial.println("line, distance_cm, echoes (out of 5)");
}
void loop() {
long total = 0;
int echoes = 0;
for (int i = 0; i < SAMPLES; i++) {
long cm = readOnceCm();
if (cm > 0) { // only count pings that came back
total = total + cm;
echoes = echoes + 1;
}
delay(60); // let the last echo die away before the next ping
}
lineNumber = lineNumber + 1;
Serial.print(lineNumber);
Serial.print(", ");
if (echoes > 0) {
long average = total / echoes; // the average of the pings that came back (whole cm)
Serial.print(average);
} else {
Serial.print("no echo");
}
Serial.print(", ");
Serial.println(echoes);
delay(200);
}
| const int TRIG_PIN = 9; | Names for the two sensor pins. If you ever move a wire, change only this number. |
| readOnceCm() | Our own function: one ping. It sends the 10 µs pulse, times the echo with pulseIn(), divides by 58 and returns centimetres - or 0 if no echo came back. |
| ECHO_TIMEOUT_US = 30000 | pulseIn() gives up after 30 ms. Without it the program could wait a long time when nothing is in front. |
| for (int i = 0; i < SAMPLES; i++) | Repeats the ping 5 times, adding up the ones that came back. Then it prints total ÷ echoes: the average. |
| It always says no echo | Unplug USB. Check VCC to the + rail, GND to the − rail, TRIG to pin 9 and ECHO to pin 10 - by the labels on the sensor, not their position. Point it at a big flat object 20-50 cm away. |
| The number is 0 or "no echo" even close up | Nothing may be in the beam: hold the book square to the sensor, not tilted. Check ECHO is on pin 10 and TRIG on pin 9, not swapped. |
| The distance is always a few cm too big | Measure from the front of the sensor's eyes, not the back of the board. Small errors are normal - Lesson 5 measures them. |
| Nothing or strange symbols in the Serial Monitor | Set the speed in the corner of the Serial Monitor to 9600 baud. Close the Serial Plotter if it is open. |
| The upload fails | See Start here, step 6: "If the upload fails". |
Measure the same book at 30 cm five times, moving it away and back each time. How far apart are your five numbers?
| Wiring from Lesson 1 | |
| Red, yellow and green LED | 1 each |
| 220 Ω resistors (red-red-brown) | 3 |
| Male-to-male jumper wires | about 6 |
Each LED has its own pin: red on 5, yellow on 6, green on 7. HIGH turns an LED on and LOW turns it off.
Each LED has its own 220 Ω resistor. Without one, the LED takes too much current and can damage itself or the UNO pin. Sharing one resistor between LEDs makes them glow unevenly.
loop() measures once, then picks one zone: under 20 cm red, under 50 cm yellow, otherwise green. Only one LED is on at a time.
If no echo comes back, the sketch does not know the distance. It blinks yellow instead of showing green: unknown must never look like safe.
| Part / pin | Connect to | Note |
|---|---|---|
| Red LED long leg (+) | 220 Ω → UNO pin 5 | One resistor for each LED |
| Yellow LED long leg (+) | 220 Ω → UNO pin 6 | |
| Green LED long leg (+) | 220 Ω → UNO pin 7 | |
| All three short legs (−) | − rail (GND) | |
| HC-SR04 | As in Lesson 1 | TRIG pin 9, ECHO pin 10 |
Easiest: scan the QR code above and press Copy or Download .ino on the lesson page, then paste it into the Arduino IDE and click Upload. The full listing is here for reading along.
/*
DIY KIT - Arduino Ultrasonic Distance & Parking Learning Kit
Lesson 2 of 9: Traffic-Light Distance Indicator
WIRING (unplug the USB cable before you wire)
HC-SR04 as in Lesson 1: VCC 5V, TRIG pin 9, ECHO pin 10, GND.
Red LED long leg -> 220 ohm resistor -> UNO pin 5
Yellow LED long leg -> 220 ohm resistor -> UNO pin 6
Green LED long leg -> 220 ohm resistor -> UNO pin 7
All three short legs -> GND (- rail)
WHAT SHOULD HAPPEN
Green = 50 cm or more (CLEAR)
Yellow = 20 to 49 cm (CAUTION)
Red = under 20 cm (NEAR)
Yellow blinking = no echo: the sensor does not know. Unknown is never shown as green.
The Serial Monitor (9600 baud) prints the zone and the distance.
*/
const int RED_LED = 5;
const int YELLOW_LED = 6;
const int GREEN_LED = 7;
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
const int CLEAR_CM = 50; // this far or more: green
const int NEAR_CM = 20; // closer than this: red
const unsigned long ECHO_TIMEOUT_US = 30000;
const int MIN_CM = 2;
const int MAX_CM = 400;
long readOnceCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long us = pulseIn(ECHO_PIN, HIGH, ECHO_TIMEOUT_US);
if (us == 0) return 0; // no echo
long cm = us / 58;
if (cm < MIN_CM || cm > MAX_CM) return 0;
return cm;
}
// Turn each LED on (true) or off (false).
void showLights(bool red, bool yellow, bool green) {
digitalWrite(RED_LED, red ? HIGH : LOW);
digitalWrite(YELLOW_LED, yellow ? HIGH : LOW);
digitalWrite(GREEN_LED, green ? HIGH : LOW);
}
void setup() {
pinMode(RED_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.begin(9600);
Serial.println("Lesson 2: Traffic-Light Distance Indicator");
// Light test: red, yellow, green, one after another.
showLights(true, false, false); delay(300);
showLights(false, true, false); delay(300);
showLights(false, false, true); delay(300);
showLights(false, false, false);
}
void loop() {
long cm = readOnceCm();
if (cm == 0) {
// No echo: blink yellow - on for half a second, off for half a second.
unsigned long halfSeconds = millis() / 500; // half-seconds since the UNO started
bool blinkOn = (halfSeconds % 2 == 0); // even: on, odd: off
showLights(false, blinkOn, false);
Serial.println("NO_ECHO");
} else if (cm < NEAR_CM) {
showLights(true, false, false);
Serial.print("NEAR ");
Serial.println(cm);
} else if (cm < CLEAR_CM) {
showLights(false, true, false);
Serial.print("CAUTION ");
Serial.println(cm);
} else {
showLights(false, false, true);
Serial.print("CLEAR ");
Serial.println(cm);
}
delay(100);
}
| const int CLEAR_CM = 50; const int NEAR_CM = 20; | The zone edges. Change them here and the whole sketch follows. |
| showLights(red, yellow, green) | Our own function: true switches an LED on, false switches it off. |
| if (cm == 0) { ... } else if (cm < NEAR_CM) ... | The decision: no echo first, then red, then yellow, otherwise green. |
| halfSeconds % 2 == 0 | Counts half-seconds since start. Even numbers: yellow on, odd: off. That makes the blink. |
| One LED never lights, not even in the light test | Unplug USB and turn that LED round (long leg towards the resistor). Check its wire goes to the right pin. |
| The colours light in the wrong zones | Two pin wires are swapped. Red must be on 5, yellow on 6, green on 7. |
| The LEDs light in the test but never change | The sensor wiring moved. Check it against Lesson 1. |
| An LED is very dim | Check the resistor bands: red-red-brown is 220 Ω. Brown-black-orange is 10 kΩ, far too big. |
| It always says no echo | Unplug USB. Check VCC to the + rail, GND to the − rail, TRIG to pin 9 and ECHO to pin 10 - by the labels on the sensor, not their position. Point it at a big flat object 20-50 cm away. |
| The upload fails | See Start here, step 6: "If the upload fails". |
Find the exact distance where yellow changes to red. Move the book 1 cm at a time. Is it always at the same place, or does it flicker at the edge?
| Wiring from Lessons 1-2 | |
| Active buzzer module | 1 |
| Male-to-female jumper wires | 3 |
The zones are the same as Lesson 2. Under 20 cm red now beeps slowly (120 ms on, 380 ms off); under 10 cm it beeps fast (100 ms on, 100 ms off).
beep() never waits. Each time round loop() it checks millis(): if it is time to switch, it flips the buzzer and works out when the next switch is due. So the sensor keeps measuring while it beeps.
With no echo, yellow blinks and the buzzer stays silent: a beep would claim the sketch knows the distance when it does not.
Real parking sensors are tested and certified. This one is a desk model for learning - never use it on a vehicle.
| Part / pin | Connect to | Note |
|---|---|---|
| Buzzer I/O (signal) | UNO pin 8 | May be labelled S or IN |
| Buzzer VCC (+) | + rail (5V) | |
| Buzzer GND (−) | − rail (GND) | |
| LEDs and HC-SR04 | As in Lessons 1-2 | Pins 5, 6, 7, 9, 10 |
Easiest: scan the QR code above and press Copy or Download .ino on the lesson page, then paste it into the Arduino IDE and click Upload. The full listing is here for reading along.
/*
DIY KIT - Arduino Ultrasonic Distance & Parking Learning Kit
Lesson 3 of 9: Parking Beeper
WIRING (unplug the USB cable before you wire)
Keep the sensor and LEDs from Lessons 1-2.
Active buzzer module, with 3 male-to-female jumper wires:
I/O (signal, may be labelled S or IN) -> UNO pin 8
VCC (+) -> 5V (+ rail)
GND (-) -> GND (- rail)
WHAT SHOULD HAPPEN
Green 50 cm or more silent
Yellow 20 to 49 cm silent
Red 10 to 19 cm slow beeps
Red under 10 cm fast beeps
No echo: yellow blinks and the buzzer stays silent.
A tabletop model of a parking sensor - never use it on a real vehicle.
*/
const int RED_LED = 5;
const int YELLOW_LED = 6;
const int GREEN_LED = 7;
const int BUZZER_PIN = 8;
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
const int CLEAR_CM = 50; // this far or more: green
const int NEAR_CM = 20; // closer than this: red + slow beeps
const int CRITICAL_CM = 10; // closer than this: red + fast beeps
const unsigned long SLOW_ON_MS = 120, SLOW_OFF_MS = 380; // slow beep
const unsigned long FAST_ON_MS = 100, FAST_OFF_MS = 100; // fast beep
// Most modules beep when the pin is HIGH. If yours beeps in the gaps or
// all the time, change false to true and upload again.
const bool BUZZER_ACTIVE_LOW = false;
const unsigned long ECHO_TIMEOUT_US = 30000;
const int MIN_CM = 2;
const int MAX_CM = 400;
bool buzzerOn = false;
unsigned long nextBeepChange = 0;
long readOnceCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long us = pulseIn(ECHO_PIN, HIGH, ECHO_TIMEOUT_US);
if (us == 0) return 0;
long cm = us / 58;
if (cm < MIN_CM || cm > MAX_CM) return 0;
return cm;
}
void showLights(bool red, bool yellow, bool green) {
digitalWrite(RED_LED, red ? HIGH : LOW);
digitalWrite(YELLOW_LED, yellow ? HIGH : LOW);
digitalWrite(GREEN_LED, green ? HIGH : LOW);
}
void buzzer(bool on) {
buzzerOn = on;
if (BUZZER_ACTIVE_LOW) digitalWrite(BUZZER_PIN, on ? LOW : HIGH);
else digitalWrite(BUZZER_PIN, on ? HIGH : LOW);
}
// Beep with the given on and off times without stopping the program:
// millis() tells us when it is time to switch.
void beep(unsigned long onMs, unsigned long offMs) {
unsigned long now = millis();
if (now >= nextBeepChange) {
buzzer(!buzzerOn);
nextBeepChange = now + (buzzerOn ? onMs : offMs);
}
}
void setup() {
pinMode(RED_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
buzzer(false);
Serial.begin(9600);
Serial.println("Lesson 3: Parking Beeper - learning model only");
Serial.println("cm, zone, buzzer");
}
void loop() {
long cm = readOnceCm();
const char *zone;
if (cm == 0) {
zone = "NO_ECHO";
unsigned long halfSeconds = millis() / 500; // blink yellow, as in Lesson 2
showLights(false, halfSeconds % 2 == 0, false);
buzzer(false); // unknown: never beep "all clear" or "danger"
} else if (cm < CRITICAL_CM) {
zone = "CRITICAL";
showLights(true, false, false);
beep(FAST_ON_MS, FAST_OFF_MS);
} else if (cm < NEAR_CM) {
zone = "NEAR";
showLights(true, false, false);
beep(SLOW_ON_MS, SLOW_OFF_MS);
} else if (cm < CLEAR_CM) {
zone = "CAUTION";
showLights(false, true, false);
buzzer(false);
} else {
zone = "CLEAR";
showLights(false, false, true);
buzzer(false);
}
Serial.print(cm);
Serial.print(", ");
Serial.print(zone);
Serial.print(", ");
Serial.println(buzzerOn ? 1 : 0);
delay(40);
}
| const int CRITICAL_CM = 10; | The new, closest zone: fast beeps. |
| const bool BUZZER_ACTIVE_LOW = false; | Most modules beep when the pin is HIGH. If yours beeps in the gaps or all the time, change it to true. |
| beep(onMs, offMs) | Our own function. It uses millis() to flip the buzzer at the right moments without stopping the program. |
| buzzer(false) in the NO_ECHO branch | Silence when the distance is unknown. |
| No sound at all | Unplug USB. Check I/O to pin 8, VCC to the + rail, GND to the − rail, by the labels on the module. |
| It beeps in the gaps, or all the time | Your module beeps when the pin is LOW. Set BUZZER_ACTIVE_LOW to true and upload again. |
| Only a click, no beep | It may be a passive buzzer, which needs tone(). Check the label says active, or ask us. |
| Beeps but the LEDs are wrong | Check the LEDs against Lesson 2: red 5, yellow 6, green 7. |
| It always says no echo | Unplug USB. Check VCC to the + rail, GND to the − rail, TRIG to pin 9 and ECHO to pin 10 - by the labels on the sensor, not their position. Point it at a big flat object 20-50 cm away. |
| The upload fails | See Start here, step 6: "If the upload fails". |
Hold the book at exactly 10 cm. Does the beep keep switching between slow and fast? Why might that happen at a zone edge?
| Wiring from Lesson 3 - nothing new | |
| A book, a cushion or jumper, and a pencil (yours) |
Very close up, the echo comes back before the sensor has finished sending, so the reading is wrong or missing. That is the blind zone.
No echo can mean the object is too far, too soft (fabric absorbs sound), too small, or tilted so the echo flies off to the side. It does NOT mean nothing is there.
In this lesson only, the LEDs mean something different: green = a good reading, yellow = too close (under 3 cm), red = no echo.
Three variables count each kind of reading. Type r and press Enter to set them back to 0 before each test.
| Part / pin | Connect to | Note |
|---|---|---|
| No changes | Same as Lesson 3 | LEDs 5, 6, 7 · buzzer 8 (kept silent) · TRIG 9 · ECHO 10 |
Easiest: scan the QR code above and press Copy or Download .ino on the lesson page, then paste it into the Arduino IDE and click Upload. The full listing is here for reading along.
/*
DIY KIT - Arduino Ultrasonic Distance & Parking Learning Kit
Lesson 4 of 9: Blind Zone & No Echo
WIRING: the sensor and LEDs from Lessons 1-3 (pins 5, 6, 7, 9, 10).
The buzzer can stay connected - this sketch keeps it silent.
IN THIS LESSON THE LEDs MEAN SOMETHING DIFFERENT:
Green = a good reading
Yellow = too close (under 3 cm) - the sensor's "blind zone"
Red = no echo came back (too far, too soft, or angled away)
The Serial Monitor (9600 baud) counts each kind of reading.
Type r and press Enter to set the counts back to 0.
*/
const int RED_LED = 5;
const int YELLOW_LED = 6;
const int GREEN_LED = 7;
const int BUZZER_PIN = 8;
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
const int BLIND_CM = 3; // closer than this is the blind zone
const unsigned long ECHO_TIMEOUT_US = 30000; // after 30 ms we call it "no echo"
const int MAX_CM = 400;
int goodCount = 0;
int blindCount = 0;
int noEchoCount = 0;
// Distance in cm, or 0 if no echo came back in time.
long readRawCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long us = pulseIn(ECHO_PIN, HIGH, ECHO_TIMEOUT_US);
if (us == 0) return 0;
return us / 58;
}
void setup() {
pinMode(RED_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW); // keep the buzzer quiet in this lesson
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.begin(9600);
Serial.println("Lesson 4: Blind Zone & No Echo");
}
void loop() {
// Type r + Enter to reset the counts.
while (Serial.available() > 0) {
char c = Serial.read();
if (c == 'r' || c == 'R') {
goodCount = 0;
blindCount = 0;
noEchoCount = 0;
Serial.println("Counts reset");
}
}
long cm = readRawCm();
bool noEcho = (cm == 0 || cm > MAX_CM);
bool blind = (!noEcho && cm < BLIND_CM);
bool good = (!noEcho && !blind);
digitalWrite(RED_LED, noEcho ? HIGH : LOW);
digitalWrite(YELLOW_LED, blind ? HIGH : LOW);
digitalWrite(GREEN_LED, good ? HIGH : LOW);
if (noEcho) noEchoCount++;
else if (blind) blindCount++;
else goodCount++;
Serial.print("cm=");
if (noEcho) Serial.print("none");
else Serial.print(cm);
Serial.print(" good=");
Serial.print(goodCount);
Serial.print(" too_close=");
Serial.print(blindCount);
Serial.print(" no_echo=");
Serial.println(noEchoCount);
delay(200);
}
| const int BLIND_CM = 3; | Readings closer than 3 cm count as "too close". |
| int goodCount = 0; ... | Three counters. ++ adds one each time. |
| while (Serial.available() > 0) | Reads anything you typed. If it is r, the counts go back to 0. |
| digitalWrite(BUZZER_PIN, LOW); | Keeps the buzzer from Lesson 3 quiet in this lesson. |
| Always red | Unplug USB. Check VCC to the + rail, GND to the − rail, TRIG to pin 9 and ECHO to pin 10 - by the labels on the sensor, not their position. Point it at a big flat object 20-50 cm away. |
| Typing r does nothing | Click in the box at the top of the Serial Monitor, type r, then press Enter. Check the speed is 9600 baud. |
| Yellow never lights | Many sensors give no echo rather than a small number when very close, so red is also a correct result. |
| The buzzer sounds | Upload the Lesson 4 code again - it keeps pin 8 LOW. |
Make a table: book, cushion, pencil, tilted book, empty room. For each, write good / too close / no echo after 10 seconds. Which target is hardest for the sensor?
| Wiring from Lesson 1 (the other parts can stay) | |
| Ruler or tape measure, flat card (yours) | 1 |
| Masking tape to mark distances (optional) |
You type the true distance from the ruler and press Enter. The sketch reads your digits one by one and builds the number: 2 then 0 makes 20.
It then takes 10 pings and prints one row: ruler_cm, average_cm, error_cm, min_cm, max_cm, spread_cm, echoes.
Error tells you whether the sensor reads too far or too near. Spread tells you how much it wobbles. A good sensor has both small.
Real sensors are often a little off in the same direction. Once you know the error, you could correct for it in code.
| Part / pin | Connect to | Note |
|---|---|---|
| No changes | Sensor as in Lesson 1 | TRIG pin 9 · ECHO pin 10 |
Easiest: scan the QR code above and press Copy or Download .ino on the lesson page, then paste it into the Arduino IDE and click Upload. The full listing is here for reading along.
/*
DIY KIT - Arduino Ultrasonic Distance & Parking Learning Kit
Lesson 5 of 9: Calibrate Your Sensor
WIRING: the sensor from Lesson 1 (TRIG pin 9, ECHO pin 10).
The LEDs and buzzer can stay connected; this sketch does not use them.
HOW TO USE IT
1. Open the Serial Monitor at 9600 baud. Check the box at the bottom
right is set to "New Line".
2. Put a flat card at a ruler mark, for example 20 cm.
3. Type 20 and press Enter. The UNO takes 10 readings and prints one row:
ruler_cm, average_cm, error_cm, min_cm, max_cm, spread_cm, echoes
4. Repeat at 10, 20, 30, 50, 75 and 100 cm. Copy the rows into a spreadsheet.
*/
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
const int SAMPLES = 10; // readings per row
const unsigned long ECHO_TIMEOUT_US = 30000;
const int MIN_CM = 2;
const int MAX_CM = 400;
long typedNumber = 0; // the number being typed, digit by digit
long readOnceCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long us = pulseIn(ECHO_PIN, HIGH, ECHO_TIMEOUT_US);
if (us == 0) return 0;
long cm = us / 58;
if (cm < MIN_CM || cm > MAX_CM) return 0;
return cm;
}
// Take SAMPLES readings and print one spreadsheet row.
void measureAt(long rulerCm) {
long total = 0;
long lowest = 9999;
long highest = 0;
int echoes = 0;
for (int i = 0; i < SAMPLES; i++) {
long cm = readOnceCm();
if (cm > 0) {
total = total + cm;
echoes = echoes + 1;
if (cm < lowest) lowest = cm;
if (cm > highest) highest = cm;
}
delay(60);
}
Serial.print(rulerCm);
Serial.print(",");
if (echoes == 0) {
Serial.println("NA,NA,NA,NA,NA,0");
return;
}
long average = total / echoes;
Serial.print(average); Serial.print(",");
Serial.print(average - rulerCm); Serial.print(","); // error: + means it reads too far
Serial.print(lowest); Serial.print(",");
Serial.print(highest); Serial.print(",");
Serial.print(highest - lowest); Serial.print(","); // spread: how much it wobbles
Serial.println(echoes);
}
void setup() {
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.begin(9600);
Serial.println("Lesson 5: Calibrate Your Sensor");
Serial.println("Put the card at a ruler mark, type the distance in cm and press Enter.");
Serial.println("ruler_cm,average_cm,error_cm,min_cm,max_cm,spread_cm,echoes");
}
void loop() {
while (Serial.available() > 0) {
char c = Serial.read();
if (c >= '0' && c <= '9') {
typedNumber = typedNumber * 10 + (c - '0'); // build the number one digit at a time
} else if (c == '\n' || c == '\r') {
if (typedNumber > 0) measureAt(typedNumber);
typedNumber = 0;
}
}
delay(1);
}
| typedNumber = typedNumber * 10 + (c - '0'); | Builds the number from your digits: "2" then "0" → 2 → 20. |
| measureAt(rulerCm) | Takes 10 readings, keeps the smallest and biggest, and prints one CSV row. |
| average - rulerCm | The error: positive means the sensor reads too far. |
| highest - lowest | The spread: how much the 10 readings wobble. |
| Nothing happens when I press Enter | Choose "New Line" in the box at the bottom right of the Serial Monitor and try again. |
| The row says NA and 0 echoes | Nothing came back: square the card up to the sensor and check the wiring from Lesson 1. |
| All the rows land in one spreadsheet column | Use Data → Split text to columns (Google Sheets) or Data → Text to Columns (Excel) with comma. |
| Nothing or strange symbols in the Serial Monitor | Set the speed in the corner of the Serial Monitor to 9600 baud. Close the Serial Plotter if it is open. |
Make a chart of error against distance in your spreadsheet. Does the error grow as the card gets further away?
| Wiring from Lesson 1 (the other parts can stay) | |
| Flat card, cushion or jumper, bottle, pencil (yours) |
Sound bounces off a flat surface at the same angle it arrives, like a ball off a wall. A card facing the sensor sends the echo straight back; a tilted card sends it off to the side.
Soft materials absorb much of the sound, so the echo is weak. Thin or round objects reflect only a little sound back towards the sensor.
Each trial fires 20 pings, 50 ms apart, then prints: trial, echoes back, missed %, average, min and max. You then have 5 seconds to change the target.
This is why real distance sensors are never the only safety check: they can miss things that are really there.
| Part / pin | Connect to | Note |
|---|---|---|
| No changes | Sensor as in Lesson 1 | TRIG pin 9 · ECHO pin 10 |
Easiest: scan the QR code above and press Copy or Download .ino on the lesson page, then paste it into the Arduino IDE and click Upload. The full listing is here for reading along.
/*
DIY KIT - Arduino Ultrasonic Distance & Parking Learning Kit
Lesson 6 of 9: Angles & Surfaces
WIRING: the sensor from Lesson 1 (TRIG pin 9, ECHO pin 10).
HOW TO USE IT
Every 5 seconds the UNO fires 20 pings at the target and prints one row:
trial, echoes_back, missed_percent, average_cm, min_cm, max_cm
Between rows, change the target: flat card, card tilted, a cushion or
jumper, a bottle, a pencil. Keep it at the same distance (about 30 cm).
Open the Serial Monitor at 9600 baud.
*/
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
const int PINGS = 20; // pings per trial
const unsigned long GAP_MS = 5000; // time to change the target
const unsigned long ECHO_TIMEOUT_US = 30000;
const int MIN_CM = 2;
const int MAX_CM = 400;
int trial = 0;
long readOnceCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long us = pulseIn(ECHO_PIN, HIGH, ECHO_TIMEOUT_US);
if (us == 0) return 0;
long cm = us / 58;
if (cm < MIN_CM || cm > MAX_CM) return 0;
return cm;
}
void setup() {
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.begin(9600);
Serial.println("Lesson 6: Angles & Surfaces");
Serial.println("trial,echoes_back,missed_percent,average_cm,min_cm,max_cm");
}
void loop() {
int back = 0;
long total = 0;
long lowest = 9999;
long highest = 0;
for (int i = 0; i < PINGS; i++) {
long cm = readOnceCm();
if (cm > 0) {
back = back + 1;
total = total + cm;
if (cm < lowest) lowest = cm;
if (cm > highest) highest = cm;
}
delay(50);
}
trial = trial + 1;
int missedPercent = (PINGS - back) * 100 / PINGS;
Serial.print(trial); Serial.print(",");
Serial.print(back); Serial.print(",");
Serial.print(missedPercent); Serial.print(",");
if (back > 0) {
long average = total / back;
Serial.print(average); Serial.print(",");
Serial.print(lowest); Serial.print(",");
Serial.println(highest);
} else {
Serial.println("NA,NA,NA");
}
Serial.println("Change the target now - next trial in 5 seconds.");
delay(GAP_MS);
}
| const int PINGS = 20; | How many pings make one trial. |
| back = back + 1; | Counts the pings that came back. |
| (PINGS - back) * 100 / PINGS | The share of pings that were missed, as a percentage. |
| delay(GAP_MS); | A 5-second pause for you to change the target. |
| Every target gives 20 echoes | Try a steeper tilt (60°) or move the target further away (50 cm). Hard surfaces like a bottle may still reflect well. |
| Every target gives 0 echoes | Unplug USB. Check VCC to the + rail, GND to the − rail, TRIG to pin 9 and ECHO to pin 10 - by the labels on the sensor, not their position. Point it at a big flat object 20-50 cm away. |
| Nothing or strange symbols in the Serial Monitor | Set the speed in the corner of the Serial Monitor to 9600 baud. Close the Serial Plotter if it is open. |
Tilt the card in steps of 15° (0°, 15°, 30°, 45°, 60°). At what angle do the echoes start to go missing?
| Wiring from Lesson 1 (the other parts can stay) | |
| A book, and your hand (yours) |
Every 80 ms the sketch stores the newest good reading in history[], an array of 5. When it is full, the newest reading replaces the oldest, going round in a circle.
The average adds the 5 and divides by 5. It is smooth, but a single wrong reading pulls it off, and it is slow to catch up when the target really moves.
The median sorts the 5 and takes the middle. One spike sits at the end of the sorted list and is ignored. But it too needs 3 new readings before it follows a real move.
Every filter trades smoothness for delay. Lesson 9 uses the median, because ignoring spikes matters most for a parking beeper.
| Part / pin | Connect to | Note |
|---|---|---|
| No changes | Sensor as in Lesson 1 | TRIG pin 9 · ECHO pin 10 |
Easiest: scan the QR code above and press Copy or Download .ino on the lesson page, then paste it into the Arduino IDE and click Upload. The full listing is here for reading along.
/*
DIY KIT - Arduino Ultrasonic Distance & Parking Learning Kit
Lesson 7 of 9: Smoothing Noisy Readings
WIRING: the sensor from Lesson 1 (TRIG pin 9, ECHO pin 10).
WHAT SHOULD HAPPEN
Open Tools > Serial Plotter at 9600 baud. Three lines follow the target:
raw - every single reading (jumpy)
average - the mean of the last 5 readings (smooth, but slow to react)
median - the middle of the last 5 readings (ignores single spikes)
Wave a hand quickly through the beam and watch which line jumps.
*/
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
const int WINDOW = 5; // how many readings each filter remembers - try 3 or 9
const unsigned long ECHO_TIMEOUT_US = 30000;
const int MIN_CM = 2;
const int MAX_CM = 400;
long history[WINDOW]; // the last WINDOW good readings
int filled = 0; // how many slots hold a reading so far
int nextSlot = 0; // where the next reading goes
long readOnceCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long us = pulseIn(ECHO_PIN, HIGH, ECHO_TIMEOUT_US);
if (us == 0) return 0;
long cm = us / 58;
if (cm < MIN_CM || cm > MAX_CM) return 0;
return cm;
}
long averageOfHistory() {
long total = 0;
for (int i = 0; i < filled; i++) total = total + history[i];
long average = total / filled; // whole centimetres
return average;
}
long medianOfHistory() {
long sorted[WINDOW];
for (int i = 0; i < filled; i++) sorted[i] = history[i];
for (int i = 1; i < filled; i++) { // sort, smallest first
long value = sorted[i];
int j = i - 1;
while (j >= 0 && sorted[j] > value) {
sorted[j + 1] = sorted[j];
j--;
}
sorted[j + 1] = value;
}
return sorted[filled / 2]; // the middle one
}
void setup() {
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.begin(9600);
}
void loop() {
long cm = readOnceCm();
if (cm > 0) { // skip "no echo" readings
history[nextSlot] = cm;
nextSlot = (nextSlot + 1) % WINDOW; // go round in a circle
if (filled < WINDOW) filled++;
// "name:value,name:value" is the format the Serial Plotter draws.
Serial.print("raw:");
Serial.print(cm);
Serial.print(",average:");
Serial.print(averageOfHistory());
Serial.print(",median:");
Serial.println(medianOfHistory());
}
delay(80);
}
| long history[WINDOW]; | The array that remembers the last 5 readings. |
| nextSlot = (nextSlot + 1) % WINDOW; | Moves to the next box and wraps from 4 back to 0. |
| medianOfHistory() | Copies the readings, sorts them smallest first, and returns the middle one. |
| Serial.print("raw:") ... | The "name:value,name:value" pattern is what the Serial Plotter draws as labelled lines. |
| The Plotter shows nothing | Close the Serial Monitor first, then open the Plotter. Check the speed is 9600 baud. |
| Only one line | Upload the Lesson 7 code again: the line names come from the raw:, average: and median: labels. |
| The lines jump to 0 | No echo readings are skipped, so 0 should not appear. If it does, check the sensor wiring. |
| The upload fails | See Start here, step 6: "If the upload fails". |
Numbers 30, 30, 31, 120, 30. Work out the average and the median by hand. Which one is closer to the real 30 cm?
| Wiring from Lesson 1 (the other parts can stay) | |
| 0.96" OLED display, 4 pins | 1 |
| Male-to-male jumper wires | 4 |
The OLED has its own controller chip, the SSD1306. The Adafruit library knows how to talk to it, so we only call commands like print() and fillRect().
The UNO talks to the display over I2C: SDA on A4 carries the data, SCL on A5 the clock. The display answers at address 0x3C (some at 0x3D) - the sketch tries both.
Drawing happens in the UNO's memory first. Nothing appears until display.display() sends the whole picture. The picture needs 1 KB of the UNO's 2 KB of memory, which is why the text uses F("...").
The zones are the same as Lessons 2 and 3. The bar is full width at 100 cm.
| Part / pin | Connect to | Note |
|---|---|---|
| OLED VCC | + rail (5V) | Rated 3.3-5V. Go by the labels: the pin order varies |
| OLED GND | − rail (GND) | |
| OLED SDA | UNO A4 | I2C data |
| OLED SCL | UNO A5 | I2C clock |
| HC-SR04 | As in Lesson 1 | TRIG pin 9, ECHO pin 10 |
Easiest: scan the QR code above and press Copy or Download .ino on the lesson page, then paste it into the Arduino IDE and click Upload. The full listing is here for reading along.
/*
DIY KIT - Arduino Ultrasonic Distance & Parking Learning Kit
Lesson 8 of 9: OLED Distance Meter
LIBRARIES (once): Tools > Manage Libraries, search "Adafruit SSD1306",
click Install, and choose "Install all" so Adafruit GFX and BusIO come too.
WIRING (unplug the USB cable before you wire)
The sensor from Lesson 1 (TRIG pin 9, ECHO pin 10) stays.
OLED display - go by the labels printed on it, the order varies:
VCC -> 5V (+ rail) GND -> GND (- rail)
SDA -> UNO A4 SCL -> UNO A5
The LEDs and buzzer can stay; this sketch does not use them.
WHAT SHOULD HAPPEN
The OLED shows the zone at the top, the distance in big numbers and a
bar that grows as the target moves away (full at 100 cm).
The Serial Monitor (9600 baud) says which address the display answered on.
F("...") keeps text in the UNO's large program memory instead of its small
working memory (RAM). The display needs 1 KB of RAM for its picture.
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
const int CLEAR_CM = 50; // the same zones as Lessons 2 and 3
const int NEAR_CM = 20;
const int BAR_FULL_CM = 100; // the bar is full width at this distance
const unsigned long ECHO_TIMEOUT_US = 30000;
const int MIN_CM = 2;
const int MAX_CM = 400;
Adafruit_SSD1306 display(128, 64, &Wire, -1); // 128 x 64 pixels, no reset pin
bool displayOk = false;
long readOnceCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long us = pulseIn(ECHO_PIN, HIGH, ECHO_TIMEOUT_US);
if (us == 0) return 0;
long cm = us / 58;
if (cm < MIN_CM || cm > MAX_CM) return 0;
return cm;
}
// Most of these displays answer at address 0x3C, some at 0x3D. Try both.
bool startDisplay() {
if (display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("OLED found at 0x3C"));
return true;
}
if (display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) {
Serial.println(F("OLED found at 0x3D"));
return true;
}
Serial.println(F("OLED not found - unplug USB and check VCC, GND, SDA (A4) and SCL (A5)"));
return false;
}
void setup() {
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
Serial.begin(9600);
Serial.println(F("Lesson 8: OLED Distance Meter"));
displayOk = startDisplay();
if (displayOk) {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.display();
}
}
void loop() {
long cm = readOnceCm();
Serial.println(cm); // 0 means no echo
if (displayOk) {
display.clearDisplay();
// Top line: the zone, in small letters.
display.setTextSize(1);
display.setCursor(0, 0);
if (cm == 0) display.print(F("NO ECHO"));
else if (cm < NEAR_CM) display.print(F("NEAR"));
else if (cm < CLEAR_CM) display.print(F("CAUTION"));
else display.print(F("CLEAR"));
// Middle: the distance, 3 times bigger.
display.setTextSize(3);
display.setCursor(0, 22);
if (cm == 0) {
display.print(F("--"));
} else {
display.print(cm);
display.setTextSize(2);
display.print(F(" cm"));
}
// Bottom: a bar, full width at BAR_FULL_CM.
if (cm > 0) {
int width = map(constrain(cm, 0, BAR_FULL_CM), 0, BAR_FULL_CM, 0, 128);
display.fillRect(0, 56, width, 8, SSD1306_WHITE);
}
display.display(); // nothing appears until display() is called
}
delay(150);
}
| #include <Adafruit_SSD1306.h> | Brings in the library you installed. |
| Adafruit_SSD1306 display(128, 64, &Wire, -1); | A display that is 128 × 64 pixels on the I2C wires (Wire), with no reset pin. |
| startDisplay() | Tries address 0x3C, then 0x3D, and tells you in the Serial Monitor which one worked. |
| display.setTextSize(3); display.print(cm); | Big digits. Then display.display() sends the picture to the screen. |
| "OLED not found" | Unplug USB. Check VCC, GND, SDA to A4 and SCL to A5 by the labels. Push the OLED pins firmly into the breadboard. |
| "Adafruit_SSD1306.h: No such file or directory" | The library is not installed: do step 1 again and choose Install All. |
| The screen stays black but "OLED found" appears | Upload again. If it still stays black, ask us - the display may need a different setting. |
| The screen shows random dots | Press the UNO's reset button. If it keeps happening, a wire may be loose. |
| It always says no echo | Unplug USB. Check VCC to the + rail, GND to the − rail, TRIG to pin 9 and ECHO to pin 10 - by the labels on the sensor, not their position. Point it at a big flat object 20-50 cm away. |
Draw a frame round the screen: add display.drawRect(0, 0, 128, 64, SSD1306_WHITE); just before display.display(). Then try a circle with display.drawCircle(110, 8, 6, SSD1306_WHITE);
| Everything from Lessons 1-8 | all |
| 9V battery holder with DC plug | 1 |
| 9V battery (yours, optional) | 1 |
This is DistanceParkingLab.ino: Lessons 2, 3, 7 and 8 in one sketch. The same zones, beeps and pins, plus a median of the last 5 readings.
Nothing waits with delay() in the main loop. Measuring (every 80 ms), the LEDs, the buzzer, the OLED and the Serial line each check millis() and do their job when it is time.
Three missed echoes in a row switch it to NO_ECHO: yellow blinks, the buzzer goes silent and the OLED says NO ECHO. One missed ping is not enough.
The OLED is optional: if none answers at 0x3C or 0x3D, everything else still works.
| Part / pin | Connect to | Note |
|---|---|---|
| Red / yellow / green LED (each through 220 Ω) | UNO pins 5 / 6 / 7 | From Lesson 2 |
| Buzzer I/O | UNO pin 8 | From Lesson 3 |
| HC-SR04 TRIG / ECHO | UNO pins 9 / 10 | From Lesson 1 |
| OLED SDA / SCL | UNO A4 / A5 | From Lesson 8 - optional |
| All VCC and + pins | + rail (5V) | |
| All GND and − pins, LED short legs | − rail (GND) | |
| 9V battery holder plug | UNO DC jack (round socket) | Battery demo only - unplug USB first |
| constexpr uint16_t CRITICAL_CM = 10; NEAR_CM = 20; CLEAR_CM = 50; | The same zones as Lessons 2 and 3, in one place. |
| updateMeasurement() | One ping; three misses in a row → NO_ECHO; otherwise the median of the last 5 (Lesson 7). |
| updateLeds(), updateBuzzer(), updateDisplay() | Each output on its own timer with millis(), so nothing blocks the others. |
| handleCommand() | Compares what you typed with help, status, scan, run, pause, sound on, sound off and test. |
| #define DISTANCE_DIAGNOSTIC false | Change to true to fake 80, 35, 15 and 6 cm and no echo - tests the outputs without the sensor. |
| Compile error about Adafruit_SSD1306.h | Install the libraries: Lesson 8, step 1. |
| Commands do nothing | Choose "New Line" at the bottom right of the Serial Monitor and check 9600 baud. |
| The OLED stays off but everything else works | That is allowed - the OLED is optional. Type status: oled=0 means it was not found. Check Lesson 8. |
| It works on USB but not on the battery | The battery may be flat. 9V batteries run down quickly - use a fresh one. |
| It beeps in the gaps, or all the time | Set BUZZER_ACTIVE_LOW to true and upload again (see Lesson 3). |
| It always says no echo | Unplug USB. Check VCC to the + rail, GND to the − rail, TRIG to pin 9 and ECHO to pin 10 - by the labels on the sensor, not their position. Point it at a big flat object 20-50 cm away. |
Type pause, then run. Then type scan: which address does the OLED answer on? Compare with Lesson 8.
Unplug the USB cable first. Every problem below has its fix in the table of the lesson or step named on the right.
Every project in this kit uses the same parts, so you can combine them: add the display to a sensor project, or a buzzer to a measuring one. The project pages on diykit.ae have the code, the parts list and ideas to take each build further.
Most problems are one wire in the wrong hole. Check these first, then the lesson's "If it doesn't work" table:
Still stuck? Send us this message on WhatsApp with a photo of your wiring - scan the code to open WhatsApp with it already filled in: