Cảm biến tiệm cận NPN, PNP 24 V nối vào ESP32 qua opto: khoảng cách phát hiện, đếm vật, đo vòng
Nối cảm biến tiệm cận NPN, PNP 24 V vào ESP32 an toàn qua opto PC817, tính điện trở, khoảng cách phát hiện theo thép, inox, nhôm, đồng, chương trình đếm vật và đo vòng quay bằng ngắt.
Cảm biến tiệm cận cảm ứng từ loại M12, M18 (như dòng LJ12A3 hay gặp) phát hiện kim loại không cần chạm, bền, rẻ, hợp để đếm sản phẩm trên băng tải, đo tốc độ quay trục, báo vị trí xi lanh. Nhưng chúng chạy 12–24 V còn ESP32 chỉ chịu 3,3 V. Bài này hướng dẫn nối cảm biến tiệm cận NPN, PNP 24 V vào ESP32 an toàn qua opto PC817, tính điện trở cho opto, khoảng cách phát hiện theo từng loại kim loại, và chương trình đếm vật, đo vòng quay bằng ngắt có lọc nhiễu.
Khái niệm NPN, PNP, NO, NC, cảm biến 2 dây, 3 dây có ở bài cùng cụm về cảm biến NPN và PNP; cấu tạo, nguyên lý dòng xoáy có ở bài cảm biến tiệm cận cảm ứng từ.
1. Nối qua opto PC817

Không nối thẳng dây đen vào chân ESP32. Nhiều cảm biến NPN có sẵn điện trở kéo lên nguồn 24 V bên trong: lúc không có vật, dây đen mang 24 V và làm hỏng chân ESP32 ngay. PNP thì lúc có vật dây đen là 24 V. Opto PC817 giải quyết cả hai: cảm biến chỉ bật tắt đèn LED trong opto, phía ra là một transistor kéo chân ESP32 xuống GND, hai phía cách ly hoàn toàn.
- NPN: chân A của opto qua điện trở 4,7 kΩ lên +24 V, chân K vào dây đen.
- PNP: dây đen qua điện trở 4,7 kΩ vào chân A, chân K xuống 0 V của nguồn 24 V.
- Phía ESP32: chân C vào GPIO27 (bật kéo lên trong chip), chân E vào GND của ESP32. Có vật thì LED sáng, transistor dẫn, GPIO27 xuống mức thấp.
Cách hoạt động của PC817, đo thời gian đáp ứng có ở bài optocoupler PC817. Cần nhiều ngõ vào thì dùng module cách ly 4, 8 kênh, hoặc xem bài cùng cụm về ngõ vào 24 V cho vi điều khiển.
2. Khoảng cách phát hiện theo vật liệu

Khoảng cách ghi trên cảm biến (Sn, ví dụ 4 mm cho loại M12) đo với tấm thép chuẩn. Inox, đồng thau, nhôm, đồng bị phát hiện ở khoảng cách ngắn hơn, có khi chỉ còn một phần ba. Lắp cảm biến cách vật khoảng 80 % khoảng cách phát hiện để không chập chờn khi trục rung, nhiệt độ thay đổi.
3. Lắp đặt và chọn linh kiện

