HC-SR04 (ultrasound) vs Sharp GP2Y0A02YK0F (IR) vs VL53L0X (Laser), which solution to choose for distance measurement with Arduino or Raspberry Pi

We will compare the three proximity sensor technologies that make distance measurement possible. HC-SR04 by ultrasound, Sharp GP2Y0A02YK0F by infrared and VL53L0X by measurement of the flight time (ToF) of a laser beam. You will find a 3D printing layout and the Arduino code that will allow you to perform comparative tests at home. You will be able to more easily test these sensors before starting the development of your Arduino, ESP32, ESP8266 or Raspberry Pi projects.

 

The HC-SR04 is a fairly accurate and very easy to use sensor (using one of the many Arduino libraries) but it can be too cumbersome in some projects. It can be replaced very advantageously by a VL53L0X, a sensor that measures the time of flight (Time Of Flight, ToF) of a laser beam. The 3 sensors are all based on the same physical principle. They measure the time required for the return of a beam reflected by an object located in front of the sensor. For the HC-SR04 it will be ultra-sound, infra-red light for the Sharp GP2Y0A02YK0F and a laser beam for the VL53L0X.

All source codes and the ODT file are available on GitHub on this page.

Ultrasonic distance measurement, HC-SR04 sensor

capteur ultrason mesure distance obstacleThe HC-SR04 is an analog sensor that requires two cables. The first (TRIG – Trigger) generates an ultrasound beam. The second is activated as soon as the returned beam is detected. No need to re-develop the wheel, there are several Arduino and Python libraries to integrate the HC-SR04 into your projects. It can be bought for less than one euro.

There are at least 7 libraries available directly from the Arduino IDE Library Manager. For this article, I used the Bifrost library developed by Jeremy Lindsay.

Characteristics of HC-SR04

  • Range of distance measurement: 2cm to 450cm (4.5m)
  • Measurement accuracy: 0.3cm
  • Supply voltage: 5V
  • Digital output: PWM
  • Weight: 9g
  • Dimensions: 45mm x 20mm x 18mm

All specials

Infrared Distance Measurement (IR) Sharp Sensor GP2Y0A02YK0F

The Sharp GP2Y0A02YK0F directly returns an analog signal proportional to the distance. It is quite difficult to get the original version of Sharp. Clones are most often purchased under the reference A02YK0.

After having tested several libraries, the ZSharpIR library developed by zoubworldArduino (GitHub page) gives satisfaction with Chinese clones at less than 4 euros.

Characteristics of Sharp A02YK0

  • Measurement range: from 20cm to 150cm
  • Analog output (signal proportional to distance)
  • Case size: 29.5mm x 13mm x 21.6mm
  • Typical current consumption: 33mA
  • Power Range: 4.5V to 5.5V
  • Output voltage: 0.4V
  • Operating temperature range: -10 ° C to + 60 ° C

All specials

Measurement of distance by laser flight time, VL53L0X sensor (Adafruit or equivalent)

The VL53L0X is a sensor that allows distance measurements to be made by measuring the flight time of a laser. The distance measurement is numeric. It can be retrieved via the I2C bus. Much more compact and more precise than the HC-SR04, this sensor will be much easier to integrate in robotic projects, RC, drones …

I used the library developed by the manufacturer Pololu. It is very compatible with compatible sensors. For this article, I bought a clone for less than €4 on AliExpress.

Remember to remove the (small) protective film from the surface of the sensor.

Features of the VL53L0X

  • Range of distance measurement: up to 200cm (2m)
  • I2c bus: address 0x29
  • Laser beam wavelength: 940nm
  • Card size (excluding connector): 25mm x 13mm (depends on the manufacturer)
  • Power Range: 2.8V to 5.5V

The VL53L1X can reach 400cm. It is harder to get. The VL6180X is dedicated to precision measurement below 10cm. Its price is similar, about €4.

All specials

Important note for operation on Arduino

The use of the Pololu library requires manually starting the wire () function in the setup otherwise the VL53L0X can not be found on the I2C bus.

#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(115200);

  vl53.init();
  vl53.startContinuous();
}

