Đo mức nước, mức nhiên liệu đang có trong bồn, bể chứa sử dụng cảm biến siêu âm SR04 và Arduino

Đo mức nước, mức nhiên liệu đang có trong bồn, bể chứa sử dụng cảm biến siêu âm SR04 và Arduino

Nếu bạn có 1 bồn chứa nước hoặc nguyên vật liệu đóng kín, bạn sẽ gặp phải yếu tố giống như tôi :

Làm sao để biết lượng nước (nhiện liệu) đang có trong bồn mà không cần mở bồn ra kiểm tra?

Xem thêm :

Đương nhiên là lắp đồng hồ đo rồi!

Dạo quanh chợ 1 vòng thì than ôi, đủ những loại hàng từ cơ đến điện tử, Ngân sách chi tiêu đắt đỏ và hiệu suất cao chưa được kiểm chứng .
Vậy là Quyết Định tự làm 1 bộ đo cho riêng mình – Tiêu chí : Ngon – Bổ – Rẻ .
Bộ đo này sử dụng những module siêu âm có sẵn trên thị trường .

Các bạn có thể sử dụng Module siêu âm chống nước JSN-SR04T hoặc Module siêu âm KHÔNG chống nước HC-SR04.

Code hoạt động giải trí trên cả 2 loại và không cần chỉnh sửa gì cả, ráp đúng chân là chạy .


Module JSN-SR04T – Chống nước


Module HC-SR04 – Không chống nước

Trong bài viết này, mình sẽ lập trình bằng Arduino để đo mức ( level ) nước, nguyên vật liệu :

  • Đơn vị đo “cm”
  • Hiển thị đồng thời qua màn LCD 16×2 dưới dạng thanh báo “%” còn lại và Serial qua máy tính dưới dạng “cm” và có cảnh báo.
  • Hiển thị LCD 16×2 dùng cách mắc trực tiếp – Hơi tốn chân GPIO của board Arduino nhưng đỡ tiền mua module I2C (Nếu không muốn dùng cách này có thể tham khảo bài sau để sử dụng cách module I2C cho tiết kiệm chân)
  • Đo môi trường ngoài không khí. Trong điều kiện khắc nghiệt, độ ẩm cao.

NGUYÊN LÝ HOẠT ĐỘNG

Cảm biến khoảng cách siêu âm SR04 được sử dụng rất phổ cập để xác lập khoảng cách vì RẺ và CHÍNH XÁC .
Cảm biến sử dụng sóng siêu âm và hoàn toàn có thể đo khoảng cách trong khoảng chừng từ 2 -> 300 cm, với độ đúng chuẩn gần như chỉ phụ thuộc vào vào cách lập trình .

  • Cảm biến SR04 có 4 chân là: Vcc, Trig, Echo, GND.
  • Để đo khoảng cách, ta sẽ phát 1 xung rất ngắn (5 microSeconds – us) từ chân Trig. Sau đó, cảm biến sẽ tạo ra 1 xung HIGH ở chân Echo cho đến khi nhận lại được sóng phản xạ ở pin này. Chiều rộng của xung sẽ bằng với thời gian sóng siêu âm được phát từ cảm biển và quay trở lại. 

Tốc độ của âm thanh trong không khí là 340 m / s ( hằng số vật lý ), tương tự với 29,412 microSeconds / cm ( 106 / ( 340 * 100 ) ) .
Khi đã tính được thời hạn, ta sẽ chia cho 29,412 để nhận được khoảng cách .
Các bước thống kê giám sát như sau :

  • Đặt chân TRIG của module lên mức Cao (5V) trong ít nhất 10 μs (microseconds)
  • Sau đó module siêu âm ghi lại thời gian và gửi ra sóng âm tần số 40Khz
  • Sóng siêu âm truyền xuống bề mặt chất lỏng trong bể và phản xạ lại
  • Sóng phản xạ sau đó truyền ngược về đầu dò
  • Module siêu âm nhận được sóng phản xạ và đánh dấu thời gian nhận được sóng phản hồi
  • Cuối cùng, module siêu âm đưa chân ECHO lên mức cao trong khoảng thời gian (microseconds ) phản hồi sóng âm (Gửi đi – nhận về) và tính toán ra khoảng cách.

