JM9 XRCU Tutorials 0.0.1a
Learn how to program XRCU
Loading...
Searching...
No Matches
Ultrasonic Sensors

An ultrasonic sensor measures how far away an object is. It sends out a short sound that is too high for humans to hear, then listens for the echo. The longer the echo takes to come back, the further away the object is. Bats use the same trick to fly in the dark!

In this chapter, we will use the HC-SR04 ultrasonic sensor with XRCU.

The Old Way

In your previous classes, you may have measured distance like this:

// Send a short pulse on the TRIG pin.
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Measure how long the ECHO pin stays HIGH, then turn it into centimeters.
long duration = pulseIn(echoPin, HIGH);
int distance = duration * 0.017;
// Wait a bit before the next measurement.
delay(50);

It works, but you have to remember many steps. It also has some hidden problems:

  • If you measure again too quickly, the sensor may hear the echo of the last measurement, and give a wrong, too-short distance.
  • If 2 sensors shout at the same time, they may hear each other's echoes.
  • While pulseIn(...) is waiting for the echo, your program cannot do anything else.

XRCU has a library that solves all of these problems for you: Uts_gpio. (Uts is short for ultrasonic.)

Wiring to an E Port

The HC-SR04 has 4 pins: VCC, Trig, Echo and Gnd. A port on XRCU also has 4 pins: 3V3, GND, an A pin and a B pin. So 1 sensor uses exactly 1 port.

On XRCU, ultrasonic sensors should be connected to the E ports: E1, E2, E3 and E4, at the top of the board. We will see why soon.

Use 4 jumper wires to connect the sensor to port E1:

HC-SR04 pin Connect to XRCU
VCC 3V3
Gnd GND
Trig E1A
Echo E1B
Warning
Always turn OFF XRCU before you connect or disconnect a sensor. Trig must go to the A pin, and Echo must go to the B pin. If you swap them, the sensor will not work.

Reading the Distance

First, include the Uts_gpio library at the top of your program:

#include <Uts_gpio.h>

Next, we create an ultrasonic sensor in our code, and tell it which port it uses:

Uts_gpio uts(E1);

This line means: "make a new Uts_gpio sensor called uts, on port E1". Write it above setup(), so that both setup() and loop() can use it. You can choose another name instead of uts, like frontSensor.

Note
E1 is the name of the whole port. E1A and E1B are the names of its 2 pins. Here we give the port name, and the library knows which pin is Trig and which is Echo.

Then, we start the sensor with begin() inside setup(), and read the distance in centimeters with read_dist_cm():

#include <Uts_gpio.h>
// Make an ultrasonic sensor called uts, on port E1.
Uts_gpio uts(E1);
void setup() {
Serial.begin(9600);
// Start the ultrasonic sensor.
uts.begin();
}
void loop() {
// Read the distance in centimeters.
int distance = uts.read_dist_cm();
Serial.println(distance);
delay(100);
}

That's it! No pulseIn(...), no maths, and no waiting between measurements. The library does all of that for you.

Warning
Do not forget uts.begin(); in setup(). Without it, the sensor never starts measuring.

Checkpoint 8.1

Turn OFF XRCU. Connect your HC-SR04 to port E1 as shown above. Turn ON XRCU, then upload the code above and run it. Open the serial monitor.

Point the sensor at a wall or a big book. Use a ruler to place the sensor 10 cm, 30 cm and 50 cm away from it. Does the serial monitor show the correct distance each time?

Record a video showing the ruler, the sensor and the serial monitor. Submit this video to your assignment portal at checkpoint 8.1.

When Nothing Comes Back

Sometimes the sensor does not hear any echo. For example:

  • there is nothing in front of the sensor for more than 4 meters,
  • the object is soft (like a pillow) and swallows the sound,
  • the object is tilted, so the echo bounces away,
  • a wire is loose.

When this happens, read_dist_cm() gives 888. 888 is a special number that means "I could not measure the distance". It does not mean the object is 888 cm away!

You can also read the distance in millimeters with read_dist_mm(). 1 cm = 10 mm, so in millimeters the special number is 8888.

int distance = uts.read_dist_cm();
if (distance == 888) {
Serial.println("No echo!");
} else {
Serial.println(distance);
}
Remarks
Always check for 888 in your robot programs. Otherwise, your robot may think the wall is very far away, when the sensor simply could not measure it.

Checkpoint 8.2

Change your program, so that it prints:

  1. No echo! when the sensor could not measure the distance.
  2. The distance in millimeters at all other times.

Upload and run it. Then try to make the sensor show No echo!: point it at the ceiling far away, at a pillow, or at a book tilted at an angle. Which ones give No echo!?

Record a video showing your tests and the serial monitor. Submit this video to your assignment portal at checkpoint 8.2.

Why E Ports?

Uts_gpio works on any port, not just the E ports. For example, Uts_gpio uts(U3); uses port U3, with Trig on U3A and Echo on U3B.