void loop() {
  Serial.println(vl53.readRangeContinuousMillimeters();
  delay(1000);
}

Test setup by 3D printing

To rigorously test the 3 sensors, I have prepared a small support on which we can fix these. The three sensors are positioned in such a way that they measure the same distance. The STL file is downloadable on Thingiverse here.

 

With the 3 sensors installed. From left to right :

  • Sharp GP2Y0A02YK0F (clone)
  • HC-SR04
  • VL53L0X (CJMCU)

Test program

Here is a test program that performs a series of 10 measurements. The delay between each measurement is one second by default. It can be modified using the DELAY_REFRESH constant.

Before you upload the program, you will need to install the following Arduino libraries:

/* Distance measurement comparison with HC-SR04, Sharp GP2Y0A02YK0F and VL53L0X SMTe sensors
&nbsp;* Recording of 10 measurement points. Time between each changeable point with the key DELAY_REFRESH
&nbsp;* Reset between each distance to restart the program

&nbsp;* Manual calibration procedure of the Sharp GP2Y0A02YK0F proximity sensor
&nbsp;* https://projetsdiy.fr/test-and-calibration-of-the-proximity-captor-a02yk0-clone-asiatic-of-sharp-gp2y0a02yk0f/
&nbsp;*
&nbsp;* Test support to print in 3D
 * https://www.thingiverse.com/thing:2961289
 * 
 * Comparaison de mesure de distance avec les capteurs HC-SR04, Sharp GP2Y0A02YK0F et VL53L0X SMTe
 * Enregistrement de 10 points de mesure. Temps entre chaque point modifiable avec la clé DELAY_REFRESH
 * Faire un reset entre chaque distance pour relancer le programme
 * 
 * Procédure d'étalonnage manuel du Sharp GP2Y0A02YK0F
 * https://projetsdiy.fr/test-et-etalonnage-du-capteur-de-proximite-a02yk0-clone-asiatique-du-sharp-gp2y0a02yk0f/
 * 
 * Support de test à imprimer en 3D 
 * https://www.thingiverse.com/thing:2961289
 */

#include <Wire.h>
#include <hcsr04.h>
#include <VL53L0X.h>
#include <MD_MAX72xx.h>
#include <ZSharpIR.h>

#define ir A0
#define model 20150
#define OFFSET_HCSR04     -10
#define PIN_HCSR04_ECHO   4
#define PIN_HCSR04_TRIG   3
#define PIN_GP2Y0A02YK0F  A0
#define DELAY_REFRESH     1000
#define POINT_FILTER      10
#define LONG_RANGE       true

int posFilter = 0;
int filterVl53[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int filterSr04[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int filterIr[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

ZSharpIR SharpIR(ir, model);

HCSR04 hcsr04(PIN_HCSR04_TRIG, PIN_HCSR04_ECHO, 20, 4000);
VL53L0X vl53;

void setup() {
  Wire.begin();
  Serial.begin(115200);

  vl53.init();
  vl53.setTimeout(DELAY_REFRESH - 100);

  #if defined LONG_RANGE
    // lower the return signal rate limit (default is 0.25 MCPS)
    vl53.setSignalRateLimit(0.1);
    // increase laser pulse periods (defaults are 14 and 10 PCLKs)
    vl53.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18);
    vl53.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14);
  #endif
  
  vl53.startContinuous(DELAY_REFRESH - 50);

}

void print(String key, String val, boolean returnline ) {
  Serial.print(key); Serial.print(": ");
  if ( returnline ) {
    Serial.println(val);
  } else {
    Serial.print(val);
  }
}

bool measure = true;

void loop() {
  if ( measure ) {
    float distSR04 = hcsr04.distanceInMillimeters() + OFFSET_HCSR04;
    int distSharp = SharpIR.distance();
    float distVl53 = vl53.readRangeContinuousMillimeters();
    if (vl53.timeoutOccurred()) { Serial.print(" TIMEOUT"); }
    print("HR-SR04 (mm)", String(distSR04), false);
    print(" | IR (mm)", String(distSharp), false);
    print(" | VL53L0X (mm)", String(distVl53), true);
    if ( distVl53 != 8190 ) {
       filterVl53[posFilter] = distVl53;
    }
    
    filterSr04[posFilter] = distSR04;
    filterIr[posFilter] = distSharp;
    posFilter++;

    if (posFilter >= 10 ) {
      Serial.println("# résultats des mesures");
      measure = false;
      for ( int k = 0 ; k <= 10 ; k++ ) {
        //Serial.print(k);
        
        Serial.print(filterSr04[k]); Serial.print(";");
        Serial.print(filterIr[k]); Serial.print(";");
        Serial.println(filterVl53[k]); 
      }

    }
  }  
  delay(DELAY_REFRESH);
}

At the end of each measurement run, the measurement table is displayed. Just import it into a spreadsheet for your calculations. Here is the order of columns:

  • HC-SR04
  • Sharp IR
  • VL53L0X
# résultats des mesures
397;367;425
397;370;426
401;366;435
395;364;424
395;359;431
394;359;435
391;359;422
397;359;429
391;359;419
399;358;424

HC-SR04 test

The first sensor that I propose to test is the ultrasound sensor HC-SR04. As can be seen in the graph, the measurements made with HC-SR04 are very stable. The dispersion of measurements increases progressively with distance. It remains very acceptable even at 1500mm. The measurement gap does not exceed 60mm to 1.5m.

test arduino distance measure hc-SR04

Synthesis of the results of measurements of HC-SR04

The HC-SR04 measurements are very accurate below 500mm. Beyond that, accuracy is sufficient to measure the fill level of a tank.

Distance (mm) Distance measured with HC-SR04 (mm) Delta (mm) Standard deviation (mm)
200 195,3 -4,7 1,25
300 288,8 -11,2 1,69
400 393,3 -6,7 3,53
500 506,9 6,9 6,61
600 574,8 -25,2 2,70
700 669,0 -31,0 3,16
800 761,2 -38,8 4,52
900 860,2 -39,8 1,69
1000 952,1 -47,9 6,61
1100 1 052,2 -47,8 7,19
1200 1 148,1 -51,9 7,09
1300 1 241,5 -58,5 4,48
1400 1 339,5 -60,5 6,22
1500 1 438,1 -61,9 6,49

Sharp GP2Y0A02YK0F test

The first measurement series highlighted the need to manually calibrate the sensor. I tested a clone bought on AliExpress about €3.5. For this comparative test, I used the ZSharpIR library from zoubworldArduino (GitHub page).

sharp GP2Y0A02YK0F zwharp librarie arduino

 

After performing a manual calibration and updating the library, I got much better results. However, I could not exceed 1300mm of measurement. The calibration procedure is detailed step by step in this tutorial.

sharp GP2Y0A02YK0F calibrated arduino

If you need precision in your project, I advise you to stay below 600mm. It is very difficult to correctly calibrate the sensor beyond 1200mm. On the one hand, the signal varies very weakly, on the other hand, the sensor returns a lot of noise. It is best to filter the measurements over a very large number of points to reduce the aberrations.

Distance (mm) Distance measured with a A02YK0 calibrated Delta (mm) Standard deviation (mm)
200 212,0 12,0 0,32
300 283,4 -16,6 1,84
400 384,4 -15,6 7,52
500 481,7 -18,3 11,81
600 606,3 6,3 28,02
700 721,2 21,2 44,27
800 805,9 5,9 59,35
900 954,0 54,0 92,49
1000 1 090,5 90,5 133,90
1100 1 131,6 31,6 128,11
1200 1 196,0 -4,0 127,00
1300 1 221,7 -78,3 31,91
1400 1 385,0 -15,0 123,49
1500 1 412,3 -87,7 84,00

VL53L0X test

It is with the VL53L0X that we will obtain the best results up to 1200mm (1.2m). Beyond, it happens that the sensor can not measure. This results in the return of an aberrant measurement of 8190mm. Luckily, it’s always the same value that is returned by the library (at least in my case). It is therefore very easy to exclude all erroneous measurement points. Beyond 1200mm, about 30% of the measuring points are lost. It depends very much on the surface. The more reflective the surface, the better the measurement accuracy. Feel free to share your experiences in the comments

If you have projects that require more precision, you can opt for his little brother, the VL6180X. Its price is similar. It allows measurements below 10cm.

test arduino distance measure vl53l0X

Summary of measurement results of VL53L0X

The measurements of the VL53L0X are very accurate up to 800mm. From 800 to 1200 mm, the accuracy is good. Beyond that, the sensor approximately 30% of the measurements. It all depends on the surface of the object and the ambient lighting.

Distance (mm) Distance measured with  VL53L0X (mm) Delta (mm) Standard deviation (mm)
200 211,8 11,8 1,81
300 307,7 7,7 3,16
400 415,5 15,5 3,03
500 515,1 15,1 5,93
600 610,7 10,7 7,23
700 729,0 29,0 5,96
800 801,0 1,0 11,24
900 858,6 -41,4 24,39
1000 977,3 -22,7 17,99
1100 1 080,3 -19,7 19,79
1200 1 149,3 -50,7 50,11
1300 1 221,8 -78,2 39,89
1400 1 347,3 -52,7 42,06
1500 1 371,9 -128,1 45,45

Synthesis, which solution to choose to measure a distance with an Arduino or a Raspberry Pi

Sensors HC-SR04 and Sharp GP2Y0A02YK0F require a supply voltage of 5V. It will be necessary to use a circuit of conversion of tension (approximately 0,80 €) for the projects Raspberry Pi, ESP8266 and ESP32. The VL53L0X accepts a supply voltage of between 3V and 5V. It can be used directly in all projects.

Caracteristics HC-SR04 Sharp A02YK0 VL53L0X
Technologie Ultra-sound Infra-red Laser beam ar 940nm
Power supply (V) 5V 4.5V to 5.5V 3V to 5V
I/O 2 PWM Analogic I2C
Minimale distance (cm) 2 20
Maximale distance (cm) 450 150 200
Measurement gap at  500mm* 7 mm 18 mm 6 mm
Measurement gap 1m* 48 mm 90 mm 23 mm
Measurement gap 1,5m* 62 mm 88 mm 128 mm
Arduino librarie recommended HC-SR04 from Jeremy Lindsay ZSharpIR de zoubworldArduino

Manuel calibration (tutorial)

Pololu librarie
Technical documentation Sharp STMicroElectronic
Acheter Compare prices Compare prices Compare prices

(*) Unofficial data given for indicative purposes obtained from an average of 10 measurements.

In terms of performance and measurement quality, the HC-SR04 and VL53L0X give the best results. The HC-SR04 is even better than the long-range laser. The power of the beam is certainly too weak to measure correctly beyond 1200m.

HC-SR04 vs Sharp GP2YAO2 vs VL53L0X

HC-SR04 (Ultrasound) vs Sharp GP2Y0A02YK0F (IR) vs VL53L0X (Laser)

Conclusion: which technology to choose?

Now, you will definitely want to know which sensor to choose. There is no ready answer. Everything will depend on your project. Here are several criteria to consider

  • Environment: light intensity, obstacles (ultrasonic diffraction)
  • Surfaces: reflection index (US, IR, Laser), absorption rate, material, texture (paint …)
  • Geometry: some shapes (angles) will diffract more or less the incident beam and make it undetectable. This is the principle used for stealth aircraft 🙂

It is best to test with these different technologies and choose the one that gives the best result.

Project: measure the level of a water tank

I now propose a little fun project that you can reuse in an automatic garden watering project. For the demo, I used a measuring cup with a capacity of one liter. To visualize the fill rate of the tank, use an array of LEDs (4 blocks). The tank measuring less than 20cm, it will not be possible to use the Sharp A02YK0 sensor which is sensitive only in the range 20 to 150cm.

Niveau d'un réservoir d'eau - Comparaison HC-SR04 (ultrason) VL53L0X (Temps de vol laser)

 

Arduino Code

The Arduino code is long enough to explain in detail. On the main lines :

  • Libraries used
  • To use a matrix of LEDs of 4 * 64 points, one can use the library MX_MAX72xx which makes it possible to manage the matrix like one and the same element. Here is the configuration
    • #define HARDWARE_TYPE MD_MAX72XX :: FC16_HW (Pololu type does not work on Chinese generic matrices)
    • #define MAX_DEVICES 4. Here we have 4 blocks of 8×8 pixels
    • MD_MAX72XX mx = MD_MAX72XX (HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES). It is imperative
    • to initialize the controller by specifying all the pins, otherwise it does not work
  • OFFSET_HCSR04 adjusts the origin of the sensor relative to the VL53L0X
#include <Wire.h>
#include <hcsr04.h>
#include <VL53L0X.h>
#include <MD_MAX72xx.h>

#define OFFSET_HCSR04     -10
#define PIN_HCSR04_ECHO   4
#define PIN_HCSR04_TRIG   3
#define PIN_GP2Y0A02YK0F  A0
#define DELAY_REFRESH     2000
#define POINT_FILTER      10

// Define the number of devices we have in the chain and the hardware interface
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES  4

/*
  pin 12 is connected to the DataIn
  pin 11 is connected to the CLK
  pin 10 is connected to LOAD
*/
const uint8_t CLK_PIN = 12;   // or SCK
const uint8_t DATA_PIN = 11;  // or MOSI
const uint8_t CS_PIN = 10;    // or SS

int posFilter = 0;
int countFilter = 0;
int filterVl53[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int filterSr04[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

HCSR04 hcsr04(PIN_HCSR04_TRIG, PIN_HCSR04_ECHO, 20, 4000);
VL53L0X vl53;

void setup() {
  Wire.begin();
  Serial.begin(115200);

  // Initialise et efface la matrice de Led - Init dot matrix display and clear it
  Serial.println("Init and clear MX Panel");
  mx.begin();
  mx.clear();

  // Initialise le capteur VL53L0X - Init VL53L0X laser sensor
    // Scan i2c bus
  Serial.println("Scan i2c");
  Serial.println(I2CScanner());
  //delay(500);
  
  Serial.println("Init ToF VL53L0X sensor");
  vl53.init();
  vl53.setTimeout(DELAY_REFRESH - 100);
  vl53.startContinuous(DELAY_REFRESH - 100);


}

void loop() {
  float distSR04 = hcsr04.distanceInMillimeters() + OFFSET_HCSR04;
  float distSharp = getDistanceSharp();
  float distVl53 = vl53.readRangeContinuousMillimeters();
  float levelSR04 = tankLevel(distSR04);
  float levelSharp = tankLevel(distSharp);
  float levelVl53 = tankLevel(distVl53);

  print("HR-SR04 (mm)", String(distSR04), false);
  print(" | IR (mm)", String(distSharp), false);
  print(" | VL53L0X (mm)", String(distVl53), true);

  updateDotMatrix(levelVl53, levelSR04, levelSharp);

  delay(DELAY_REFRESH);
}

void print(String key, String val, boolean returnline ) {
  Serial.print(key); Serial.print(": ");
  if ( returnline ) {
    Serial.println(val);
  } else {
    Serial.print(val);
  }
}

float getDistanceSharp() {
  int a0 = analogRead(PIN_GP2Y0A02YK0F);
  Serial.println(a0);
  float dist = 0.002421276045 * a0 * a0 - 3.024416502 * a0 + 114.7941861;
  //float dist = 0.0001936428309 * a0 * a0 + 0.06987226424 * a0 - 14.32575223;
  if ( dist < 20 ) {
    dist = 20;
  }

  return dist;
}

void updateDotMatrix(float vl53, float sr04, float ir) {
  filterVl53[posFilter] = vl53;
  filterSr04[posFilter] = sr04;
  posFilter++;
  if ( posFilter > 10 ) { posFilter = 0;}
  countFilter++;
  
  if (countFilter > 10 ) {
    vl53 = average(filterVl53,POINT_FILTER);
    sr04 = average(filterSr04,POINT_FILTER);

    print("SR04(%)", String(sr04), false);
    print(" | VL53L0X(%)", String(vl53), true);

    int dotVl53 = round(vl53 / 3.125);
    int dotSr04 = round(sr04 / 3.125);
    
    mx.clear();
    mx.update(MD_MAX72XX::OFF);
    for ( int i = 32 ; i >= 32 - dotVl53 ; i--) {
      mx.setPoint(0, i, true);
      mx.setPoint(1, i, true);
      mx.setPoint(2, i, true);
    }
    for ( int i = 32 ; i >= 32 - dotSr04 ; i--) {
      mx.setPoint(5, i, true);
      mx.setPoint(6, i, true);
      mx.setPoint(7, i, true);
    }
    mx.update(MD_MAX72XX::ON);
  }
}

float average (int * array, int len)  // assuming array is int.
{
  long sum = 0L ;  // sum will be larger than an item, long for safety.
  for (int i = 0 ; i < len ; i++)
    sum += array [i] ;
  return  ((float) sum) / len ;  // average will be fractional, so float may be appropriate.
}

float tankLevel(float dist) {
  float tankLev = map(dist, 150, 20, 0, 100);
  //Serial.print("Tank level (%)");
  //Serial.println(tankLev);
  return tankLev;
}

String I2CScanner()
{
  byte error, address;
  int nDevices;
  String s;

  s = "Scanning:\n";

  nDevices = 0;
  for (address = 1; address < 127; address++ )
  {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0)
    {
      //s+="\n";
      s += "I2C device found at address 0x";
      if (address < 16)
        s += "0";
      s += String(address, HEX);
      s += "\n";

      nDevices++;
    }
    else if (error == 4)
    {
      s += "Unknow error at address 0x";
      if (address < 16)
        s += "0";
      s += String(address, HEX);
      s += "\n";
    }
  }
  if (nDevices == 0)
    s += "No I2C devices found\n";
  else
    s += "done\n";
  return s;
}

Demo video

Nothing better than a small demonstration video

That’s it, this comparative study is now complete, it’s up to you to choose the sensor that best suits your project. I was very pleasantly surprised by the performance of the two HC-SR04 and VL53L0X sensors. To monitor the fill level of a tank, the HC-SR04 is more efficient than the laser. Water is a medium that must absorb more laser radiation than ultrasound. If you have autonomous or RC car projects, I recommend the VL53L0X, much more compact and very simple to implement. On the price side, count about €3.50 for the VL53L0X and less than €1 for the HC-SR04. Remember to take into account the medium, the surface and the shape of the object. It is best to test with these different technologies and choose the one that gives the best result.

Subscribe to the weekly newsletter

No spam and no other use will be made of your email. You can unsubscribe anytime.

DIY Projects