Mạch chuyển nguồn tự động ATS: nguyên lý, khóa liên động, trễ chuyển, trễ về, mô hình 12 V ESP32
ATS là gì, gồm khởi động từ, bộ điều khiển, khóa liên động; trình tự chuyển với trễ chuyển, trễ về; mô hình ATS 12 V an toàn bằng ESP32 và relay 2 kênh; khác nhau giữa mô hình và ATS 220 V thật.
Nhà có máy phát điện, bộ lưu điện inverter hay hệ pin mặt trời thường cần một bộ ATS (Automatic Transfer Switch, bộ chuyển nguồn tự động): mất điện lưới thì tự chuyển sang nguồn dự phòng, có điện lại thì tự chuyển về. Bài này giải thích ATS gồm những gì, vì sao khóa liên động quan trọng, trình tự chuyển với trễ chuyển và trễ về, rồi làm một mô hình ATS 12 V an toàn bằng ESP32 và module relay 2 kênh để hiểu logic điều khiển.
1. Lưu ý an toàn trước tiên
ATS cho điện lưới 220 V phải là thiết bị chuyên dụng (khởi động từ có khóa liên động cơ khí, bộ điều khiển ATS, CB đúng định mức) và do thợ điện có chuyên môn lắp đặt. Không tự ghép ATS 220 V bằng module relay nhỏ: tiếp điểm không đủ dòng, không có khóa liên động cơ khí, không đủ khoảng cách cách điện. Mô hình trong bài chỉ dùng nguồn một chiều 12 V để học logic.
2. ATS gồm những gì

- Hai khởi động từ (hoặc một cơ cấu chuyển mạch có động cơ): một nối nguồn chính, một nối nguồn dự phòng tới tải.
- Bộ điều khiển: đo điện áp, tần số nguồn chính; đếm thời gian trễ; với máy phát còn gửi tín hiệu khởi động, chờ máy chạy ổn định rồi mới chuyển.
- Khóa liên động bằng điện (tiếp điểm phụ của khởi động từ này cắt cuộn hút của khởi động từ kia) và bằng cơ khí (thanh chặn): hai khởi động từ không bao giờ đóng cùng lúc.
Vì sao khóa liên động sống còn: đóng cả hai là nối hai nguồn với nhau (chập, cháy thiết bị), hoặc máy phát đẩy điện ngược lên đường dây lưới, gây nguy hiểm cho người đang sửa đường dây mà tưởng đã mất điện. ATS cho điện một pha nên cắt cả dây pha và dây trung tính (loại 2 cực).
3. Trình tự chuyển và thời gian trễ
- Trễ chuyển: nguồn chính phải mất liên tục một khoảng (từ vài trăm mili giây tới vài giây) mới chuyển, để bỏ qua các lần chớp điện rất ngắn.
- Ngắt trước, đóng sau: cắt nguồn cũ, chờ một chút, rồi mới đóng nguồn mới. Tải mất điện trong khoảng thời gian này: ATS khác UPS ở chỗ có một khoảng gián đoạn ngắn.
- Trễ về: nguồn chính có lại phải ổn định liên tục một thời gian (vài giây tới vài phút) mới chuyển về, tránh chuyển qua lại khi điện chập chờn. Nếu trong lúc chờ điện lại chớp tắt, bộ đếm chạy lại từ đầu.

4. Mô hình ATS 12 V bằng ESP32 và relay