Kết quả trên chân ECHO có thể được chuyển đổi khá đơn giản về: 58 μs/cm

Vì vậy, nếu chân ECHO lên mức cao trong thời hạn 5800 μs ( 5.8 ms ), thì tất cả chúng ta tính được khoảng cách giữa cảm biến và mức chất lỏng trong bể là :

5800μs / 58μs/cm = 100cm = 1m


Mô phỏng bồn chứa và cách đo

 

VẬT TƯ CẦN THIẾT


Arduino Uno


Module JSN-SR04T hoặc HC-SR04


Đầu dò siêu âm chống nước của module JSN-SR04T

Module siêu âm chống nước JSN-SR04T rất tiện lợi cho những ứng dụng ngoài trời như cảm biến đỗ xe ; đo khoảng cách ngoài trời ; đo mức nước bể cá, bể nước, đo mức nguyên vật liệu xăng, dầu, …

Bảng thông số của module JSN-SR04T mình trích ra từ tài liệu nhà sản xuất: Download


Giải thích :

Điện áp hoạt động DC 3 – 5.5V
Dòng tiêu thụ Khoảng 8mA
Tần số đầu dò 40KHz
Dải đo 20cm – 600cm
Sai số +- 1cm
Độ phân giải 1mm
Góc mở đầu dò 75 độ

Chú ý: 

Vì cảm biến JSN-SR04T có góc mở rất lớn ( >= 75 độ) nên KHÔNG phù hợp đo khoảng cách trong không gian chật hẹp.

Chỉ nên dùng trong không gian mở như ngoài trời hoặc bể chứa kích thước lớn- không vật cản.

Nếu đo trong không gian chật hẹp thì sai số rất lớn do sóng phản xạ lại bị va đập vào vật cản hoặc thành bể – bình chứa.

Ảnh dưới miêu tả góc quét và phản xạ của 1 loại cảm biến siêu âm trên thị trường, tùy loại sẽ có góc khác nhau :


Góc quét và phản xạ của 1 loại cảm biến siêu âm


LCD 16×2


Biến trở

 

SƠ ĐỒ NGUYÊN LÝ

Sơ đồ chân liên kết

MODULE PIN ARDUINO PIN
Trigger pin (Module SR04) Pin 8
Echo Pin (Module SR04) Pin 9
RS Pin (LCD) Pin A4
EN Pin (LCD) Pin A5
D4 Pin (LCD) Pin 5
D5 Pin (LCD) Pin 4
D6 Pin (LCD) Pin 3
D7 Pin (LCD) Pin 2
Những chân còn lại của LCD Nối theo sơ đồ (hĩnh vẽ)
LED xanh Pin 6
LED đỏ Pin 7
5V 5V
GND GND

 

THƯ VIỆN CHO CẢM BIẾN SIÊU ÂM

  • Thư viện NewLiquidCrystal : Download here
  • Thư viện LiquidCrystal : Download here

Hướng dẫn: Tải thư viện về và copy vào thư mục library của Arduino trong Documents

 

CODE

Code Arduino sử dụng thư viện LiquidCrystal, hiển thị đồng thời LCD dạng “%” và Serial đợn vị đo “cm”

Môi trường đo trong không khí, có điều chỉnh mức cảnh báo “dưới ngưỡng” hoặc “vượt ngưỡng” cho phép, tín hiệu cảnh báo “dưới ngưỡng” hoặc “vượt ngưỡng” đưa ra 2 đèn LED (dễ dàng thay bằng còi hú, relay.. tùy bạn chỉnh) và cảnh báo cả trên Serial:

