Ultrasonic sensors for liquid level measurement

I am trying to measure the liquid level in a reservoir. The liquid can either be water or mineral oil ( no vapours expected as application is at temperatures around 30 Deg C.) Required range is from 30 cm to 200 cm.

Been experimenting with the HCRS04- but somehow not very satisfied withe results. The function used is as below :

// FUNCTION TO READ THE ULTRASONIC SENSOR

void readUltrasonic () {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  // Sets the trigPin on HIGH state for 10 micro seconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH);

  // Calculating the distance
  measDistance = (duration / 29.15) / 2;

  // Calculating the water level
  if ( maxDistance > measDistance) {
    waterLevel = maxDistance - measDistance;
    waterLevelPC = (waterLevel * 100 ) / maxDistance;
  }
  else {
    waterLevel = 0;
  }

  // Prints the distance on the Serial Monitor
  Serial.print("Distance in cm : ");
  Serial.println(measDistance);
  Serial.println();
  Serial.print(" WaterLevel in cm : ");
  Serial.println(waterLevel);
  Serial.println();
  Serial.print(" WaterLevel in % : ");
  Serial.println(waterLevelPC);
  Serial.println();
}

Anyone has experience with this or any other better sensors exist ? More important is the HCSR04 suitable for liquid level measurement ??

but somehow not very satisfied withe results

Could you be a little less vague?

:slight_smile:

Inconsistent values - even with a fixed level of water the readings are rolling . Like if its 70cm in reality the values swing between 50 to 120. The other problem is lack of resolution - like I need to change the sensing distance by atleast 25cm to see any change.

Actually its a bit difficult to write what I see as there is no pattern to the same. Maybe I have a dud sensor ?

Maybe you're pinging too frequently and catching returns from earlier pulses.

Impossible to say from the posted snippet.

This is the actual code ( cleaned and removed trial code statements to make it compact )

/*
  2.* Ultrasonic Sensor HC-SR04 used as Level Sensor
  3.*
  4.* R Raghunathan. Code start 12 May 2016
  5.* The speed of sound is 340 m/s or 29.15 microseconds per centimeter.
  6.* So distance in CM = microseconds /29 / 2
  7. The level is displayed on ten LEDs arranged as a vertcial bar Graph. 

  General Formula :
  
  distanceCm= duration*0.034/2;
  distanceInch = duration*0.0133/2;

  14 May 2016 : Code completed 

  
  */

// DEFINE PIN NUMBERS

byte LED_10  = 2;
byte LED_20  = 3;
byte LED_30  = 4;
byte LED_40  = 5;
byte LED_50  = 6;
byte LED_60  = 7;
byte LED_70  = 8;
byte LED_80  = 9;
byte LED_90  = 10;
byte LED_100  = 11;
byte LED_OKState = 12;
byte LED_ERRState = 13;
byte trigPin = A1;
byte echoPin = A0;

// Globals
long duration;
int measDistance;
int waterLevel, waterLevelPC;
int maxDistance = 220;              // This is required for scaling the actual distance as a %
int updateDelay = 5;                // Really not required .. just provided for trials to avoid jumping displays

void setup() {

  Serial.begin(9600);                 // Starts the serial communication
  pinMode(echoPin, INPUT);            // Sets the echoPin as an Input
  pinMode(trigPin, OUTPUT);           // Sets the trigPin as an Output
  pinMode(LED_10, OUTPUT);
  pinMode(LED_20, OUTPUT);
  pinMode(LED_30, OUTPUT);
  pinMode(LED_40, OUTPUT);
  pinMode(LED_50, OUTPUT);
  pinMode(LED_60, OUTPUT);
  pinMode(LED_70, OUTPUT);
  pinMode(LED_80, OUTPUT);
  pinMode(LED_90, OUTPUT);
  pinMode(LED_100, OUTPUT);
  pinMode(LED_OKState, OUTPUT);
  pinMode(LED_ERRState, OUTPUT);
}