Chuẩn bị: ESP32 DevKit V1, module relay 2 kênh, hai nguồn 12 V (ví dụ adapter và bộ pin 3S trong khay pin 18650), một tải nhỏ (dải LED 12 V), bốn điện trở cho hai cầu phân áp 47k / 10k.
- K2 là relay chuyển đổi: chân COM chỉ chạm NO hoặc NC, không bao giờ cả hai, nên nguồn A và B không thể nối với nhau, kể cả khi chương trình lỗi. Đây chính là khóa liên động "bằng cấu tạo".
- K1 ngắt tải trong lúc K2 đổi tiếp điểm, để tiếp điểm K2 không phải cắt dòng tải.
- ESP32 lấy điện riêng (USB, hoặc qua hai diode từ hai nguồn rồi hạ áp xuống 5 V) để không bị mất điện theo tải.
- Module relay 5 V kích mức thấp dùng được với ESP32; xem module relay: kích mức thấp, JD-VCC.
5. Chương trình
Chương trình chỉ dùng gói ESP32 cho Arduino IDE. Mỗi 50 ms đo hai nguồn; nguồn A dưới 10 V liên tục 0,5 giây thì chuyển sang B (nếu B còn trên 10,5 V); A trên 11 V liên tục 5 giây thì chuyển về A. Giữa hai ngưỡng 10 V và 11 V là vùng trễ, tránh nhảy qua lại khi điện áp dao động quanh một mức. Lúc khởi động, nếu A đang tốt thì chờ đủ 5 giây mới cấp tải.
// PYWORLD.VN - Mô hình ATS 12 V bằng ESP32 và module relay 2 kênh: nguồn A là chính, nguồn B dự phòng
// PYWORLD.VN - K2 (relay chuyển đổi) chỉ nối tải với một nguồn tại một thời điểm; K1 ngắt tải trong lúc đổi nguồn
// PYWORLD.VN - Chỉ dùng cho nguồn một chiều 12 V; ATS cho điện lưới 220 V phải là thiết bị chuyên dụng do thợ điện lắp
const int CHAN_DO_A = 34; // nguồn A qua cầu 47k / 10k
const int CHAN_DO_B = 35; // nguồn B qua cầu 47k / 10k
const float HE_SO = 5.7f; // (47k + 10k) / 10k
const int CHAN_K1 = 26; // relay cấp điện cho tải
const int CHAN_K2 = 27; // relay chọn nguồn: hút = nguồn A (NO), nhả = nguồn B (NC)
const bool RELAY_KICH_MUC_THAP = true; // module relay thường kích khi chân IN ở mức thấp
const float A_TOT_V = 11.0f; // A trên mức này là tốt
const float A_MAT_V = 10.0f; // A dưới mức này là mất (giữa hai mức: giữ nguyên nhận định)
const float B_TOI_THIEU_V = 10.5f; // B dưới mức này thì không chuyển sang B
const uint32_t TRE_CHUYEN_MS = 500; // A mất liên tục chừng này mới chuyển (bỏ qua chập chờn ngắn)
const uint32_t TRE_VE_MS = 5000; // A tốt liên tục chừng này mới chuyển về
const uint32_t NGAT_TRUOC_DONG_MS = 300; // khoảng nghỉ giữa các bước đóng cắt
enum Nguon { NGUON_A, NGUON_B, KHONG_CO };
Nguon dangDung = KHONG_CO;
uint32_t lucATot = 0; // 0 = A chưa tốt liên tục
uint32_t lucAMat = 0; // 0 = A chưa mất liên tục
void datRelay(int chan, bool hut) {
digitalWrite(chan, (hut != RELAY_KICH_MUC_THAP) ? HIGH : LOW);
}
float doVon(int chan) {
uint32_t tong = 0;
for (int i = 0; i < 8; i++) tong += analogReadMilliVolts(chan);
return tong / 8.0f / 1000.0f * HE_SO;
}
const char *tenNguon(Nguon n) {
return n == NGUON_A ? "A" : (n == NGUON_B ? "B" : "khong co");
}
// Ngắt tải, đổi K2, rồi mới cấp lại: không bao giờ nối hai nguồn với nhau
void chuyenSang(Nguon moi) {
datRelay(CHAN_K1, false);
delay(NGAT_TRUOC_DONG_MS);
if (moi != KHONG_CO) {
datRelay(CHAN_K2, moi == NGUON_A);
delay(NGAT_TRUOC_DONG_MS);
datRelay(CHAN_K1, true);
}
dangDung = moi;
Serial.printf("%7.1f s -> tai dung nguon %s\n", millis() / 1000.0f, tenNguon(moi));
}
void setup() {
Serial.begin(115200);
pinMode(CHAN_K1, OUTPUT);
pinMode(CHAN_K2, OUTPUT);
datRelay(CHAN_K1, false);
datRelay(CHAN_K2, false);
Serial.println("Mo hinh ATS 12 V");
}
void loop() {
uint32_t bayGio = millis();
float vA = doVon(CHAN_DO_A);
float vB = doVon(CHAN_DO_B);
bool bDuoc = vB >= B_TOI_THIEU_V;
if (vA >= A_TOT_V) {
lucAMat = 0;
if (lucATot == 0) lucATot = bayGio;
} else {
lucATot = 0;
if (vA < A_MAT_V && lucAMat == 0) {
lucAMat = bayGio;
Serial.printf("%7.1f s A mat (%.1f V), cho %u ms\n", bayGio / 1000.0f, vA, (unsigned)TRE_CHUYEN_MS);
}
}
bool aTotDu = lucATot != 0 && bayGio - lucATot >= TRE_VE_MS;
bool aMatDu = lucAMat != 0 && bayGio - lucAMat >= TRE_CHUYEN_MS;
if (dangDung == NGUON_A && aMatDu) {
chuyenSang(bDuoc ? NGUON_B : KHONG_CO);
} else if (dangDung != NGUON_A && aTotDu) {
chuyenSang(NGUON_A);
} else if (dangDung == KHONG_CO && !aTotDu && bDuoc && (lucATot == 0)) {
chuyenSang(NGUON_B);
} else if (dangDung == NGUON_B && !bDuoc) {
chuyenSang(KHONG_CO);
}
delay(50);
}