/*
  Arduino Uno Ultrasonic Fuel Gauge / Liquid Level Sensor System
  by Scott Ogrin
  MIT License
*/

// include the library code:
#include 
#include 

#define G_LED 6 // Onboard Green LED
#define R_LED 7  // Onboard Red LED

// Init vars
// **************
//   CHANGE ME!
// **************
const int tankEmptyDepth = 200; // This MUST be no greater than 450 cm (500cm for the HC-SR04)!
const int tankFullDepth = 25; // This should be at least 25 cm, if possible (2cm for the HC-SR04)

// Change the above tankEmptyDepth and tankFullDepth constants to be the distance (in centimeters):
//    - tankEmptyDepth  = between the ultrasonic sensor and the empty level (i.e. bottom of tank)
//    - tankFullDepth   = between the sensor and the liquid when the tank is full (MINIMUM 25cm)
// Note that the ultrasonic sensor works only between 25cm and 450cm, so the min tankFullDepth = 25.
// For my tank, the tankFullDepth = 15, which is okay... BUT it means that when the tank is full, 
// I will probably get incorrect readings until the level drops 10cm. This isn't a problem in my case 
// since I I don't care about accurate level readings when the fuel tank is full! But it means that 
// after getting the tank filled, the level will read near-empty or "Error: Timeout".
//
// You could also use the HC-SR04, which is larger but has a min depth of 2cm and max of 500cm.
// Note however that it's not waterproof! I chose the JSN-SR04T-2.0 for that reason.
//
// Note also that you might want to set tankEmptyDepth to be less than the bottom of your tank,
// esp if you have a vertical feed sucking liquid from, say, 5cm above the bottom of the tank.
// For example, my tank is 163cm deep from the sensor, so I set tankEmptyDepth to 153. This ensures
// that when my LevelMatic reads 0%, I should have 10cm of fuel left in the tank.
//
// If measuring in inches: 1 inch = 2.54 cm

// These vars hold the current and last-measured level from the sensor
int currentLevel = 0;
int lastLevel = 0;

int maximumRange = 190; // Maximum range needed
int minimumRange = 30; // Minimum range needed

// These vars are for showPartialBarChar() and showCurrentLevel()
int done = 0;
char levelTxt[] = "Current level:";

// Var for showError
// This error means the ultrasound unit couldn't do a measurement, and timed out
// Usually that means the sensor is at a weird angle, too close to the liquid, or
// the ultrasound waves are bouncing off the walls of the tank.
char timeoutErrorTxt[] = "ERROR: Timeout";

// Var for echo response from ultrasonic sensor board
unsigned long timeHigh;

// Custom chars for LCD
byte barEmpty[8] = {
  B11111,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B11111,
};
byte barOne[8] = {
  B11111,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B11111,
};
byte barTwo[8] = {
  B11111,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11000,
  B11111,
};
byte barThree[8] = {
  B11111,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11100,
  B11111,
};
byte barFour[8] = {
  B11111,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11110,
  B11111,
};

// constants for ultrasonic sensor board IO pins
const int trigPin = 8, echoPin = 9;

// constants for LCD IO pins
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {

  // Set up IO pins for ultrasonic sensor board
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  // Set trigger pin for sensor to 0 (aka "do nothing yet")
  digitalWrite(trigPin, LOW);

  pinMode(G_LED, OUTPUT); // Use LED indicator (if required)
  pinMode(R_LED, OUTPUT); // Use LED indicator (if required)

  Serial.begin(9600); // Serial monitoring 
  
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);

  // Set custom chars 0-7
  lcd.createChar(0, barEmpty);
  lcd.createChar(1, barOne);
  lcd.createChar(2, barTwo);
  lcd.createChar(3, barThree);
  lcd.createChar(4, barFour);
  lcd.clear();
}

