Skip to content

Week 9 Individual Assignment: Adding an

Distance Sensor to XIAO Expansion Board

Project Introduction

The main goal of this week's individual project is to add a sensor to my microcontroller board and read data from it. To achieve this goal, I chose the Grove - Ultrasonic Ranger distance sensor, a non-contact distance measurement module operating at 40KHz.

Grove - Ultrasonic Ranger distance sensor

For this week's assignment, I've outlined several specific objectives including:

  1. Understanding the working principles of ultrasonic distance sensors
  2. Designing the sensor circuit and connecting it to the XIAO ESP32C3
  3. Writing programs to test the distance measurement functionality
  4. Visualizing the measurement results
  5. Documenting the entire design, manufacturing, and testing process

Understanding the Working Principles of Ultrasonic Distance Sensors

Introduction to Grove - Ultrasonic Ranger

The Grove - Ultrasonic Ranger is an ultrasonic distance module produced by Seeed Studio, featuring a standard Grove interface for easy integration with various microcontrollers (such as XIAO development boards). The image below shows the purchased sensor on the left and my completed development board on the right.

Comparison between the Grove - Ultrasonic Ranger module (left) and my XIAO development board

Ultrasonic Distance Measurement Principles

Ultrasonic distance measurement is a technology based on sound wave propagation time to measure distance. The working principle is as follows:

  1. Emitting Ultrasonic Waves: The sensor emits 8 pulses of 40KHz ultrasonic waves
  2. Waiting for Echo: The ultrasonic waves propagate through the air and reflect back upon hitting an obstacle
  3. Receiving Echo: The sensor receives the reflected ultrasonic waves
  4. Calculating Distance: The distance is calculated based on the time difference between emission and reception of the ultrasonic waves

Distance calculation formula: Distance = Echo Signal High Level Time × Speed of Sound (340m/s) ÷ 2

The division by 2 accounts for the round-trip distance traveled by the ultrasonic waves.

Grove - Ultrasonic Ranger Technical Specifications

According to the official Wiki documentation, the Grove - Ultrasonic Ranger has the following technical characteristics:

ParameterSpecification
Operating Voltage3.2V-5.2V
Operating Current8mA
Ultrasonic Frequency40KHz
Measuring Range2-350cm
Resolution1cm
Output MethodPWM
Dimensions50mm × 25mm × 16mm
Weight13g
Measuring Angle15 degrees
Operating Temperature-10~60℃
Trigger Signal10μS TTL
Echo SignalTTL

The core sensor Ceramic Ultrasonic Sensor NU40C16T/R-1 documentation provides a test circuit as shown below.

Test circuit provided in the Ceramic Ultrasonic Sensor NU40C16T/R-1 documentation

This test circuit diagram shows the test configuration for the ultrasonic sensor (NU40C16T/R-1), divided into Receiver and Transmitter sections. I will explain each element and the overall working principle:

Receiver Circuit

The left side shows the receiver test setup:

  1. A: Oscillator - Provides the test signal
  2. TW: Tweeter - Used to emit sound waves
  3. F.C.: Frequency Counter - Monitors signal frequency
  4. MIC - Standard reference microphone, receives sound waves simultaneously with the receiver ultrasonic sensor (R.U.S.)
  5. R.U.S.: Receiver Ultrasonic Sensor - The receiver being tested
  6. R: 3.9K Resistor - Provides load for the signal
  7. B: Amplifier - Amplifies the sensor output signal
  8. C: Voltmeter - Measures output voltage, reference value 0dB=10V/Pa

Transmitter Circuit

The right side shows the transmitter test setup:

  1. A: Oscillator - Provides 10Vrms input voltage
  2. F.C.: Frequency Counter - Monitors signal frequency
  3. T.U.S.: Transmitter Ultrasonic Sensor - The transmitter being tested
  4. MIC - Used to receive ultrasonic waves produced by the transmitter
  5. B: Amplifier - Amplifies signals received by the microphone
  6. C: Voltmeter - Measures the amplified signal, reference value 0dB=0.02mPa