void loop() {

  readUltrasonic();

  displayLevel();

  delay (500);                        // Try different values to optimize reponse ...
}

// Functions :::

// FUNCTION TO READ THE ULTRASONIC SENSOR 

void readUltrasonic () {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  // Sets the trigPin on HIGH state for 10 micro seconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH);

  // Calculating the distance
  measDistance = (duration / 29.15) / 2;

  // Calculating the water level
  if ( maxDistance > measDistance) {
    waterLevel = maxDistance - measDistance;
    waterLevelPC = (waterLevel * 100 ) / maxDistance;
  }
  else {
    waterLevel = 0;
  }

  // Prints the distance on the Serial Monitor when connected to PC
  Serial.print("Distance in cm : ");
  Serial.println(measDistance);
  Serial.println();
  Serial.print(" WaterLevel in cm : ");
  Serial.println(waterLevel);
  Serial.println();
  Serial.print(" WaterLevel in % : ");
  Serial.println(waterLevelPC);
  Serial.println();
}

//*******************************************************

// FUNCTION TO DISPLAY THE LEVEL IN LED ARRAY
// ToDo : Why not a Switch case ?? 

void displayLevel () {
   if ( waterLevelPC > 10 ) {
    digitalWrite( LED_10, HIGH);
  }
  else {
    digitalWrite( LED_10, LOW);
  }
  delay(updateDelay);
 
  if ( waterLevelPC > 20 ) {
    digitalWrite( LED_20, HIGH);
  }
  else {
    digitalWrite( LED_20, LOW);
  }
   delay(updateDelay);

  if ( waterLevelPC > 30 ) {
    digitalWrite( LED_30, HIGH);
  }
  else {
    digitalWrite( LED_30, LOW);
  }
   delay(updateDelay);

  if ( waterLevelPC > 40 ) {
    digitalWrite( LED_40, HIGH);
  }
  else {
    digitalWrite( LED_40, LOW);
  }
   delay(updateDelay);

  if ( waterLevelPC > 50 ) {
    digitalWrite( LED_50, HIGH);
  }
  else {
    digitalWrite( LED_50, LOW);
  }
   delay(updateDelay);

  if ( waterLevelPC > 60 ) {
    digitalWrite( LED_60, HIGH);
  }
  else {
    digitalWrite( LED_60, LOW);
  }
   delay(updateDelay);

  if ( waterLevelPC > 70 ) {
    digitalWrite( LED_70, HIGH);
  }
  else {
    digitalWrite( LED_70, LOW);
  }
   delay(updateDelay);

  if ( waterLevelPC > 80 ) {
    digitalWrite( LED_80, HIGH);
  }
  else {
    digitalWrite( LED_80, LOW);
  }
   delay(updateDelay);

  if ( waterLevelPC > 90 ) {
    digitalWrite( LED_90, HIGH);
  }
  else {
    digitalWrite( LED_90, LOW);
  }
   delay(updateDelay);

  if ( waterLevelPC >= 95 ) {
    digitalWrite( LED_100, HIGH);
  }
  else {
    digitalWrite( LED_100, LOW);
  }
   delay(updateDelay);

}

I would suggest an air pressure sensor run a tube to the bottom of the tank the tube needs to have a pressure sensor on the top end (out of the watter) this one looks promising but I am not sure if it would be the best for your application.
MPS20N0040D-D

The tube is attached to the pressure sensor 0-5.9 psi shown above now as the tube with only air inside descends to the bottom of your reservoir (sensor end stays out of the water) at the bottom the air pressure is greater because of the depth of the water. The air tries to escape out the top but the pressure sensor is there and only displaces the water. If the water level drops the tube displaces less water and the pressure decreases. with some calibration you can calculate the depth based on air pressure in the tube.

The MPS20N0040D-D seems to be a novel way with an inverted tube ! Takes out the messiness in having an immersed sensor.

The sealing between the sensing tube and the sensor opening becomes critical as any leak here will undermine the readings. For sure will check this out.

Thanks