HC-SR04 ultrasonic sensor

Hi:

View Image.

Using the HC-SR04 sensor which cost me less than 5 €. I was going to buy the SFR05 for 15 € but I thought better for the price.

Using the code below, so you see, is not very accurate, ie, if I leave the HC-SR04 sensor activated and in a fixed location, such as the photo above, the fixed floor pointing at the ceiling. It analyzes each soda 1 second.

In the variation ranges from 2 inches, sometimes 4. If I had an earthquake understand the variation. I want to know why it vary?

No matter if the code, or just the HC-SR04 sensor is not very accurate as compared to other Ping))), SFR05, SFR08 and some more.

The code I've tried is this:

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
     
    const int trigger=10;
    const int echo=13;
     
    float distance;
    const String nivel_agua = "Nivel de agua:";
     
    void setup(){
      lcd.begin(16,2);
      Serial.begin(9600);
      pinMode(trigger,OUTPUT);
      pinMode(echo,INPUT);
    }
     
    void loop(){
    //Inicializamos el sensor
      digitalWrite(trigger,LOW);
      delayMicroseconds(5);
    // Comenzamos las mediciones
    // Enviamos una señal activando la salida trigger durante 10 microsegundos
      digitalWrite(trigger,HIGH);
      delayMicroseconds(10);
      digitalWrite(trigger,LOW);
    // Adquirimos los datos y convertimos la medida a metros
     distance=pulseIn(echo,HIGH); // Medimos el ancho del pulso
                                  // (Cuando la lectura del pin sea HIGH medira
                                  // el tiempo que transcurre hasta que sea LOW
     distance=distance*0.0001657;
    // Enviamos los datos medidos a traves del puerto serie y al display LCD
      Serial.println("Nivel de agua:");
      Serial.print(distance);
      Serial.println(" Metros.");
      
      lcd.setCursor(0,1);
      lcd.print(distance);
      lcd.print(" Metros.");
      lcd.setCursor(0,0);
      lcd.print(nivel_agua);
      delay(1000);
    }

Sure you could do much better.

A greeting.

first is to try to take the average of 10 measurements

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int trigger=10;
const int echo=13;

float distance;
char nivel_agua[] = "Nivel de agua:";

void setup()
{
  lcd.begin(16,2);
  lcd.setCursor(0,0);
  lcd.print(nivel_agua);
  
  Serial.begin(115200);  // use fastest serial possible
  pinMode(trigger,OUTPUT);
  pinMode(echo,INPUT);
}

void loop()
{
  digitalWrite(trigger,LOW);
  delayMicroseconds(5);
  digitalWrite(trigger,HIGH);
  delayMicroseconds(10);
  digitalWrite(trigger,LOW);
  
  // take average of 10 measurements
  distance = 0.0;
  for (int i=0; i<10; i++)
  {
    distance += pulseIn(echo, HIGH);
  }
  distance = distance*0.001657;  // 10 measurements means another divider!!
  
  Serial.println(nivel_agua);  // reuse char array
  Serial.print(distance, 2);  // two decimal places
  Serial.println(" Metros.");

  lcd.setCursor(0, 1);
  lcd.print(distance);
  lcd.print(" Metros. ");

  delay(1000);
}

give it a try

Hello:

I tried the code should be updated every 100 milliseconds or 1 second. It takes a long time to update and says it measures 18.84 meters.

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int trigger=10;
const int echo=13;

float distance;
char nivel_agua[] = "Water level:";

void setup()
{
  lcd.begin(16,2);
  lcd.setCursor(0,0);
  lcd.print(nivel_agua);
  
  Serial.begin(115200);  // use fastest serial possible
  pinMode(trigger,OUTPUT);
  pinMode(echo,INPUT);
}

void loop()
{
  digitalWrite(trigger,LOW);
  delayMicroseconds(5);
  digitalWrite(trigger,HIGH);
  delayMicroseconds(10);
  digitalWrite(trigger,LOW);
  
  // take average of 10 measurements
  distance = 0.0;
  for (int i=0; i<10; i++)
  {
    distance += pulseIn(echo, HIGH);
  }
  distance = distance*0.001657;  // 10 measurements means another divider!!
  
  Serial.println(nivel_agua);  // reuse char array
  Serial.print(distance, 2);  // two decimal places
  Serial.println(" Meters.");

  lcd.setCursor(0, 1);
  lcd.print(distance);
  lcd.print(" Meters. ");

  delay(100);
}

:wink:

small change, divide the delay over the measurements

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int trigger=10;
const int echo=13;

float distance;
char nivel_agua[] = "Water level:";

void setup()
{
  lcd.begin(16,2);
  lcd.setCursor(0,0);
  lcd.print(nivel_agua);
  
  Serial.begin(115200);  // use fastest serial possible
  pinMode(trigger,OUTPUT);
  pinMode(echo,INPUT);
}