Testing Principles and Procedures

  1. Receiver Testing:
    • The oscillator generates a 40kHz test signal
    • The tweeter (TW) converts the electrical signal into ultrasonic waves
    • In an anechoic room at 30cm distance, both the reference microphone and the receiver under test receive the ultrasonic waves
    • The receiver's output signal goes through a 3.9K resistor to ground, then is amplified
    • The voltmeter measures the amplified signal, comparing it with the standard 0dB=10V/Pa to determine the receiver's sensitivity
  2. Transmitter Testing:
    • The oscillator outputs a 10Vrms 40kHz signal to the transmitter under test
    • The transmitter produces ultrasonic waves
    • At 30cm distance in an anechoic room, the standard microphone receives the ultrasonic waves
    • The microphone signal is amplified
    • The voltmeter measures the amplified signal, comparing it with the standard 0dB=0.02mPa to determine the transmitter's sound pressure level

Test Environment

The anechoic room is a key environment for testing, which:

  • Eliminates environmental reflections and interference
  • Provides stable, consistent test conditions
  • Ensures the accuracy and repeatability of measurement results

This testing method allows precise measurement of key performance parameters of the sensors, including center frequency (40kHz±1kHz), transmitter sound pressure level (≥114dB), and receiver sensitivity (≥-68dB).

Workflow

The Grove - Ultrasonic Ranger workflow is as follows:

  1. Initialization: The microcontroller initializes the sensor
  2. Triggering Measurement: The microcontroller sends a high-level trigger signal of at least 10μs to the sensor
  3. Emitting Ultrasonic Waves: Upon receiving the trigger signal, the sensor emits 8 pulses of 40KHz ultrasonic waves
  4. Waiting for Echo: The sensor waits for the ultrasonic waves to reflect back
  5. Receiving Echo: The sensor receives the reflected ultrasonic waves and outputs a high-level signal proportional to the distance
  6. Calculating Distance: The microcontroller measures the duration of the high-level signal and calculates the distance using the formula

Interface with Microcontrollers

The Grove - Ultrasonic Ranger uses a standard Grove interface to communicate with microcontrollers:

  • VCC: Connected to the microcontroller's 3.3V or 5V power supply
  • GND: Connected to the microcontroller's ground
  • SIG: Connected to a digital pin on the microcontroller, used for sending trigger signals and receiving echo signals
  • NC: Not connected

A feature of the Grove - Ultrasonic Ranger is that the trigger signal and echo signal share a single SIG pin, which simplifies wiring but requires special handling in software.

Designing the Sensor Circuit and Connecting it to the XIAO ESP32C3 Development Board

Hardware Connection Plan

The 8-pin connector on my XIAO development board is shown in the figure below.

Pin numbering on my designed XIAO development board

Function description of the 8-pin connector (J1) on the XIAO ESP32C3 expansion board:

  1. GND - Ground connection
  2. 3.3V - 3.3V power output
  3. RX/D7 - Serial receive pin (GPIO20)
  4. TX/D6 - Serial transmit pin (GPIO21)
  5. SCK/D8 - SPI clock signal (GPIO8), marked with * as a Strapping pin
  6. MISO/D9 - SPI master input slave output (GPIO9), marked with * as a Strapping pin
  7. MOSI/D10 - SPI master output slave input (GPIO10)
  8. RST/Other available pins - Can be used as reset signal or for other expansion functions

Connecting the Grove - Ultrasonic Ranger distance sensor to the XIAO ESP32C3 development board is very simple and direct. The sensor only requires three pin connections, including power, ground, and signal line.

Connection Details

Grove - Ultrasonic Ranger PinXIAO ESP32C3 PinDevelopment Board 8-pin Connector (J1) NumberFunction
VCC (Red Wire)3.3V2Power positive
GND (Black Wire)GND1Power ground
SIG (Yellow Wire)D6 / GPIO214Signal line (shared for trigger and echo)
NC (White Wire)Not connectedUnused

