Đo dung lượng và dòng xả pin 18650 bằng ESP32: mAh, Wh, điện trở trong, nhận ra pin giả, pin chai
Làm máy đo dung lượng pin 18650 bằng ESP32, MOSFET và điện trở tải: mAh, Wh, dòng xả và C-rate, đo điện trở trong, cộng mAh tới 3,0 V, đọc kết quả để nhận ra pin giả và pin đã chai.
Pin 18650 ghi 9900 mAh, pin tháo từ laptop cũ, pin của bộ sạc dự phòng hỏng: còn bao nhiêu dung lượng thật? Cách chắc chắn nhất là xả pin với dòng biết trước và cộng số mAh cho tới khi pin cạn. Bài này giải thích dung lượng (mAh, Wh), dòng xả và C-rate, điện trở trong, rồi làm máy đo dung lượng bằng ESP32, một MOSFET và một điện trở tải: tự cộng mAh, mWh tới 3,0 V, đo điện trở trong, và cách đọc kết quả để nhận ra pin giả, pin đã chai.
1. mAh, Wh và điện trở trong
- mAh (dung lượng): dòng điện × thời gian. Pin 2600 mAh xả 1 A được khoảng 2,6 giờ.
- Wh (năng lượng): mAh × điện áp. Pin 2600 mAh ở điện áp trung bình 3,7 V chứa khoảng 9,6 Wh. Dùng Wh để so pin khác điện áp, khác số cell.
- Điện trở trong: pin càng chai, điện trở trong càng lớn, điện áp tụt càng sâu khi có tải và pin nóng hơn. Pin 18650 còn tốt thường có điện trở trong vài chục mΩ.
2. Dòng xả và C-rate

C-rate là dòng xả so với dung lượng: 1C với pin 2600 mAh là 2,6 A. Mỗi loại pin có dòng xả tối đa ghi trong tài liệu (loại dung lượng cao thường vài ampe, loại dòng cao 10–20 A). Dòng càng lớn thì pin càng nóng và số mAh đo được càng thấp, nên khi so sánh nhiều pin với nhau, xả cùng một dòng nhỏ (0,2C–0,5C).
3. Mạch xả

- Điện trở tải 5 Ω, loại 10 W trở lên: dòng xả khoảng 0,6–0,8 A (khoảng 0,3C với pin 2600 mAh). Đo giá trị thật bằng đồng hồ (gồm cả dây) và điền vào
R_TAI: sai số điện trở là sai số dòng điện. - MOSFET kênh N loại logic-level (dẫn tốt khi cổng chỉ có 3,3 V); xem MOSFET logic-level và điện trở gate. Điện trở 100k kéo cổng xuống để tải tắt khi ESP32 khởi động lại.
- Cầu phân áp 100k + 100k đưa 4,2 V xuống 2,1 V cho ADC. Xem đọc ADC và đo điện áp pin 18650.
- Pin đặt trong khay pin 18650; lắp trên breadboard với dây cắm được, riêng đường dòng tải nên dùng dây to, hàn chắc.
An toàn: điện trở tải tỏa khoảng 3–3,5 W khi pin đầy, nóng tới mức gây bỏng: đặt trên nền không cháy, có người trông. Không bao giờ nối thẳng hai cực pin.
4. Chương trình
Chương trình chỉ dùng gói ESP32 cho Arduino IDE trên ESP32 DevKit V1. Sạc đầy pin, lắp vào khay, mở Serial Monitor và gõ S. ESP32 đo điện trở trong (so điện áp lúc hở và lúc có tải), bật tải, mỗi giây tính dòng I = V ÷ R tải, cộng mAh và mWh; mỗi 10 phút đo lại điện trở trong; điện áp có tải xuống 3,0 V thì tắt tải và in kết quả.
// PYWORLD.VN - Đo dung lượng pin 18650 bằng ESP32: xả qua điện trở tải, cộng mAh và mWh tới 3,0 V, đo điện trở trong
// PYWORLD.VN - MOSFET kênh N loại logic-level đóng cắt tải ở GPIO25; điện áp pin đo ở GPIO34 qua cầu 100k / 100k
// PYWORLD.VN - Điện trở tải 5 ohm phải là loại công suất 10 W trở lên, rất nóng khi xả: đặt trên nền không cháy, có người trông
#include <math.h>
const int CHAN_DO_PIN = 34;
const int CHAN_MOSFET = 25;
const float HE_SO_PIN = 2.0f; // (100k + 100k) / 100k
const float R_TAI = 5.0f; // điện trở tải đo bằng đồng hồ, gồm cả dây (ohm)
const float V_NGAT = 3.0f; // dừng khi điện áp lúc có tải xuống mức này
const uint32_t CHU_KY_MS = 1000;
const uint32_t IN_MOI_MS = 60000;
const uint32_t DO_NOI_TRO_MOI_MS = 600000; // mỗi 10 phút đo điện trở trong một lần
bool dangXa = false;
double tongMah = 0, tongMwh = 0;
uint32_t lucBatDau = 0, lucTruoc = 0, lucIn = 0, lucDoNoiTro = 0;
float noiTroMohm = NAN;
float doPin() {
uint32_t tong = 0;
for (int i = 0; i < 16; i++) tong += analogReadMilliVolts(CHAN_DO_PIN);
return tong / 16.0f / 1000.0f * HE_SO_PIN;
}
// So điện áp lúc hở mạch và lúc có tải: R trong = (V hở - V tải) / I
float doNoiTro(float &vHo) {
digitalWrite(CHAN_MOSFET, LOW);
delay(1000);
vHo = doPin();
digitalWrite(CHAN_MOSFET, HIGH);
delay(1000);
float vTai = doPin();
float dong = vTai / R_TAI;
return (vHo - vTai) / dong * 1000.0f;
}
void inThoiGian(uint32_t ms) {
uint32_t s = ms / 1000;
Serial.printf("%u:%02u:%02u", (unsigned)(s / 3600), (unsigned)(s / 60 % 60), (unsigned)(s % 60));
}
void setup() {
Serial.begin(115200);
pinMode(CHAN_MOSFET, OUTPUT);
digitalWrite(CHAN_MOSFET, LOW);
Serial.println("Do dung luong pin 18650. Sac day pin, lap vao khay roi go S de bat dau xa");
}
void loop() {
uint32_t bayGio = millis();
if (!dangXa) {
if (Serial.available() && (Serial.read() | 0x20) == 's') {
float vHo;
noiTroMohm = doNoiTro(vHo);
tongMah = tongMwh = 0;
lucBatDau = lucTruoc = lucIn = lucDoNoiTro = millis();
dangXa = true;
Serial.printf("Bat dau: pin %.3f V luc ho, noi tro %.0f mOhm, tai %.1f ohm\n", vHo, noiTroMohm, R_TAI);
}
return;
}
if (bayGio - lucTruoc < CHU_KY_MS) return;
float v = doPin();
float dong = v / R_TAI;
double gio = (bayGio - lucTruoc) / 3600000.0;
tongMah += dong * 1000.0 * gio;
tongMwh += v * dong * 1000.0 * gio;
lucTruoc = bayGio;
if (v < V_NGAT) {
digitalWrite(CHAN_MOSFET, LOW);
dangXa = false;
Serial.print("Xong sau ");
inThoiGian(bayGio - lucBatDau);
Serial.printf(": %.0f mAh, %.2f Wh, noi tro %.0f mOhm\n", tongMah, tongMwh / 1000.0, noiTroMohm);
return;
}
if (bayGio - lucDoNoiTro >= DO_NOI_TRO_MOI_MS) {
float vHo;
noiTroMohm = doNoiTro(vHo);
lucDoNoiTro = lucTruoc = millis();
}
if (bayGio - lucIn >= IN_MOI_MS) {
lucIn = bayGio;
inThoiGian(bayGio - lucBatDau);
Serial.printf(" %.3f V %.0f mA %.0f mAh %.0f mWh\n", v, dong * 1000, tongMah, tongMwh);
}
}