void loop() {
  // Do level scan with ultrasonic board

  // Start a scan - trigger pin must be high for at least 10us
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // Get time echo pin is high using pulseIn, which returns value in microseconds
  // Default timeout of 1s is more than enough for 8 pulses, and we're not in a hurry
  timeHigh = pulseIn(echoPin, HIGH);

  if (timeHigh == 0) {
    // Oops! Timeout...
    showError();
  } else {
    // Calculate level
    // Assume 343 m/s for the speed of the 40kHz sound waves in air at standard temperature and pressure
    // It's 58 us/cm, which we get from:
    //    (343 m/s * (1s / 1000000 us) * (100cm / 1m)) / 2 = 0.01715 cm / us
    // Must divide by 2 because sound wave travels to liquid, and back
    // Invert that to get:
    //    1 / 0.01715 cm/us = 58.309038 us/cm
    // Note resolution of ultrasonic sensor is +/- 0.5cm
    currentLevel = round(timeHigh / 58);
    if (currentLevel > tankEmptyDepth) {
      // If level is lower than empty, show 0%
      // This is useful if you want to have "empty" be "still 10cm of liquid left in tank"
      currentLevel = tankEmptyDepth;
    } else if (currentLevel < tankFullDepth) {
      // If level is higher than full, show 100%
      // This is useful since "full" level may vary when tank is refilled
      currentLevel = tankFullDepth;
    }
    // Don't redraw screen if level is the same as last time
    if (currentLevel != lastLevel) {
      lastLevel = currentLevel;
      showCurrentLevel();
    }
    
  }

 if (currentLevel <= minimumRange){
   /* Send a negative number to computer and Turn LED ON 
   to indicate "out of range" */
   Serial.println("SAFE");
   digitalWrite(R_LED, LOW);
   digitalWrite(G_LED, HIGH);  
 } else if (currentLevel >= maximumRange){
   /* Send the distance to the computer using Serial protocol, and
   turn LED OFF to indicate successful reading. */
   Serial.println("DANGER");
   digitalWrite(R_LED, HIGH);
   digitalWrite(G_LED, LOW); 
 }                       
  else {
   /* Send the distance to the computer using Serial protocol, and
   turn LED OFF to indicate successful reading. */
   Serial.println("NORMAL");
   digitalWrite(R_LED, LOW);
   digitalWrite(G_LED, LOW); 
 }     
    
  // Delay 2s between scans
  delay(2000);
}

void showError() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(timeoutErrorTxt);
}

void showPartialBarChar(int val) {
  switch (val) {
    case 0:
      lcd.write(byte(0)); // barEmpty
      ++done;
      break;
    case 1:
      lcd.write(byte(1)); // one bar
      ++done;
      break;
    case 2:
      lcd.write(byte(2)); // two bars
      ++done;
      break;
    case 3:
      lcd.write(byte(3)); // three bars
      ++done;
      break;
    case 4:
      lcd.write(byte(4)); // four bars
      ++done;
      break;
  }
}