Thử bằng cách rút adapter (nguồn A): sau khoảng 1 giây đèn sáng lại nhờ nguồn B. Cắm lại, chờ 5 giây đèn chớp tắt một lần khi chuyển về A. Rút cắm nhanh liên tục để thấy bộ đếm trễ về chạy lại từ đầu.
6. Từ mô hình tới ATS thật
| Mô hình 12 V | ATS 220 V thật |
|---|---|
| Module relay vài ampe | Khởi động từ hoặc cơ cấu chuyển mạch đúng dòng tải, 2 cực cho điện 1 pha |
| Khóa liên động bằng relay chuyển đổi | Khóa liên động điện và cơ khí |
| Đo điện áp bằng ADC | Bộ điều khiển đo điện áp, tần số, mất pha |
| Trễ 0,5 s và 5 s | Trễ chỉnh được, thường dài hơn; chờ máy phát ổn định |
| Tự lắp trên bàn | Thợ điện lắp trong tủ điện, có CB, nối đất |
ESP32 vẫn hữu ích ở hệ thống thật theo cách an toàn: đọc tiếp điểm báo trạng thái (tiếp điểm khô) của bộ ATS để gửi cảnh báo mất điện lên điện thoại, không can thiệp vào mạch động lực.
7. Lỗi hay gặp với mô hình
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| ESP32 khởi động lại khi relay đổi | ESP32 lấy điện từ tải; nhiễu cuộn relay | Cấp điện riêng cho ESP32; xem bài relay làm ESP32 bị reset |
| Relay hoạt động ngược | Module kích mức cao | Đổi RELAY_KICH_MUC_THAP thành false |
| Chuyển qua lại liên tục | Hai ngưỡng quá gần nhau; nguồn A sụt áp khi có tải | Nới khoảng cách hai ngưỡng, tăng trễ về |
| Không chuyển sang B | B dưới 10,5 V hoặc cầu phân áp đo sai | Đo B bằng đồng hồ, chỉnh hệ số |
8. Xem thêm
- Công tắc, nút nhấn, cầu chì và relay
- Bài UPS mini 12 V cho router trong cùng loạt năng lượng: chuyển nguồn không gián đoạn bằng hai diode, khác với ATS có khoảng nghỉ.
Xem thêm