Script dưới đây tính khoảng cách theo vật liệu, điện trở và công suất cho LED opto, tốc độ quay lớn nhất đo được theo tần số đóng cắt ghi trong datasheet cảm biến:
# PYWORLD.VN - Tính nhanh khi lắp cảm biến tiệm cận cảm ứng từ 24 V với ESP32: khoảng cách phát hiện theo vật liệu, khe lắp an toàn
# PYWORLD.VN - Điện trở cho LED opto PC817 ở phía 24 V, và tốc độ quay lớn nhất đo được theo tần số đóng cắt của cảm biến
# PYWORLD.VN - Chạy: python tinh_tiem_can.py (sửa SN, TAN_SO_CAM_BIEN theo datasheet cảm biến của bạn)
SN = 4.0 # mm, khoảng cách danh định (ví dụ cảm biến M12 loại 4 mm, đo với thép)
TAN_SO_CAM_BIEN = 500 # Hz, tần số đóng cắt lớn nhất trong datasheet (giá trị ví dụ)
HE_SO = {"thep thuong": 1.0, "inox 304": 0.7, "dong thau": 0.45, "nhom": 0.4, "dong": 0.35} # khoảng, theo kích thước và loại cảm biến
def khoang_cach(vat_lieu: str) -> tuple[float, float]:
"""(khoảng cách phát hiện ước tính, khe lắp nên dùng ≈ 80 % để chắc chắn)."""
s = SN * HE_SO[vat_lieu]
return s, 0.8 * s
def dien_tro_led(v_nguon: float = 24.0, v_led: float = 1.2, v_sut_cam_bien: float = 1.5, i_led_ma: float = 5.0) -> tuple[float, float]:
"""(điện trở Ω, công suất W) cho LED opto nối nối tiếp với ngõ ra cảm biến."""
r = (v_nguon - v_led - v_sut_cam_bien) / (i_led_ma / 1000)
return r, (i_led_ma / 1000) ** 2 * r
def vong_quay_toi_da(so_dau_bu_long: int) -> float:
return TAN_SO_CAM_BIEN * 60 / so_dau_bu_long
if __name__ == "__main__":
for vl in HE_SO:
s, khe = khoang_cach(vl)
print(f"{vl:12s}: phat hien ~{s:.1f} mm, nen lap khe <= {khe:.1f} mm")
r, p = dien_tro_led()
print(f"Dien tro LED opto o 24 V, 5 mA: {r:.0f} Ohm (chon 4,3-4,7 kOhm), cong suat {p:.2f} W")
for n in (1, 2, 4):
print(f"{n} dau bu long tren truc: do duoc toi ~{vong_quay_toi_da(n):.0f} vong/phut")
4. Chương trình đếm vật, đo vòng quay
Mỗi lần có vật kim loại đi qua (hoặc đầu bu lông trên trục quay qua), GPIO27 xuống mức thấp và gọi hàm ngắt. Hàm ngắt bỏ qua các xung cách nhau dưới 2 ms (nhiễu, rung tiếp xúc), cộng bộ đếm. Mỗi giây chương trình in tổng số vật, số xung trong giây, vòng/phút, và báo khi trục đang quay bỗng chậm hẳn (băng tải kẹt, đứt đai).
// PYWORLD.VN - Cảm biến tiệm cận cảm ứng từ 24 V (NPN hoặc PNP) nối ESP32 qua opto PC817: đếm vật đi qua và đo tốc độ quay trục bằng ngắt
// PYWORLD.VN - Lọc xung nhiễu ngắn hơn 2 ms ngay trong hàm ngắt; mỗi giây in tổng số vật, vòng/phút; báo khi trục quay chậm bất thường (kẹt)
// PYWORLD.VN - Ngõ ra transistor của opto: chân C -> GPIO27, chân E -> GND ESP32. Có vật: opto dẫn, GPIO27 xuống mức thấp
const int CHAN_CAM_BIEN = 27, LED_PIN = 2;
const uint32_t KHOANG_MIN_US = 2000; // xung cách nhau dưới 2 ms coi là nhiễu (tối đa 500 xung/giây)
const int SO_DAU_BU_LONG = 1; // số đầu bu lông (vật kim loại) trên một vòng trục
const float VONG_PHUT_TOI_THIEU = 30; // dưới mức này khi đang chạy thì báo kẹt
volatile uint32_t demTong = 0, demTrongGiay = 0, lucXungCuoi = 0;
portMUX_TYPE khoa = portMUX_INITIALIZER_UNLOCKED;
uint32_t lucIn = 0;
float vongPhutTruoc = 0;
void IRAM_ATTR coVat() {
uint32_t t = micros();
if (t - lucXungCuoi < KHOANG_MIN_US) return;
lucXungCuoi = t;
portENTER_CRITICAL_ISR(&khoa);
demTong = demTong + 1;
demTrongGiay = demTrongGiay + 1;
portEXIT_CRITICAL_ISR(&khoa);
}
void setup() {
Serial.begin(115200);
pinMode(CHAN_CAM_BIEN, INPUT_PULLUP); // dây dài, nhiều nhiễu: thêm điện trở 4,7 kΩ lên 3,3 V
pinMode(LED_PIN, OUTPUT);
attachInterrupt(digitalPinToInterrupt(CHAN_CAM_BIEN), coVat, FALLING);
Serial.println("San sang dem vat / do vong quay");
}
void loop() {
digitalWrite(LED_PIN, digitalRead(CHAN_CAM_BIEN) == LOW ? HIGH : LOW); // đèn sáng khi đang có vật trước cảm biến
if (millis() - lucIn < 1000) return;
lucIn = millis();
portENTER_CRITICAL(&khoa);
uint32_t tong = demTong, giay = demTrongGiay;
demTrongGiay = 0;
portEXIT_CRITICAL(&khoa);
float vongPhut = giay * 60.0f / SO_DAU_BU_LONG;
Serial.printf("Tong %lu vat | %lu xung/giay | %.0f vong/phut%s\n", (unsigned long)tong, (unsigned long)giay, vongPhut,
(vongPhutTruoc >= VONG_PHUT_TOI_THIEU && vongPhut < VONG_PHUT_TOI_THIEU) ? " <- QUAY CHAM, KIEM TRA KET" : "");
vongPhutTruoc = vongPhut;
}

Đo vòng quay chậm (vài chục vòng/phút) thì đếm trong 1 giây cho kết quả nhảy bậc; đo khoảng thời gian giữa hai xung bằng micros() sẽ chính xác hơn. Cách dùng ngắt, chống dội trong ngắt có ở bài ngắt GPIO trên ESP32.
5. Lỗi hay gặp
| Hiện tượng | Nguyên nhân | Cách xử lý |
|---|---|---|
| Đèn trên cảm biến sáng mà ESP32 không nhận | Nối opto sai chiều NPN/PNP, thiếu GND phía ESP32 | Đối chiếu sơ đồ; chân E opto nối GND ESP32 |
| Đếm nhiều hơn số vật | Nhiễu từ động cơ, vật rung qua lại ở mép vùng phát hiện | Tăng khoảng lọc trong ngắt; lắp gần hơn; dây đi riêng |
| Không phát hiện vật nhôm, đồng | Khoảng cách với kim loại màu ngắn hơn nhiều | Lắp gần hơn, hoặc dùng cảm biến lớn hơn |
| Chân ESP32 hỏng sau khi thử | Nối thẳng dây đen 24 V vào GPIO | Luôn qua opto hoặc mạch chuyển mức |
| Trục quay nhanh đo ra thấp hơn thực tế | Vượt tần số đóng cắt của cảm biến hoặc khoảng lọc 2 ms | Giảm số đầu bu lông; chọn cảm biến tần số cao hơn |
Mạch dùng trong bài: ESP32 DevKit V1 30 chân.
Bài cùng cụm chủ đề: cảm biến NPN và PNP khác nhau thế nào; ngõ vào 24 V cho vi điều khiển; ESP32 trong môi trường công nghiệp.
Xem thêm