Vòng dòng 4-20 mA là gì và cách đọc cảm biến 4-20 mA bằng ESP32
Giải thích vòng dòng 4-20 mA: vì sao dùng dòng điện, vì sao bắt đầu từ 4 mA, cách đổi ra giá trị đo và ngưỡng báo lỗi. Hướng dẫn đọc bằng ESP32 qua điện trở shunt 120 Ω, với ADC có sẵn và với ADS1115.
Vòng dòng 4-20 mA là cách truyền một giá trị đo đi xa lâu đời và phổ biến nhất trong công nghiệp: cảm biến áp suất, mức, nhiệt độ, lưu lượng phần lớn đều có ngõ ra 4-20 mA. Khác với các chuẩn còn lại trong loạt bài này, đây là tín hiệu tương tự: độ lớn của dòng điện chính là giá trị đo. Bài viết giải thích vì sao dùng dòng điện thay cho điện áp, vì sao bắt đầu từ 4 mA, cách đổi dòng điện thành giá trị đo, và cách đọc tín hiệu này bằng ESP32 qua một điện trở shunt, với ADC có sẵn hoặc với module ADS1115.
1. Vòng dòng 4-20 mA hoạt động thế nào?
- Cảm biến loại hai dây mắc nối tiếp với nguồn 24 V một chiều. Nó tự điều chỉnh dòng điện chạy qua mình theo giá trị đo: 4 mA ở đầu thang, 20 mA ở cuối thang.
- Trong một vòng nối tiếp, dòng điện ở mọi điểm bằng nhau. Dây dài, điện trở dây lớn hay mối nối kém đều không làm đổi dòng điện, miễn là nguồn còn đủ điện áp. Tín hiệu điện áp thì bị sụt dọc đường dây.
- Dòng điện ít bị nhiễu điện từ ảnh hưởng hơn điện áp, nên dây đi được hàng trăm mét trong nhà xưởng.
- Chính dòng 4 mA tối thiểu là nguồn nuôi cảm biến, nên chỉ cần hai dây cho cả nguồn và tín hiệu.
- Bên nhận đặt một điện trở shunt trong vòng và đo điện áp trên nó: U = I × R.
2. Từ dòng điện ra giá trị đo
- Công thức: giá trị đo = nhỏ nhất + (dòng − 4) / 16 × (lớn nhất − nhỏ nhất), với dòng tính bằng mA.
- Ví dụ cảm biến áp suất 0 đến 10 bar: 12 mA ứng với 50 %, tức 5 bar.
- Vì sao bắt đầu từ 4 mA? Nếu đầu thang là 0 mA thì không phân biệt được "giá trị đo bằng 0" với "đứt dây". Với 4 mA là điểm không, dòng 0 mA chắc chắn là sự cố.
- Ngưỡng báo lỗi: theo quy ước phổ biến, dòng dưới 3,6 mA hoặc trên 21 mA nghĩa là đứt dây, mất nguồn, chập mạch hoặc cảm biến tự báo hỏng.
3. Chọn điện trở shunt cho ESP32
| Điện trở shunt | Điện áp ở 4 mA | Điện áp ở 20 mA | Nhận xét |
|---|---|---|---|
| 250 Ω | 1,00 V | 5,00 V | Chuẩn công nghiệp cho ngõ vào 1 đến 5 V; quá cao cho ESP32 |
| 150 Ω | 0,60 V | 3,00 V | Sát giới hạn của ADC, phần trên thang đo kém chính xác |
| 120 Ω | 0,48 V | 2,40 V | Nằm trong vùng ADC của ESP32 đo tốt; bài dùng giá trị này |
| 100 Ω | 0,40 V | 2,00 V | Dùng được, độ phân giải thấp hơn một chút |
- Dùng điện trở sai số 1 % hoặc tốt hơn: sai số của điện trở shunt đi thẳng vào kết quả đo.
- Ở 20 mA, điện trở 120 Ω tiêu tán 48 mW; loại 0,25 W là đủ khi làm việc bình thường.
- Điện trở shunt làm sụt bớt điện áp của vòng (2,4 V ở 20 mA). Nguồn 24 V thừa sức bù phần này cho hầu hết cảm biến.
4. Chuẩn bị
- Một mạch ESP32 DevKit V1 30 chân (hoặc bản dùng chip CH340).
- Điện trở 120 Ω loại 1 %, điện trở 10 kΩ, tụ 100 nF.
- Để thử khi chưa có cảm biến: một biến trở 1 kΩ và một điện trở 47 Ω.
- Cho chương trình 2: một module ADS1115.
- Breadboard và dây cắm.
- Arduino IDE 2.x với gói esp32 by Espressif Systems (bài dùng phiên bản 3.3.10). Không cần cài thêm thư viện.
5. Sơ đồ nối
- Mạch thử: biến trở 1 kΩ nối tiếp điện trở 47 Ω từ chân 3V3 tới điểm X. Vặn biến trở, dòng qua điện trở shunt thay đổi từ khoảng 2,8 mA đến 19,8 mA, đủ để thử cả dải đo và ngưỡng báo đứt dây.
- Với cảm biến thật: bỏ mạch thử. Cực dương nguồn 24 V nối cực dương cảm biến; cực âm cảm biến nối điểm X; cực âm nguồn 24 V nối GND của ESP32.
- Điện trở 10 kΩ hạn dòng vào chân ADC nếu điểm X lỡ vượt 3,3 V; tụ 100 nF cùng với nó tạo thành bộ lọc nhiễu.
- Mạch này không cách ly: GND của ESP32 nối chung với cực âm nguồn 24 V.
6. Chương trình 1: đọc bằng ADC của ESP32
/*
PYWORLD.VN - Vòng dòng 4-20 mA với ESP32: đọc dòng điện qua điện trở shunt 120 ôm bằng ADC, đổi sang giá trị đo
Điện áp trên điện trở shunt đưa vào D34 qua điện trở 10 kΩ; 4 mA cho 0,48 V và 20 mA cho 2,40 V
Dòng dưới 3,6 mA được báo là đứt dây hoặc cảm biến lỗi, trên 21 mA là quá dòng
*/
#define CHAN_DO 34
const float DIEN_TRO_SHUNT = 120.0; // Ôm; nên dùng điện trở sai số 1 %
const float DONG_MIN_MA = 4.0; // Ứng với giá trị đo nhỏ nhất
const float DONG_MAX_MA = 20.0; // Ứng với giá trị đo lớn nhất
const float NGUONG_DUT_DAY_MA = 3.6;
const float NGUONG_QUA_DONG_MA = 21.0;
const float GIA_TRI_MIN = 0.0; // Dải đo của cảm biến, ví dụ cảm biến áp suất 0 đến 10 bar
const float GIA_TRI_MAX = 10.0;
const char *DON_VI = "bar";
const int SO_MAU = 64;
// Lấy trung bình nhiều mẫu để giảm nhiễu; mV chia cho ôm ra mA
float docDongMa() {
uint32_t tongMv = 0;
for (int i = 0; i < SO_MAU; i++) {
tongMv += analogReadMilliVolts(CHAN_DO);
delayMicroseconds(500);
}
float dienApMv = (float)tongMv / SO_MAU;
return dienApMv / DIEN_TRO_SHUNT;
}
void setup() {
Serial.begin(115200);
analogSetPinAttenuation(CHAN_DO, ADC_11db);
Serial.printf("\nVong dong 4-20 mA, shunt %.0f ohm: %.1f mA = %.1f %s, %.1f mA = %.1f %s\n",
DIEN_TRO_SHUNT, DONG_MIN_MA, GIA_TRI_MIN, DON_VI, DONG_MAX_MA, GIA_TRI_MAX, DON_VI);
}
void loop() {
float dongMa = docDongMa();
if (dongMa < NGUONG_DUT_DAY_MA) {
Serial.printf("Dong %.2f mA: DUT DAY hoac cam bien mat nguon\n", dongMa);
} else if (dongMa > NGUONG_QUA_DONG_MA) {
Serial.printf("Dong %.2f mA: QUA DONG, cam bien bao loi hoac bi chap\n", dongMa);
} else {
// Nội suy tuyến tính: 4 mA là 0 %, 20 mA là 100 %
float tiLe = (dongMa - DONG_MIN_MA) / (DONG_MAX_MA - DONG_MIN_MA);
tiLe = constrain(tiLe, 0.0f, 1.0f);
float giaTri = GIA_TRI_MIN + tiLe * (GIA_TRI_MAX - GIA_TRI_MIN);
Serial.printf("Dong %.2f mA | %.1f %% | %.2f %s\n", dongMa, tiLe * 100.0f, giaTri, DON_VI);
}
delay(500);
}
Sửa GIA_TRI_MIN, GIA_TRI_MAX và DON_VI theo dải đo của cảm biến đang dùng. ADC của ESP32 có sai số vài phần trăm và hơi phi tuyến; với phép đo cần chính xác hơn, dùng chương trình 2. Nền tảng về ADC có trong bài đọc ADC trên ESP32.
7. Chương trình 2: đọc bằng ADS1115
Nối chân A0 của ADS1115 vào điểm X (vẫn qua điện trở 10 kΩ), VDD vào 3V3, GND vào GND, SDA vào D21, SCL vào D22. Ở thang ±4,096 V mỗi bước của ADS1115 là 0,125 mV, ứng với khoảng 1 µA trên điện trở 120 Ω.
/*
PYWORLD.VN - Vòng dòng 4-20 mA với ESP32 và ADS1115: đo chính xác hơn bằng bộ chuyển đổi 16 bit qua I2C
Điện áp trên điện trở shunt 120 ôm đưa vào chân A0 của ADS1115; mỗi bước của kết quả là 0,125 mV, tức khoảng 1 µA
ADS1115: VDD = 3V3, SDA = D21, SCL = D22, ADDR để trống (địa chỉ 0x48); đọc thanh ghi trực tiếp bằng thư viện Wire
*/
#include <Wire.h>
#define SDA_PIN 21
#define SCL_PIN 22
const uint8_t DIA_CHI_ADS = 0x48;
const uint8_t THANH_GHI_KET_QUA = 0x00;
const uint8_t THANH_GHI_CAU_HINH = 0x01;
const uint16_t CAU_HINH_DO_A0 = 0xC383; // Đo một lần kênh A0 so với GND, thang ±4,096 V, 128 mẫu mỗi giây
const float MV_MOI_BUOC = 0.125;
const float DIEN_TRO_SHUNT = 120.0;
const float DONG_MIN_MA = 4.0;
const float DONG_MAX_MA = 20.0;
const float NGUONG_DUT_DAY_MA = 3.6;
const float NGUONG_QUA_DONG_MA = 21.0;
const float GIA_TRI_MIN = 0.0; // Dải đo của cảm biến
const float GIA_TRI_MAX = 10.0;
const char *DON_VI = "bar";
// Trả về false nếu ADS1115 không trả lời
bool docDienApMv(float *dienApMv) {
Wire.beginTransmission(DIA_CHI_ADS);
Wire.write(THANH_GHI_CAU_HINH);
Wire.write((uint8_t)(CAU_HINH_DO_A0 >> 8));
Wire.write((uint8_t)(CAU_HINH_DO_A0 & 0xFF));
if (Wire.endTransmission() != 0) {
return false;
}
delay(10); // Một lần đo ở 128 mẫu mỗi giây mất khoảng 8 ms
Wire.beginTransmission(DIA_CHI_ADS);
Wire.write(THANH_GHI_KET_QUA);
if (Wire.endTransmission(false) != 0 || Wire.requestFrom(DIA_CHI_ADS, (uint8_t)2) != 2) {
return false;
}
uint8_t byteCao = Wire.read();
uint8_t byteThap = Wire.read();
int16_t ketQua = (int16_t)(((uint16_t)byteCao << 8) | byteThap);
*dienApMv = ketQua * MV_MOI_BUOC;
return true;
}
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN, 100000);
Serial.printf("\nVong dong 4-20 mA voi ADS1115, shunt %.0f ohm\n", DIEN_TRO_SHUNT);
}
void loop() {
// Trung bình 8 lần đo
float tongMv = 0;
for (int i = 0; i < 8; i++) {
float dienApMv = 0;
if (!docDienApMv(&dienApMv)) {
Serial.println("Khong doc duoc ADS1115: kiem tra day SDA, SCL va nguon");
delay(1000);
return;
}
tongMv += dienApMv;
}
float dienApMv = tongMv / 8;
float dongMa = dienApMv / DIEN_TRO_SHUNT;
if (dongMa < NGUONG_DUT_DAY_MA) {
Serial.printf("%.1f mV | dong %.3f mA: DUT DAY hoac cam bien mat nguon\n", dienApMv, dongMa);
} else if (dongMa > NGUONG_QUA_DONG_MA) {
Serial.printf("%.1f mV | dong %.3f mA: QUA DONG, cam bien bao loi hoac bi chap\n", dienApMv, dongMa);
} else {
float tiLe = constrain((dongMa - DONG_MIN_MA) / (DONG_MAX_MA - DONG_MIN_MA), 0.0f, 1.0f);
float giaTri = GIA_TRI_MIN + tiLe * (GIA_TRI_MAX - GIA_TRI_MIN);
Serial.printf("%.1f mV | dong %.3f mA | %.2f %% | %.3f %s\n", dienApMv, dongMa, tiLe * 100.0f, giaTri, DON_VI);
}
delay(500);
}
Chương trình đọc thẳng thanh ghi của ADS1115 bằng thư viện Wire. Cách dùng thư viện có sẵn cho module này có trong bài đo điện áp 16 bit bằng ADS1115.
8. Kết quả mong đợi
- Mở Tools > Serial Monitor ở 115200 baud và vặn biến trở: dòng điện, phần trăm và giá trị đo thay đổi theo.
- Vặn biến trở về phía điện trở lớn nhất: dòng xuống dưới 3,6 mA và chương trình báo đứt dây.
- Rút dây khỏi điểm X: dòng về gần 0 mA, cũng được báo là đứt dây.
- So hai chương trình: kết quả từ ADS1115 ổn định hơn và có nhiều chữ số có nghĩa hơn.
9. Lưu ý an toàn và độ chính xác
- Điện áp ở điểm X: bình thường không quá 2,4 V. Nhưng nếu cảm biến bị chập, toàn bộ 24 V đặt lên điện trở shunt: điện trở nóng lên rất nhanh và điểm X lên 24 V. Thiết bị dùng thật cần cầu chì hoặc nguồn có giới hạn dòng, và một đi-ốt ổn áp 3,3 V ở chân ADC sau điện trở 10 kΩ.
- Nối đúng cực tính nguồn 24 V và kiểm tra bằng đồng hồ trước khi nối vào ESP32.
- Hiệu chuẩn: để đo chính xác, đo giá trị thật của điện trở shunt bằng đồng hồ và sửa hằng
DIEN_TRO_SHUNT. - Môi trường công nghiệp: đường dây dài có thể mang xung quá áp. Bộ thu thương mại có thêm phần chống sét lan truyền và cách ly; mạch trong bài chỉ dùng để học và thử nghiệm.
10. Xử lý lỗi thường gặp
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| Luôn báo đứt dây | Ngược cực cảm biến, thiếu nguồn 24 V, chưa nối chung GND | Đo dòng trong vòng bằng đồng hồ; kiểm tra cực tính và dây GND |
| Dòng đọc được thấp hơn thực tế | Giá trị điện trở shunt lớn hơn khai báo, hoặc sai số ADC | Đo lại điện trở shunt; so với đồng hồ; dùng ADS1115 |
| Số đo nhảy lung tung | Nhiễu trên dây dài, thiếu tụ lọc | Thêm tụ 100 nF, tăng số mẫu lấy trung bình, dùng dây xoắn có chống nhiễu |
| Dòng không lên tới 20 mA | Nguồn vòng không đủ điện áp cho cảm biến cộng dây và điện trở shunt | Dùng nguồn 24 V; giảm điện trở shunt |
| Không đọc được ADS1115 | Sai dây SDA, SCL hoặc sai địa chỉ | Kiểm tra dây; chân ADDR để trống hoặc nối GND cho địa chỉ 0x48 |
| Giá trị đo sai tỉ lệ | Khai báo sai dải đo của cảm biến | Sửa GIA_TRI_MIN, GIA_TRI_MAX theo nhãn cảm biến |
11. Xem thêm
- Đọc ADC trên ESP32 và đo điện áp bằng cầu phân áp.
- Đo điện áp 16 bit bằng module ADS1115 với ESP32.
- Mạch hạ áp LM2596 và MP1584: lấy 5 V cho ESP32 từ chính nguồn 24 V của vòng dòng.
Xem thêm