But the E ports are special. On an E port, XRCU measures the distance by itself, in the background, about 20 times every second. When you call read_dist_cm(), you get the newest distance immediately. You have seen this idea before: the Buttons library and the Ain library also do their work in the background.

On other ports, every read_dist_cm() makes your program wait for a new measurement, which takes about 0.05 seconds or more. Your program cannot do anything else during that time. For a fast robot, that is a long time!

You do not need to do anything special to get the faster way. Uts_gpio checks which port you gave it. If it is an E port, Uts_gpio quietly hands the work to another library called Uts_tic, which measures in the background. Your code stays exactly the same. Just change the port!

Remarks
Always connect ultrasonic sensors to the E ports (E1 to E4), unless you have more than 4 ultrasonic sensors.

Checkpoint 8.3

Let's measure how much faster the E ports are. Upload this program with the sensor on port E1:

#include <Uts_gpio.h>
Uts_gpio uts(E1);
int counter;
void setup() {
Serial.begin(9600);
uts.begin();
counter = 0;
}
void loop() {
int distance = uts.read_dist_cm();
counter = counter + 1;
// Print a message after every 100 readings.
if (counter == 100) {
Serial.print("100 readings done. Distance: ");
Serial.println(distance);
counter = 0;
}
delay(10);
}

Open the serial monitor with timestamps. How many seconds are there between 2 messages?

Then turn OFF XRCU, move the sensor to port U3, change E1 to U3 in the code, and try again. How many seconds are there between 2 messages now?

Take a screenshot of the serial monitor with timestamps for each port. Submit both screenshots to your assignment portal at checkpoint 8.3.

Using 2 Sensors

A robot often needs more than 1 sensor, for example one at the front and one at the side. To use more sensors, we create one Uts_gpio for each sensor. Give each sensor its own name and its own port:

#include <Uts_gpio.h>
// Make 2 ultrasonic sensors: one on E1, one on E2.
Uts_gpio front(E1);
Uts_gpio side(E2);
void setup() {
Serial.begin(9600);
// Start both sensors.
front.begin();
side.begin();
}
void loop() {
// Print both distances on the same line.
Serial.print(front.read_dist_cm());
Serial.print(" ");
Serial.println(side.read_dist_cm());
delay(100);
}
Note
Unlike the buttons, ultrasonic sensors do not start by themselves. You need to call begin() for each sensor you use.

When you use more than 1 sensor, they take turns: only 1 sensor sends out a sound at a time. This way, they never hear each other's echoes. The more sensors you use, the longer each sensor waits for its turn. With 2 sensors, each sensor measures about 10 times every second.

Checkpoint 8.4

Turn OFF XRCU. Connect a second HC-SR04 to port E2. Write a program with 2 Uts_gpio sensors on E1 and E2, so that:

  1. The serial monitor prints both distances on the same line.
  2. LED_BUILTIN is ON when either sensor sees something closer than 20 cm. (Hint: in an if, || means "or". Do not forget to check for 888!)

Upload and run it. Point the 2 sensors in different directions, then move your hand towards each sensor one by one.

Record a video showing the sensors, the LED and the serial monitor. Submit this video to your assignment portal at checkpoint 8.4.

Pausing a Sensor

Sometimes we want a sensor to stay quiet for a while. For example, in some competitions, 2 robots work on the same field. The sound from your robot may confuse your teammate's robot.

We can pause a sensor with disable(), and start it again with enable():

side.disable(); // The side sensor stops sending sounds.
side.enable(); // The side sensor starts again.

While a sensor is disabled, its read_dist_cm() gives 888, because it is not measuring anything.

Checkpoint 8.5

Keep both sensors on E1 and E2. Change your program from checkpoint 8.4, so that:

  1. Clicking SW1 disables the sensor on E2.
  2. Clicking SW2 enables the sensor on E2 again.

(Hint: use button1.isClicked() and button2.isClicked() from the Buttons chapter.)

Upload and run it. Click SW1, then move your hand in front of the sensor on E2. What does the serial monitor show? Then click SW2 and try again.

Record a video showing the buttons, the sensors and the serial monitor. Submit this video to your assignment portal at checkpoint 8.5.

Summary

Code What it does
#include <Uts_gpio.h> Lets us use Uts_gpio.
Uts_gpio uts(E1); Makes a sensor called uts on port E1. Write it above setup().
uts.begin() Starts the sensor. Call it in setup().
uts.read_dist_cm() The distance in centimeters, or 888 if there is no echo.
uts.read_dist_mm() The distance in millimeters, or 8888 if there is no echo.
uts.disable() Pauses the sensor.
uts.enable() Starts the paused sensor again.

Remember:

  • Connect Trig to the A pin and Echo to the B pin.
  • Use the E ports whenever you can. XRCU then measures in the background, so your program never has to wait.