Đo dòng điện với ESP32: INA219 hay ACS712, đo dòng, điện áp, công suất và mAh không cần thư viện
So sánh INA219 (điện trở shunt, I2C) và ACS712 (cảm biến Hall), nối với ESP32 có cầu phân áp, chương trình đo dòng, điện áp, công suất, điện lượng mAh không cần thư viện, kèm bảng lỗi.
Biết dòng điện thiết bị đang dùng giúp chọn đúng nguồn, ước tính pin dùng được bao lâu, phát hiện động cơ bị kẹt hay bóng đèn hỏng. Hai cảm biến dòng phổ biến nhất cho vi điều khiển là INA219 (đo bằng điện trở shunt, giao tiếp I2C) và ACS712 (đo bằng cảm biến Hall, ra điện áp tương tự). Bài này giải thích hai cách đo, khi nào chọn loại nào, nối cả hai với ESP32 và viết chương trình đo dòng, điện áp, công suất và điện lượng đã dùng (mAh), không cần thư viện.
1. Hai cách đo dòng điện

- Điện trở shunt (INA219): cho dòng điện chạy qua một điện trở rất nhỏ (0,1 Ω), đo điện áp rơi trên nó rồi chia cho điện trở theo định luật Ohm. 0,5 A qua 0,1 Ω chỉ tạo ra 50 mV, nên INA219 có bộ ADC riêng đủ nhạy để đo, rồi gửi kết quả qua I2C. INA219 đo thêm được điện áp phía tải, từ đó tính công suất.
- Cảm biến Hall (ACS712): dòng điện chạy qua một dải đồng bên trong chip, tạo ra từ trường. Cảm biến Hall đặt sát đó đo từ trường và cho ra điện áp: khi không có dòng, điện áp bằng một nửa VCC (2,5 V); mỗi ampe làm điện áp thay đổi một lượng cố định (185 mV/A với loại 5 A). Phần dây tải và phần mạch đo cách ly với nhau, và đo được cả dòng xoay chiều.
| INA219 (module điện trở 0,1 Ω) | ACS712 (loại 5 A) | |
|---|---|---|
| Dải đo | ±3,2 A | ±5 A (có loại 20 A, 30 A) |
| Bước đo nhỏ nhất | 0,1 mA | Khoảng 10–20 mA khi đọc bằng ADC của ESP32 |
| Đo điện áp tải | Có, tới 26 V | Không |
| Giao tiếp | I2C, chạy 3,3 V | Điện áp tương tự, cấp 5 V |
| Dòng xoay chiều | Không | Có (cần lấy mẫu nhanh, tính giá trị hiệu dụng) |
| Hợp với | Đo pin, mạch nhỏ, dòng chờ của thiết bị chạy pin | Động cơ, tải vài ampe, cần cách ly |
2. Module INA219

Chữ R100 trên điện trở nghĩa là 0,100 Ω. Một số module dùng điện trở khác (R010 là 0,01 Ω cho dòng lớn hơn): đọc chữ trên điện trở rồi sửa hằng R_SHUNT_OHM cho đúng. Hai miếng hàn A0, A1 đổi địa chỉ I2C (0x40 đến 0x45) khi cần nối nhiều INA219.
3. Linh kiện cần chuẩn bị
- ESP32 DevKit V1 30 chân (hoặc loại 38 chân).
- Module INA219 và/hoặc module ACS712 loại 5 A; hai điện trở 10 kΩ và 20 kΩ cho cầu phân áp.
- Một tải để đo: dải LED 12 V, quạt nhỏ, hoặc chính mạch chạy pin như mạch ESP32 tích hợp pin 18650.
- Nguồn cho tải; muốn thử với nhiều mức điện áp thì dùng mạch hạ áp LM2596 chỉnh điện áp.
- Dây cắm, breadboard, dây USB nạp code.
4. Sơ đồ nối dây

Phần tín hiệu nối như bảng trong hình. Chân OUT của ACS712 lên tới 5 V khi cấp nguồn 5 V, quá mức chân ADC của ESP32 chịu được, nên phải qua cầu phân áp: OUT → điện trở 10 kΩ → D34 → điện trở 20 kΩ → GND. Điện áp tới D34 chỉ còn 2/3, chương trình nhân lại 1,5. Chân D34 chỉ dùng làm đầu vào và thuộc bộ ADC1, đọc được cả khi bật Wi-Fi.