Wiring Diagram

Wiring diagram of XIAO ESP32C3 and Grove - Ultrasonic Ranger module

Hardware Connection Notes

  1. Power Selection: The Grove - Ultrasonic Ranger can operate within a voltage range of 3.2V-5.2V, and the XIAO ESP32C3's 3.3V output meets this requirement.
  2. Signal Line Connection: The sensor's SIG pin is connected to the XIAO ESP32C3's digital pin D6, used for sending trigger signals and receiving echo signals.
  3. Sensor Placement: The placement of the ultrasonic sensor is very important, ensure that:
    • There are no obstructions in front of the sensor
    • The measurement area is no less than 0.5 square meters and has a smooth surface
    • The sensor is firmly secured to avoid movement that could cause measurement errors
  4. Connection Wire Length: Use the shortest possible connection wires to reduce signal interference. Recommended wire length should not exceed 20cm.

Actual Connection Implementation

Since the project is in the prototype stage, I used jumper wires for temporary connections, connecting the Grove - Ultrasonic Ranger module to the previously designed XIAO ESP32C3 expansion board. The completed connection is shown in the figure below:

Connection of the homemade XIAO ESP32C3 development board with the Grove - Ultrasonic Ranger module

Programming and Testing the Distance Measurement Function

Development Environment Setup

The development environment is based on Arduino IDE, including the following steps:

  1. Installing XIAO ESP32C3 Board Support:
    • Add Seeed Studio XIAO board support in Arduino IDE.
    • Select "XIAO_ESP32C3" as the target board.
  2. Installing Ultrasonic Ranger Library:
    • Search for grove Ultrasonic in the Arduino IDE library manager.
    • Install Grove Ultrasonic Ranger by Seeed Studio, as shown below.

Need to search and install Grove Ultrasonic Ranger by Seeed Studio library in the Library Manager

  1. Test Environment Preparation:
    • Set serial monitor baud rate to 115200.
    • Prepare the test environment, ensuring stable measurement conditions.

Test Program Code

Based on the test program provided in the Wiki documentation:

cpp
#include "Ultrasonic.h"

Ultrasonic ultrasonic(7);
void setup()
{
 Serial.begin(9600);
}
void loop()
{
 long RangeInInches;
 long RangeInCentimeters;

 Serial.println("The distance to obstacles in front is: ");
 RangeInInches = ultrasonic.MeasureInInches();
 Serial.print(RangeInInches);//0~157 inches
 Serial.println(" inch");
 delay(250);

 RangeInCentimeters = ultrasonic.MeasureInCentimeters(); // two measurements should keep an interval
 Serial.print(RangeInCentimeters);//0~400cm
 Serial.println(" cm");
 delay(250);
}