- Điện trở trong đo được gồm cả khay pin và dây nối, nên lớn hơn số của riêng viên pin; dùng cùng khay, cùng dây để so các viên pin với nhau.
- Muốn chính xác hơn: dùng module ADC 16 bit ADS1115 đo điện áp, thêm điện trở shunt nhỏ để đo dòng trực tiếp.
5. Đọc kết quả

| Kết quả | Đánh giá |
|---|---|
| Trên 90% dung lượng ghi, điện trở trong thấp | Pin tốt |
| 70–90% | Pin đã dùng, vẫn dùng được cho thiết bị dòng nhỏ |
| Dưới khoảng 70%, điện trở trong tăng gấp đôi so với lúc mới | Pin đã chai, nên thay |
| Ghi 6000–9900 mAh, đo được vài trăm tới hơn 1000 mAh | Pin giả, ghi sai dung lượng |
Khi ghép bộ pin nhiều cell, đo từng cell và chọn các cell có dung lượng, điện trở trong gần nhau; xem ghép pin 18650 nối tiếp, song song.
6. Lỗi hay gặp
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| Dừng ngay sau khi bắt đầu | Pin chưa sạc; tiếp xúc khay kém làm điện áp có tải tụt dưới 3,0 V | Sạc đầy; lau tiếp điểm, dùng dây to |
| Dòng đo được khác đồng hồ | R_TAI sai; điện trở nóng lên thay đổi giá trị | Đo lại điện trở, dùng loại công suất lớn hơn |
| Tải không tắt khi ESP32 khởi động lại | Thiếu điện trở kéo cổng MOSFET xuống | Thêm 100k từ G xuống GND |
| MOSFET nóng | MOSFET không phải loại logic-level, dẫn không hết | Đổi MOSFET logic-level |
| Điện trở trong rất lớn ở mọi viên pin | Khay, dây nối có điện trở lớn | Dùng khay tốt, dây ngắn và to |
7. Xem thêm
- Các bài cùng loạt năng lượng: sạc pin 18650 đúng cách (CC/CV, nhiệt độ, nhận biết pin giả), UPS mini 12 V, pin mặt trời mini cho ESP32.
Xem thêm