void loop()
{
  digitalWrite(trigger,LOW);
  delayMicroseconds(5);
  digitalWrite(trigger,HIGH);
  delayMicroseconds(10);
  digitalWrite(trigger,LOW);
  
  // take average of 10 measurements
  distance = 0.0;
  for (int i=0; i<10; i++)
  {
    distance += pulseIn(echo, HIGH);
    delay(10); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 10 times 10 millis == 100 millis
  }
  distance = distance*0.001657;  // 10 measurements means another divider!!
  
  Serial.println(nivel_agua);  // reuse char array
  Serial.print(distance, 2);  // two decimal places
  Serial.println(" Meters.");

  lcd.setCursor(0, 1);
  lcd.print(distance);
  lcd.print(" Meters. ");
}

Hello:

It works a bit faster but there is a problem. Must measure 1.38 meters entornno.

Why measuring 15.22 meters?

Greetings.

probably the formula distance = distance * 0.001657 is not right?

speed of sound is 330 meter/second in normal air. (in air above water it is higher due to the humidity - don't know how much)
The pulse time is twice the distance (forth and back).
distance = time/2 * 330/second.
as time is in microseconds
distance = time * 0.000165;

so adjust the formula to match in the order of magnitude. You should now read 1.52 meter while it is 1.38 meter
so we can add a correction factor

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int trigger=10;
const int echo=13;

float distance;
char nivel_agua[] = "Water level:";

void setup()
{
  lcd.begin(16,2);
  lcd.setCursor(0,0);
  lcd.print(nivel_agua);
  
  Serial.begin(115200);  // use fastest serial possible
  pinMode(trigger,OUTPUT);
  pinMode(echo,INPUT);
}

void loop()
{
  digitalWrite(trigger,LOW);
  delayMicroseconds(5);
  digitalWrite(trigger,HIGH);
  delayMicroseconds(10);
  digitalWrite(trigger,LOW);
  
  // take average of 10 measurements
  unsigned long time = 0.0;
  for (int i=0; i<10; i++)
  {
    time += pulseIn(echo, HIGH);
    delay(10);
  }
  distance = time * 0.0001657; 
  
  // adjust for accuracy 
  distance = 1.38/1.52 * distance;  //  correction factor = (wanted/measured)
  
  Serial.println(nivel_agua);
  Serial.print(distance, 2);
  Serial.println(" Meters.");

  lcd.setCursor(0, 1);
  lcd.print(distance, 2);
  lcd.print(" Meters. ");
}

give it a try

You might want to check - Arduino Playground - MultiMap - to create an interpolating lookup table

It works well.
Long time seconds to update the data.

Did some investigation why there was this factor 10 in the code and concluded there is a bug in the code I posted. The sensor needs a handshake to get started and that is not used in the loop (of 10 readings). That caused to effectively make only one reading and resulted in the factor 10 error. That is why you go a factor 10 too much.

put the measurement in a separate function.

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int trigger=10;
const int echo=13;

float distance;
char nivel_agua[] = "Water level:";

void setup()
{
  lcd.begin(16,2);
  lcd.setCursor(0,0);
  lcd.print(nivel_agua);
  
  Serial.begin(115200);  // use fastest serial possible

  pinMode(trigger,OUTPUT);
  pinMode(echo,INPUT);
}

void loop()
{
  // MEASUREMENT
  float distance = getDistance();
 
  // DISPLAY
  Serial.println(nivel_agua);
  Serial.print( millis() );  // add a time stamp
  Serial.print('\t');
  Serial.print(distance, 2);
  Serial.println(" Meters.");

  lcd.setCursor(0, 1);
  lcd.print(distance, 2);
  lcd.print(" Meters. ");
}

float getDistance()
{
  unsigned long time = 0;
  for (int i=0; i<10; i++)
  {
    digitalWrite(trigger,LOW);
    delayMicroseconds(5);
    digitalWrite(trigger,HIGH);
    delayMicroseconds(10);
    digitalWrite(trigger,LOW);
    time += pulseIn(echo, HIGH);
  }
  return  time * 0.00001657 * 1.38/1.52; //  correction factor = (wanted/measured)
}

can you give it a try (maybe adjust the 0.00001657 factor)

  • readings should be more stable with the above code

update: patched error in code (see below)

Error.

sketch_feb14a.ino: In function 'float getDistance()':
sketch_feb14a:49: error: expected `;' before 'time'

    time += pulseIn(echo, HIGH);

:wink:

please patch in your code (it was not tested as I do not have such sensor)

digitalWrite(trigger,LOW) <<<<<<<<<<<<<< missing ; please add
time += pulseIn(echo, HIGH);

(patched in post above)

Hi anyone to help,

I am trying to use the pulsein function with the HC-SR04 ultrasonic sensor and my problem is to distinguish when the sensor is not connected and when its out of range. Using pulsein fucntion in both cases it returns a zero, so my question is how to i return a different value not zero when the sensor is not connected and zero when the sensor is out of range or vice versa.