I made a few modifications, changing Ultrasonic ultrasonic(7); to Ultrasonic ultrasonic(21); (to match the XIAO ESP32C3's pin D6/GPIO21 numbering, note that you need to use the GPIO number here, otherwise no signal will be received), and also simplified the output to only display the values to facilitate graphical display in the serial plotter. The modified program is shown below:

cpp
#include "Ultrasonic.h"

// Use the correct GPIO number
Ultrasonic ultrasonic(21);  // Corresponds to XIAO ESP32C3's D6/GPIO21

void setup() {
  Serial.begin(115200);  // XIAO ESP32C3 typically uses 115200 baud rate
  delay(1000);  // Wait for serial connection
  
  // Send a unit information at the start
  // Most plotters will ignore this line, but it's useful information for human users
  Serial.println("Distance(cm)");
}

void loop() {
  // Measure distance
  long distance = ultrasonic.MeasureInCentimeters();
  
  // Output only the value, without text, suitable for serial plotter
  Serial.println(distance);
  
  delay(500);  // Measure once every 500 milliseconds
}

After running the program, the measured distance when I extend my hand can be seen via the serial monitor, as shown below.

Serial monitor begins to output the measured distance

Data Visualization

Clicking on the serial plotter in the upper right corner of Arduino IDE opens a graphical window, displaying the measured distance changes in a visual format, as shown below.

Serial plotter dynamically displays the measured distance changes in a visual way

Testing Methods and Results

I set up a simple distance testing environment with a ruler, and at relatively close distances, it was quite accurate.

Distance testing video, the serial monitor on the screen reports the measurement distance (cm) output by the sensor

I designed a systematic testing process to comprehensively evaluate the distance measurement functionality:

  1. Basic Function Testing:
    • Placing test objects at different distances
    • Recording the measurement results and actual distances
    • Calculating measurement errors
  2. Testing Different Materials:
    • Testing the reflection effects of objects with different materials
    • Hard flat surfaces (such as wooden boards): high measurement accuracy, error about ±1cm
    • Soft materials (such as fabric): weaker reflection, larger measured distances
    • Irregular surfaces: greater fluctuation in measurement results
  3. Testing Different Angles:
    • Testing measurement effects when objects are at different angles relative to the sensor
    • Vertical placement: most accurate measurement
    • Inclined placement (within 15°): increased measurement error but still acceptable
    • Inclined placement (>15°): significantly increased measurement error
  4. Long-term Stability Testing:
    • Running continuously for 30 minutes, recording the stability of measurement results
    • Analyzing the drift of measurement data

The test results show that this ultrasonic distance measurement system has good stability and reliability in normal usage environments, with measurement errors within ±2cm, fully meeting general application requirements.

Adding Distance Value Feedback with Development Board LEDs

Now that we can get distance output from the sensor, we can create interesting applications. I thought of utilizing the 6 LEDs on the development board to provide distance feedback. If the distance is within 10cm, D1 will light up; then for every additional 10cm increase in distance, the next LED in sequence will light up until D6.

Planning to use the 6 LEDs on my DIY development board to provide distance feedback indication

Below is the ultrasonic distance measurement test program I developed, which can measure the distance between objects and the sensor, and provide feedback through LEDs and serial output.

cpp
/**
 * XIAO ESP32C3 Ultrasonic Distance Measurement Program + LED Distance Display
 * Using Grove Ultrasonic Ranger sensor
 * 
 * Author: Lei Feng
 * Date: 2025-03-25
 */

#include "Ultrasonic.h"

// Sensor pin definition
#define ULTRASONIC_PIN 21  // Corresponds to D7/GPIO21

// LED pin definitions - using 6 LEDs on the expansion board
const int LED_PINS[] = {D0, D1, D2, D3, D4, D5};
const int LED_COUNT = 6;

// Create ultrasonic sensor object
Ultrasonic ultrasonic(ULTRASONIC_PIN);

// Define variables
long distance = 0;
unsigned long lastMeasureTime = 0;
const unsigned long MEASURE_INTERVAL = 500; // Measurement interval (milliseconds)

// Distance display configuration
const int DISTANCE_PER_LED = 10; // Each LED represents 10cm
const int MAX_DISPLAY_DISTANCE = LED_COUNT * DISTANCE_PER_LED; // Maximum display distance 60cm

/**
 * Initialization function
 */
void setup() {
  // Initialize serial communication
  Serial.begin(115200);
  
  // Wait for serial connection
  delay(1000);
  Serial.println("\n\nXIAO ESP32C3 - Ultrasonic Distance Measurement System Initialization");
  
  // Initialize LED pins as outputs
  for (int i = 0; i < LED_COUNT; i++) {
    pinMode(LED_PINS[i], OUTPUT);
  }
  
  // LED test - sequentially light up all LEDs
  for (int i = 0; i < LED_COUNT; i++) {
    digitalWrite(LED_PINS[i], HIGH);
    delay(100);
  }
  
  // Turn off all LEDs
  for (int i = 0; i < LED_COUNT; i++) {
    digitalWrite(LED_PINS[i], LOW);
  }
  
  Serial.println("System ready! Starting distance measurement...");
  Serial.println("Distance display: Each LED represents 10cm distance");
  
  // Print CSV header for data recording
  Serial.println("Time(ms),Distance(cm),LED Count");
}

/**
 * Main loop function
 */
void loop() {
  // Timed distance measurement
  unsigned long currentTime = millis();
  if (currentTime - lastMeasureTime >= MEASURE_INTERVAL) {
    lastMeasureTime = currentTime;
  
    // Measure distance
    distance = ultrasonic.MeasureInCentimeters();
  
    // Check if sensor is working properly
    if (distance == 0) {
      Serial.println("Warning: Received distance of 0, please check sensor connection!");
    
      // LED flashing indication when sensor error
      blinkAllLEDs(3, 200);
    } else {
      // Output CSV format data
      Serial.print(currentTime);
      Serial.print(",");
      Serial.print(distance);
      Serial.print(",");
    
      // Calculate number of LEDs to light up
      int ledsToLight = 0;
      if (distance <= MAX_DISPLAY_DISTANCE) {
        ledsToLight = (distance + DISTANCE_PER_LED - 1) / DISTANCE_PER_LED;
      } else {
        ledsToLight = LED_COUNT; // Beyond maximum display distance, light up all LEDs
      }
    
      Serial.println(ledsToLight);
    
      // Light up corresponding number of LEDs based on distance
      updateLEDDisplay(ledsToLight);
    
      // Display detailed measurement information
      Serial.print("Distance: ");
      Serial.print(distance);
      Serial.print(" cm | LED: ");
      Serial.print(ledsToLight);
      Serial.println(" units");
    }
  }
  
  // Brief delay to avoid high CPU usage
  delay(50);
}

/**
 * Update LED display
 * 
 * @param count Number of LEDs to light up
 */
void updateLEDDisplay(int count) {
  // Ensure count is within valid range
  if (count > LED_COUNT) {
    count = LED_COUNT;
  }
  
  // Update LED states
  for (int i = 0; i < LED_COUNT; i++) {
    if (i < count) {
      digitalWrite(LED_PINS[i], HIGH); // Light up
    } else {
      digitalWrite(LED_PINS[i], LOW);  // Turn off
    }
  }
}

/**
 * All LEDs flash simultaneously
 * 
 * @param times Number of flashes
 * @param delayTime Flash interval time (milliseconds)
 */
void blinkAllLEDs(int times, int delayTime) {
  for (int i = 0; i < times; i++) {
    // Light up all LEDs
    for (int j = 0; j < LED_COUNT; j++) {
      digitalWrite(LED_PINS[j], HIGH);
    }
    delay(delayTime);
  
    // Turn off all LEDs
    for (int j = 0; j < LED_COUNT; j++) {
      digitalWrite(LED_PINS[j], LOW);
    }
    delay(delayTime);
  }
}

After running the program, the serial monitor will display the measured distance and the number of LED lights lit, as shown below.

Serial monitor displays the measured distance and number of LED lights

The video below demonstrates the interactive effect of the device changing the number of LEDs based on the measured distance.

Main Issues Encountered During Measurement

During the measurement process, I encountered the following main challenges:

  1. Measurement Instability Issues:
    • In some cases, measurement results would show jumps or abnormal values.
    • This was especially true when measuring irregular surfaces or inclined objects, where measurement results were unstable.
  2. Blind Zone Issue:
    • The sensor cannot accurately measure distances within 2cm (blind zone).
    • Need to avoid placing objects within the blind zone.
  3. Angle Limitations:
    • The sensor's measurement angle is limited (about 15 degrees), objects beyond this range may not be accurately detected.
    • Need to ensure the measured object is within the sensor's detection range.