void showCurrentLevel() {
  // Get integer between 0 and 50 for bar graph
  // We have 10 progress bar characters, and each character can have 0-5 vertical columns of pixels
  // Subtracting tankFullDepth gives us a precise ratio between full/empty, as if the ultrasonic sensor
  // would be 0 cm away from the liquid level when the tank is full.
  // Also, currentLevel contains height of "emptiness" above the liquid, so to get liquid level we do:
  //   abs(1 - currentLevel/tankEmptyDepth).
  float ratio = 1 - ((float)currentLevel - (float)tankFullDepth) / ((float)tankEmptyDepth - (float)tankFullDepth);
  ratio = abs(ratio);
  int textLevelInt = round(ratio * 100.0);
  int levelInt = round(ratio * 50.0);
  int fulls = 0;

  // Reset done
  done = 0;

  // Display text above progress bar
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(levelTxt);
  // Display progress bar based on levelInt
  lcd.setCursor(0, 1);
  // Draw progress bar for XX%
  fulls = levelInt / 5;
  if (fulls == 0) {
    // First char on bar is a partial char with 0-4 vertical columns of pixels
    showPartialBarChar(levelInt);
  } else {
    for (int i = 0; i < fulls; ++i) {
      lcd.write(255); // full
      ++done;
    }
  }
  if (done < 10) {
    if (fulls > 0) {
      // Here we may have a partial char with 0-4 vertical columns of pixels
      showPartialBarChar(levelInt - (fulls * 5));
    }
    // Here we may have blank boxes left
    if (done < 10) {
      // We have empty boxes to draw
      for (int i = 0; i < 10 - done; ++i) {
        lcd.write(byte(0)); // barEmpty
      }
    }
  }

  // Lastly, print percentage:
  if (textLevelInt == 100) {
    lcd.setCursor(12, 1);
    lcd.print("100%");
  } else if (textLevelInt < 10) {
    lcd.setCursor(14, 1);
    lcd.print(textLevelInt);
    lcd.print("%");
  } else {
    lcd.setCursor(13, 1);
    lcd.print(textLevelInt);
    lcd.print("%");
  }
    Serial.print("Distance ");
    Serial.print(currentLevel);
    Serial.println("cm");
    delay(100);
    
}
  • Điều chỉnh “%” lượng nước hiển thị trên LCD thông qua 2 dòng này trong code (Các bạn tự điều chỉnh phù hợp với thực tế bể chứa nhà bạn – Nên tính toán sao cho vẫn còn dư 10cm so với ngưỡng cảnh báo để dự phòng khẩn cấp):

—- Khoảng cách từ cảm biến đến mặt nước 200cm- Bể hết-> Hiển thị 0% (Tối đa 600cm – Khuyến nghị dùng trong 250cm) 

const int tankEmptyDepth = 200; 

—- Khoảng cách từ cảm biến đến mặt nước còn 25cm- Bể đầy-> Hiển thị 100% (Tối thiểu 20cm)

const int tankFullDepth = 25; 
  • Điều chỉnh thông số cảnh báo “dưới ngưỡng” hoặc “vượt ngưỡng” ở 2 dòng dưới đây (Các bạn tự điều chỉnh phù hợp với thực tế bể chứa nhà bạn – Nên tính toán sao cho vẫn còn dư 10cm so với ngưỡng cảnh báo để dự phòng khẩn cấp ) :

— – Khoảng cách từ cảm biến đến mặt nước 190 cm – Bể sắp hết -> cảnh báo nhắc nhở ( Tối đa 600 cm – Khuyến nghị dùng tối đa trong khoảng chừng 250 – 450 cm )

int maximumRange = 190; 

— – Khoảng cách từ cảm biến đến mặt nước còn 30 cm – Bể sắp đầy -> cảnh báo nhắc nhở ( Tối thiểu 20 cm )

int minimumRange = 30; 
  • Chân kết nối 2 LED cảnh báo: Pin 6 và Pin 7 của Arduino nối với Anot (+) của mỗi LED (Có thể nối Loa báo động hoặc Relay vào 2 chân này thay cho LED) :
define G_LED 6 // Onboard Green LED – Pin 6
define R_LED 7 // Onboard Red LED – Pin 7

TEST

Mạch hoạt động giải trí khá tốt, khoảng cách đo mức nước, mức nguyên vật liệu tối thiểu 20 cm và tối đa 6 m ( khuyến nghị đo tối đa trong khoảng chừng 2.5 – 4 m ) .

  • Nếu muốn đo mức nhiên liệu chính xác hơn và dùng trong không gian nhỏ, chật hẹp thì nên dùng module HC-SR04
  • Nếu muốn đo trong không gian rộng rãi + yêu cầu chống nước, đo dưới nước, đo trong môi trường khắc nghiệt thì nên dùng module JSC-SR04T


Nguồn: scottiestech.info

5/5 – ( 1 bầu chọn )

Source: https://dvn.com.vn
Category: Phụ Kiện

Alternate Text Gọi ngay