Phần dòng tải: dây dương từ nguồn đi vào cọc vít VIN+ của INA219, ra VIN−, vào IP+ của ACS712, ra IP−, rồi tới tải. Cực âm của nguồn, của tải và GND của ESP32 nối chung. Chỉ dùng một cảm biến thì bỏ khối còn lại.
5. Chương trình đo dòng, điện áp, công suất, mAh
Chọn bo ESP32 Dev Module rồi nạp. Bài dùng gói esp32 phiên bản 3.3.10, chỉ cần thư viện Wire có sẵn. Khi bật nguồn, để tải không chạy trong 2 giây đầu để chương trình đo điểm 0 của ACS712.
// PYWORLD.VN - Đo dòng điện một chiều bằng INA219 (I2C) và ACS712 (cảm biến Hall) với ESP32, không cần thư viện
// INA219: SDA D21, SCL D22, VCC 3V3; tải nối qua VIN+ -> VIN-. ACS712: VCC 5 V, OUT qua cầu phân áp 10 kΩ / 20 kΩ vào D34
// Lúc khởi động, để tải KHÔNG chạy 2 giây để chương trình đo điểm 0 của ACS712
#include <Wire.h>
const uint8_t DIA_CHI_INA219 = 0x40;
const float R_SHUNT_OHM = 0.1; // điện trở đo dòng trên module (chữ R100 = 0,100 Ω)
const int CHAN_ACS712 = 34;
const float HE_SO_PHAN_AP = 1.5; // (10 kΩ + 20 kΩ) / 20 kΩ: điện áp OUT thật = điện áp đo được × 1,5
const float DO_NHAY_MV_A = 185.0; // ACS712 loại 5 A: 185 mV/A; loại 20 A: 100; loại 30 A: 66
float diemKhongAcsMv = 2500; // điện áp OUT khi dòng bằng 0 (khoảng VCC/2), đo lại lúc khởi động
double dienLuongMah = 0; // điện lượng cộng dồn theo số đo INA219
bool docThanhGhi(uint8_t tg, int16_t &giaTri) {
Wire.beginTransmission(DIA_CHI_INA219);
Wire.write(tg);
if (Wire.endTransmission(false) != 0) return false;
if (Wire.requestFrom(DIA_CHI_INA219, (uint8_t)2) != 2) return false;
giaTri = (int16_t)((Wire.read() << 8) | Wire.read()); // byte cao gửi trước
return true;
}
// Đọc INA219: thanh ghi 0x01 điện áp shunt (bước 10 uV, có dấu), 0x02 điện áp bus (bit 15-3, bước 4 mV).
bool docIna219(float &dongMa, float &dienApV) {
int16_t shunt, bus;
if (!docThanhGhi(0x01, shunt) || !docThanhGhi(0x02, bus)) return false;
float shuntMv = shunt * 0.01f;
dongMa = shuntMv / R_SHUNT_OHM; // I = U / R
dienApV = ((uint16_t)bus >> 3) * 0.004f; // điện áp ở chân VIN- so với GND
return true;
}
// Trung bình nhiều lần đọc để giảm nhiễu của ADC và của chính cảm biến Hall.
float docAcsMv(int soLan = 200) {
uint32_t tong = 0;
for (int i = 0; i < soLan; i++) tong += analogReadMilliVolts(CHAN_ACS712);
return tong / (float)soLan * HE_SO_PHAN_AP;
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
analogSetPinAttenuation(CHAN_ACS712, ADC_11db); // đo được tới khoảng 3,1 V
// Cấu hình mặc định của INA219 (0x399F): dải shunt ±320 mV, ADC 12 bit, đo liên tục
Wire.beginTransmission(DIA_CHI_INA219);
Wire.write(0x00);
Wire.write(0x39);
Wire.write(0x9F);
if (Wire.endTransmission() != 0) Serial.println("Khong thay INA219 o dia chi 0x40: kiem tra SDA, SCL");
delay(1000);
diemKhongAcsMv = docAcsMv(2000);
Serial.printf("Diem 0 cua ACS712: %.0f mV (ly thuyet 2500 mV)\n", diemKhongAcsMv);
}
void loop() {
static uint32_t lanTruoc = millis();
float dongIna, dienAp;
bool coIna = docIna219(dongIna, dienAp);
float dongAcs = (docAcsMv() - diemKhongAcsMv) / DO_NHAY_MV_A * 1000.0f;
uint32_t bayGio = millis();
if (coIna) dienLuongMah += dongIna * (bayGio - lanTruoc) / 3600000.0; // mA × giờ
lanTruoc = bayGio;
if (coIna) {
Serial.printf("INA219: %.2f V, %.1f mA, %.0f mW, da dung %.2f mAh | ACS712: %.0f mA\n", dienAp, dongIna,
dienAp * dongIna, dienLuongMah, dongAcs);
} else {
Serial.printf("INA219: loi doc I2C | ACS712: %.0f mA\n", dongAcs);
}
delay(500);
}
6. Từ số đọc được ra dòng điện

