Đ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

So sánh hai cách đo dòng: INA219 đo điện áp rơi trên điện trở 0,1 ohm bằng ADC 12 bit; ACS712 có dây dẫn chạy qua chip, cảm biến Hall đo từ trường và cho ra điện áp VCC/2 cộng 0,185 V mỗi ampe
INA219 đo điện áp rơi trên điện trở nhỏ; ACS712 đo từ trường do dòng điện tạo ra (hình minh họa).
  • Đ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ất0,1 mAKhoảng 10–20 mA khi đọc bằng ADC của ESP32
Đo điện áp tảiCó, tới 26 VKhông
Giao tiếpI2C, chạy 3,3 VĐiện áp tương tự, cấp 5 V
Dòng xoay chiềuKhôngCó (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

Module INA219 màu xanh có điện trở shunt ghi R100, chip INA219, các chân Vcc, Gnd, Scl, Sda, Vin+, Vin- và hai lỗ cọc vít Vin+, Vin-
Module INA219: điện trở shunt ghi R100 (0,100 Ω) nằm giữa hai cọc vít Vin+, Vin−. Ảnh: oomlout, CC BY-SA 2.0, Wikimedia Commons; đã cắt, thu nhỏ, làm mờ logo hãng.

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ị

4. Sơ đồ nối dây

Sơ đồ nối INA219 với ESP32: VCC vào 3V3, SCL vào D22, SDA vào D21; ACS712: VCC vào VIN, OUT qua cầu phân áp vào D34, GND chung
INA219 dùng I2C; ACS712 đưa ra điện áp, qua cầu phân áp vào D34 (hình minh họa).

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.

Mạch đo dòng một tải: nguồn 12 V cực dương qua INA219 từ VIN+ sang VIN-, qua ACS712 từ IP+ sang IP-, tới tải, cực âm về GND chung với ESP32
Dòng tải đi qua cả hai cảm biến để so sánh hai cách đo (hình minh họa).

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

Bảng đổi số: thanh ghi shunt INA219 0x1388 bằng 5000 nhân 10 micro vôn bằng 50 mV chia 0,1 ohm bằng 500 mA; thanh ghi bus 0x5DC0 bằng 12 V; ACS712 1728 mV ở D34 nhân 1,5 ra 500 mA
Phép đổi chương trình dùng cho mỗi cảm biến (số liệu ví dụ).
  • 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

Serial Monitor in điểm 0 của ACS712, rồi điện áp, dòng, công suất, điện lượng đã dùng của INA219 và dòng của ACS712 khi tải 0,5 A chạy
Lúc chưa bật tải và khi tải 0,5 A chạy (hình minh họa, số liệu ví dụ).

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ượngNguyên nhân thường gặpCá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 VKhông nối GND chung giữa nguồn tải và ESP32Nố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 độngKhởi động lại khi tải tắt; nguồn 5 V ổn định
ACS712 báo sai gấp 1,5 lầnQuên hoặc nối sai cầu phân ápKiểm tra điện trở 10 kΩ, 20 kΩ và hằng HE_SO_PHAN_AP
ACS712 số liệu nhảy nhiềuADC nhiễu, ít mẫuTăng số lần lấy trung bình; thêm tụ 100 nF từ D34 xuống GND

10. Xem thêm


Tin tức liên quan

Hướng dẫn dùng màn hình OLED 0.96 inch SSD1306 với Arduino Nano
Hướng dẫn dùng màn hình OLED 0.96 inch SSD1306 với Arduino Nano

Hướng dẫn nối màn hình OLED 0.96 inch SSD1306 (I2C) với Arduino Nano, cài thư viện Adafruit, hiện chữ nhiều cỡ, vẽ thanh mức theo điện áp và lưu ý về bộ nhớ RAM.

Hướng dẫn dùng màn hình LCD 2004 I2C với ESP32
Hướng dẫn dùng màn hình LCD 2004 I2C với ESP32

Hướng dẫn nối màn hình LCD 2004 I2C (mạch PCF8574) với ESP32 DevKit V1, quét địa chỉ I2C, hiển thị 4 dòng chữ, tạo ký tự riêng, làm bộ đếm thời gian và đổi sang LCD 1602.

Hướng dẫn làm cân điện tử với ESP32, HX711 và load cell trên Arduino IDE
Hướng dẫn làm cân điện tử với ESP32, HX711 và load cell trên Arduino IDE

Hướng dẫn nối load cell với module HX711 và ESP32 DevKit V1, gá load cell đúng cách, hiệu chuẩn bằng vật đã biết khối lượng và viết chương trình cân trên Arduino IDE.

Hướng dẫn dùng PWM trên ESP32: điều chỉnh độ sáng LED bằng analogWrite và LEDC
Hướng dẫn dùng PWM trên ESP32: điều chỉnh độ sáng LED bằng analogWrite và LEDC

Hướng dẫn tạo PWM trên ESP32 bằng Arduino IDE: analogWrite, ledcAttach và ledcWrite của gói esp32 3.x, chỉnh độ sáng LED bằng biến trở, chọn tần số và độ phân giải.

Hướng dẫn dùng màn hình OLED SSD1306 với ESP8266
Hướng dẫn dùng màn hình OLED SSD1306 với ESP8266

Hướng dẫn nối OLED SSD1306 với ESP8266, quét địa chỉ I2C, cài thư viện và hiển thị thời gian bằng mã Arduino mẫu.

ESP32 điều khiển servo SG90 bằng PWM, không cần thư viện: quét góc, biến trở, hiệu chỉnh
ESP32 điều khiển servo SG90 bằng PWM, không cần thư viện: quét góc, biến trở, hiệu chỉnh

Hướng dẫn điều khiển servo SG90 bằng ESP32 với bộ PWM LEDC 50 Hz có sẵn: xung 0,5–2,4 ms, sơ đồ nối, quét góc, vặn biến trở, hiệu chỉnh hai đầu hành trình và thí nghiệm STEM.

Hướng dẫn đọc ADC trên ESP32 và đo điện áp pin 18650 bằng cầu phân áp
Hướng dẫn đọc ADC trên ESP32 và đo điện áp pin 18650 bằng cầu phân áp

Hướng dẫn đọc điện áp analog bằng ADC của ESP32 trên Arduino IDE, dùng analogReadMilliVolts, làm cầu phân áp đo pin Li-ion 18650 và ước lượng phần trăm pin còn lại.

ESP32 với Firebase Realtime Database qua REST: đăng nhập ẩn danh, làm mới token, điều khiển relay
ESP32 với Firebase Realtime Database qua REST: đăng nhập ẩn danh, làm mới token, điều khiển relay

ESP32 dùng Firebase Realtime Database không cần thư viện Firebase: đăng nhập ẩn danh, tự làm mới token, đọc lệnh relay, ghi trạng thái và lịch sử, Rules theo uid.


Bình luận
Đã thêm vào giỏ hàng