Build an ultrasonic walking aid that beeps faster near obstacles

A handheld helper that "sees" with sound, like a bat. The closer an obstacle, the faster it beeps, and right in front of it the beep becomes one long tone. A button switches between indoor and outdoor range.
  • Level:Beginner
  • Duration: 45 min
  • Ages 10+
  • Platform: Arduino

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Build an ultrasonic walking aid that beeps faster near obstacles – EEMENTOR STEM™ lesson

Bats find their way in the dark by shouting and listening for the echo. The ultrasonic sensor in your kit does the same. In this lesson you turn it into a handheld walking aid, an idea that inventors around the world have developed into real "smart canes" for people who are blind or partially sighted. Point it ahead of you: when something comes closer than the range you chose, it starts to beep, faster and faster as you get nearer, and it gives one long tone when you are about to bump into it. Because it runs on a battery you can walk around with it.

What you'll learn

  • How to turn a distance into a beep speed with map()
  • Why a median of five readings is better than one reading
  • How to make the beeps without stopping the measurements
  • How to run the UNO from a battery

What you need

  • UNO R3 board and USB cable (UNO R3 Starter Kit)
  • Ultrasonic sensor HC-SR04 (UNO R3 Starter Kit)
  • Active buzzer (UNO R3 Starter Kit)
  • 1 push button (UNO R3 Starter Kit)
  • 9 V battery and battery snap (UNO R3 Starter Kit), or a USB power bank
  • Optional: a ruler or a cardboard tube to hold it all
  • Breadboard and jumper wires (UNO R3 Starter Kit)

Important: this is a learning project, not a certified safety device. It can't replace a white cane or a guide dog. It only sees what is straight in front of the sensor, so it misses steps, kerbs and things at head height. Try it with your eyes open, in a safe room.

Wiring

Breadboard wiring diagram: Build an ultrasonic walking aid that beeps faster near obstacles
Breadboard view. Check each connection against the wiring table below.
From To
HC-SR04 VCC 5V
HC-SR04 Trig UNO pin 9
HC-SR04 Echo UNO pin 10
HC-SR04 GND GND
Active buzzer + (longer leg) UNO pin 8
Active buzzer − GND
Push button, one leg UNO pin 2
Push button, the diagonally opposite leg GND

For walking around, unplug the USB cable and power the UNO from a USB power bank, or from the 9 V battery: plug the snap's red wire into VIN and its black wire into GND. Use the sketch with the USB cable first, so you can see the distances in the Serial Monitor.

The sketch

const int TRIG_PIN = 9, ECHO_PIN = 10, BUZZER_PIN = 8, BUTTON_PIN = 2;
const int VERY_CLOSE = 25;                // cm: one long tone below this
const int RANGE[2] = {100, 200};          // cm: indoor and outdoor range
const char* RANGE_NAME[2] = {"indoor (1 m)", "outdoor (2 m)"};

int mode = 0;                             // starts in indoor mode
bool lastButton = HIGH;
unsigned long nextBeep = 0;

long measureCm() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);           // a 10 µs pulse starts a measurement
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  long echo = pulseIn(ECHO_PIN, HIGH, 30000);   // wait at most 30 ms
  if (echo == 0) return 999;              // no echo: nothing within about 5 m
  return echo / 58;                       // microseconds -> cm
}

long medianDistance() {                   // 5 readings, sorted, take the middle one
  long d[5];
  for (int i = 0; i < 5; i++) {
    d[i] = measureCm();
    delay(10);
  }
  for (int i = 1; i < 5; i++) {           // insertion sort
    long x = d[i];
    int j = i - 1;
    while (j >= 0 && d[j] > x) {
      d[j + 1] = d[j];
      j--;
    }
    d[j + 1] = x;
  }
  return d[2];
}

void beeps(int n) {
  for (int i = 0; i < n; i++) {
    digitalWrite(BUZZER_PIN, HIGH);
    delay(80);
    digitalWrite(BUZZER_PIN, LOW);
    delay(120);
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  beeps(1);
  Serial.println("Walking aid ready, indoor mode");
}

void loop() {
  bool button = digitalRead(BUTTON_PIN);
  if (lastButton == HIGH && button == LOW) {   // button pressed: switch the range
    mode = 1 - mode;
    beeps(mode + 1);                      // 1 beep = indoor, 2 beeps = outdoor
    Serial.print("Mode: ");
    Serial.println(RANGE_NAME[mode]);
  }
  lastButton = button;

  long cm = medianDistance();
  Serial.print(cm);
  Serial.println(" cm");

  unsigned long now = millis();
  if (cm <= VERY_CLOSE) {
    digitalWrite(BUZZER_PIN, HIGH);       // right in front of you: long tone
  } else if (cm < RANGE[mode]) {
    unsigned long gap = map(cm, VERY_CLOSE, RANGE[mode], 60, 700);
    if (now >= nextBeep) {                // time for the next short beep
      digitalWrite(BUZZER_PIN, HIGH);
      delay(30);
      nextBeep = now + gap;
    }
    digitalWrite(BUZZER_PIN, LOW);
  } else {
    digitalWrite(BUZZER_PIN, LOW);        // nothing in range: quiet
  }
}

Try it

  1. Upload the sketch and open the Serial Monitor at 9600 baud. You hear one beep: indoor mode, range 1 m.
  2. Move your hand slowly towards the sensor from about 1 m. The beeps start and get faster, and below 25 cm they become one long tone.
  3. Press the button: two beeps mean outdoor mode, which starts warning at 2 m. Press again for indoor mode.
  4. Unplug the USB cable and connect the power bank or the battery. Hold the sensor at waist height, pointing forward, and walk slowly towards a wall or a door.
  5. Try different things: a wall, a chair, a coat, a plant. Soft and thin things reflect less sound, so they are found later.

How it works

The sensor sends a short burst of ultrasound, too high for us to hear, and measures how long the echo takes to come back. Sound travels about 343 metres per second, which is 0.0343 cm per microsecond. The sound goes there and back, so the distance in cm is the echo time divided by 2 × 29, or simply by 58.

Sometimes the sensor gets a wrong reading, for example when the sound bounces off at an angle. If you used every reading, the walking aid would beep at nothing. medianDistance() takes five readings, sorts them and uses the middle one, the median. One or two wrong values end up at the start or end of the sorted list and are simply ignored.

map() turns the distance into a gap between beeps: at 25 cm the gap is 60 ms (very fast), at the edge of the range it is 700 ms (slow). Instead of waiting with delay(), the sketch remembers when the next beep is due in nextBeep and keeps measuring in the meantime, so it reacts at once when you get closer.

Try this next

  • Add a second ultrasonic sensor pointing down at an angle, to warn about steps and kerbs.
  • Use the passive buzzer instead, and make the tone higher as obstacles get closer, so you hear the distance in two ways.
  • Add a vibration: a small DC motor with an off-centre weight, switched by a transistor, buzzes in your hand.
  • Build it into a stick or a torch-like case, with the button under your thumb.

Troubleshooting

  • It always says 999 cm: check Trig on pin 9 and Echo on pin 10, and that VCC goes to 5V. Swapped Trig and Echo wires are the most common mistake.
  • It beeps at nothing: the floor or a table edge may be in the sensor's wide "view". Hold it a little higher and point it straight ahead.
  • It works on USB but not on the battery: check the battery snap: red to VIN, black to GND. A used 9 V battery runs empty quickly; a power bank lasts much longer.
  • No sound: the active buzzer has a + leg (the longer one, or marked on top). Turn it round.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

UNO R3 Starter Kit | Arduino IDE Compatible | Free Tutorials $56.00

Shop the kit