- INA219, thanh ghi 0x01 là điện áp shunt, mỗi đơn vị 10 µV, có dấu (dòng chạy ngược ra số âm). Chia cho 0,1 Ω được dòng điện.
- INA219, thanh ghi 0x02 là điện áp phía tải: bỏ 3 bit thấp (bit trạng thái), nhân 4 mV.
- Chương trình không dùng thanh ghi hiệu chuẩn 0x05 và thanh ghi dòng 0x04 của INA219: tự chia bằng điện trở shunt cho dễ hiểu và dễ đổi khi dùng điện trở khác.
- ACS712: đọc trung bình 200 lần bằng
analogReadMilliVolts()(đã hiệu chỉnh theo từng chip ESP32), nhân 1,5, trừ điểm 0, chia độ nhạy 185 mV/A. - Điện lượng (mAh): mỗi lần đọc, cộng thêm dòng × thời gian trôi qua. Pin 18650 ghi 2000 mAh nghĩa là cấp được khoảng 2000 mA trong 1 giờ.
7. Kết quả trên Serial Monitor

Hai cảm biến cho số gần nhau nhưng không giống hệt: INA219 ổn định tới 0,1 mA, còn số của ACS712 dao động vài chục mA do nhiễu của cảm biến Hall và của ADC. Với dòng nhỏ dưới 100 mA, chỉ nên tin INA219.
8. Đo cho đúng, an toàn
- Không cho dòng vượt dải đo: INA219 với shunt 0,1 Ω tối đa 3,2 A, điện áp tải tối đa 26 V.
- Dây tải dùng dây đủ to và bắt chặt cọc vít: dây nhỏ, tiếp xúc lỏng sẽ nóng và làm sai số đo.
- ACS712 nằm gần nam châm, động cơ, dây dòng lớn khác sẽ bị lệch điểm 0. Đo lại điểm 0 bằng cách khởi động lại khi tải tắt.
- Có thể đặt ACS712 trên dây điện lưới 220 V vì phần đo cách ly, nhưng module bán phổ biến có cọc vít hở và khoảng cách cách điện nhỏ. Đo điện lưới nên dùng thiết bị chuyên dụng như PZEM-004T (bài cùng loạt) và nhờ người có chuyên môn đấu nối.
9. Xử lý lỗi thường gặp
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| "Khong thay INA219 o dia chi 0x40" | Đảo SDA/SCL; đã hàn A0, A1 nên đổi địa chỉ | Quét I2C để biết địa chỉ thật, sửa DIA_CHI_INA219 |
| INA219 báo dòng âm | Đảo VIN+ với VIN− | Dòng phải đi vào VIN+, ra VIN− |
| INA219 báo điện áp 0 V | Không nối GND chung giữa nguồn tải và ESP32 | Nối GND nguồn tải với GND ESP32 |
| ACS712 báo vài trăm mA khi không có tải | Điểm 0 đo lúc tải đang chạy; nguồn 5 V dao động | Khởi động lại khi tải tắt; nguồn 5 V ổn định |
| ACS712 báo sai gấp 1,5 lần | Quên hoặc nối sai cầu phân áp | Kiểm tra điện trở 10 kΩ, 20 kΩ và hằng HE_SO_PHAN_AP |
| ACS712 số liệu nhảy nhiều | ADC nhiễu, ít mẫu | Tăng số lần lấy trung bình; thêm tụ 100 nF từ D34 xuống GND |
10. Xem thêm
- ADC trên ESP32: đo điện áp pin 18650: cầu phân áp và cách đọc ADC.
- ADS1115 đo điện áp 16 bit: khi cần đọc điện áp tương tự chính xác hơn ADC của ESP32.
- Deep Sleep trên ESP32: dùng INA219 để đo dòng chờ khi mạch ngủ.
- Module ADS1115 của shop: đọc chân OUT của ACS712 chính xác hơn.
Xem thêm