LDR (Light Dependent Resistor) — còn gọi là quang trở hoặc photoresistor — là linh kiện thay đổi điện trở theo cường độ ánh sáng. Đơn giản nhất trong các cảm biến ánh sáng nhưng đủ mạnh cho hầu hết ứng dụng điều khiển cơ bản. Bài này phân tích vật liệu CdS (cadmium sulfide), tại sao điện trở giảm khi có sáng, và cách đọc analog chính xác.
Nguyên Lý Hoạt Động
1. CdS — Vật Liệu Photoconductor
LDR dùng CdS (Cadmium Sulfide) — vật liệu bán dẫn với bandgap ~2.42eV, nhạy với ánh sáng nhìn thấy (đặc biệt 500–700nm, đỉnh ~520nm xanh lá).
Cơ chế dẫn điện:
Trong bóng tối:
Electron ở vùng hóa trị (valence band) — bị "khóa"
Vùng dẫn điện (conduction band) trống
→ Ít electron tự do → Điện trở RẤT CAO (1MΩ – 10MΩ)
Khi có ánh sáng:
Photon ánh sáng có năng lượng E = hν
Nếu E ≥ Eg (bandgap CdS = 2.42eV)
→ Photon đẩy electron từ valence lên conduction band
→ Tạo cặp electron-hole tự do
→ Nhiều carrier điện → Điện trở THẤP (100Ω – 10kΩ)
Khi sáng tắt:
Electron và hole tái kết hợp → Carrier giảm → Điện trở tăng lại
(Chậm: 10ms–100ms → LDR "chậm" hơn photodiode)
Công thức điện trở tương đương (log-log relationship):
R_LDR = C × E^(-γ)
Trong đó:
E = cường độ ánh sáng (lux)
C và γ = hệ số phụ thuộc loại LDR (thường γ ≈ 0.7 – 0.9)
2. Voltage Divider — Đọc Ánh Sáng Bằng ADC
LDR không tạo điện áp tự động. Cần mạch voltage divider để chuyển điện trở → điện áp đọc được bởi ADC:
VCC
│
[R_cố_định] ← Thường 10kΩ
│
├──── VOUT (đọc bởi ADC)
│
[LDR] ← Điện trở thay đổi theo ánh sáng
│
GND
Điện áp VOUT = VCC × LDR / (R_fixed + LDR):
| Điều kiện | R_LDR | VOUT (với R_fixed=10kΩ, VCC=5V) |
|---|---|---|
| Tối hoàn toàn | ~1MΩ | ≈ 5V × 1000/1010 ≈ 4.95V (gần VCC) |
| Ánh sáng phòng | ~10kΩ | = 5V × 10/20 = 2.5V |
| Nắng gắt | ~100Ω | ≈ 5V × 0.1/10.1 ≈ 0.05V (gần GND) |
Lưu ý: Sáng → VOUT thấp (logic ngược). Điều chỉnh R_fixed để tối ưu dải đo:
- Môi trường tối (đêm): dùng R_fixed lớn hơn (47kΩ–100kΩ)
- Môi trường sáng ban ngày: R_fixed 1kΩ–10kΩ
- Để nhạy nhất ở cường độ quan tâm: đặt Rfixed = RLDR tại cường độ đó
3. Thời Gian Đáp Ứng — “Chậm” So Với Photodiode
LDR có thời gian đáp ứng chậm hơn nhiều so với photodiode:
- LDR: ~10ms đến vài trăm ms
- Photodiode: ~µs đến ns
Nguyên nhân: Cặp electron-hole tạo ra bởi photon cần thời gian để recombine khi tắt ánh sáng → điện trở giảm chậm sau khi bật sáng, tăng chậm sau khi tắt.
Điều này hoàn toàn chấp nhận được cho ứng dụng điều khiển ánh sáng thông thường (đèn đường, cửa chớp, điều chỉnh màn hình). Không phù hợp cho đo ánh sáng tần số cao (như đo nhấp nháy LED tần số kHz).
Thông Số Kỹ Thuật (LDR Thông Dụng GL5528)
| Thông số | Giá trị |
|---|---|
| Điện trở tối | 1MΩ – 10MΩ |
| Điện trở sáng (10 lux) | 10kΩ – 20kΩ |
| Điện trở sáng (100 lux) | 2kΩ – 5kΩ |
| Đỉnh nhạy sáng | ~540nm (xanh lá–vàng) |
| Dải bước sóng nhạy | ~400nm – 700nm (visible) |
| Điện áp tối đa | 100V DC (linh kiện rời) |
| Công suất tối đa | 100mW |
| Thời gian tắt–sáng | ~20ms |
| Thời gian sáng–tối | ~30ms |
| Nhiệt độ hoạt động | -30°C – 70°C |
Sơ Đồ Chân (Pinout)
LDR Rời (GL5528 hoặc tương đương)
Hình dạng: ⌾ (hình tròn đường kính 5mm, 10mm, hoặc 12mm)
2 chân giống nhau — KHÔNG phân cực
Chân 1 ──┤ LDR ├── Chân 2
(tương đương điện trở — không phân cực)
Module KY-018 (trong kit 37 module)
┌──────────────────────┐
│ [LDR] │
│ [10kΩ pull-down] │
└──────────────────────┘
GND VCC S(Signal)
| Chân | Chức năng |
|---|---|
| GND | Đất |
| VCC | 3.3V – 5V |
| S | Analog output — điện áp tỷ lệ với ánh sáng |
Logic KY-018: S cao = tối, S thấp = sáng (thường) — phụ thuộc cách nối voltage divider trong module.
So Sánh LDR Với Cảm Biến Ánh Sáng Khác
| Sensor | Nguyên lý | Đầu ra | Độ chính xác | Ghi chú |
|---|---|---|---|---|
| LDR (CdS) | Photoconductor | Analog điện trở | Tương đối | Đơn giản, rẻ |
| TEMT6000 | Phototransistor | Analog dòng | Tốt hơn | Nhỏ, tuyến tính hơn |
| BH1750 | Photodiode + ADC | I2C digital (lux) | Cao | 16-bit, hiệu chỉnh sẵn |
| APDS-9930 | Photodiode | I2C, ambient + proximity | Cao | Có cả đo gần |
| TSL2561 | Dual photodiode | I2C (lux + IR) | Cao | Bù IR tốt |
Khi nào dùng LDR: Cần đơn giản, rẻ, không cần lux chính xác — bật/tắt đèn, phát hiện ngày/đêm. Khi nào dùng BH1750: Cần đọc lux thực tế, so sánh với tiêu chuẩn, IoT logging ánh sáng.
Kết Nối Phần Cứng
LDR Rời với Voltage Divider — ESP32
3V3
│
[10kΩ điện trở cố định]
│
├──── GPIO32 (ADC1_CH4) ← Đọc điện áp
│
[LDR]
│
GND
Hoặc module KY-018:
ESP32 DevKit V1 KY-018 Module
───────────────────── ─────────────────
3V3 ─────────────────→ VCC
GND ─────────────────→ GND
GPIO32 (ADC1) ←────────── S (Signal)
LDR Rời với Voltage Divider — Arduino Uno
5V
│
[10kΩ]
│
├──── A0 ← Đọc điện áp analog
│
[LDR]
│
GND
Code Arduino IDE
Code Đọc Ánh Sáng Và Bật Đèn Tự Động — Arduino Uno
/*
* LDR Quang Trở — Đọc ánh sáng, bật đèn khi tối
* Board: Arduino Uno
* Kết nối:
* Voltage divider: 5V → [10kΩ] → A0 → [LDR] → GND
* LED/Relay → Pin 9 (OUTPUT)
*
* Hiệu chỉnh: Chạy code lần đầu, đọc giá trị ADC trong
* điều kiện TỐI và SÁNG để set DARK_THRESHOLD
*/
const int LDR_PIN = A0;
const int LED_PIN = 9; // LED hoặc relay — bật khi tối
// Ngưỡng — ĐỌC GIÁ TRỊ THỰC TẾ TRƯỚC!
// Khi tối: ADC cao (gần 1023 với R pull-up=10kΩ)
// Khi sáng: ADC thấp
// Ngưỡng 600 = bật đèn khi ADC > 600 (tương đối tối)
const int DARK_THRESHOLD = 600;
// Hysteresis: tránh đèn bật tắt liên tục ở ngưỡng
const int HYSTERESIS = 50; // ADC khác biệt 50 đơn vị để thay đổi trạng thái
bool lightOn = false; // Trạng thái đèn hiện tại
void setup() {
Serial.begin(9600);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
Serial.println("=== LDR Đèn Tự Động - Arduino Uno ===");
Serial.println("Chạy lần đầu: đọc ADC trong TỐI và SÁNG để hiệu chỉnh DARK_THRESHOLD");
}
void loop() {
// Đọc 5 lần lấy trung bình để ổn định
int total = 0;
for (int i = 0; i < 5; i++) {
total += analogRead(LDR_PIN);
delay(10);
}
int ldrValue = total / 5;
// Logic với hysteresis
if (!lightOn && ldrValue > DARK_THRESHOLD + HYSTERESIS) {
// Tối → bật đèn
lightOn = true;
digitalWrite(LED_PIN, HIGH);
Serial.print("TỐI → Bật đèn | ADC: "); Serial.println(ldrValue);
} else if (lightOn && ldrValue < DARK_THRESHOLD - HYSTERESIS) {
// Sáng → tắt đèn
lightOn = false;
digitalWrite(LED_PIN, LOW);
Serial.print("SÁNG → Tắt đèn | ADC: "); Serial.println(ldrValue);
}
// In trạng thái mỗi 2 giây
static unsigned long lastPrintTime = 0;
if (millis() - lastPrintTime >= 2000) {
lastPrintTime = millis();
Serial.print("ADC: "); Serial.print(ldrValue);
Serial.print(" | Đèn: "); Serial.println(lightOn ? "BẬT" : "TẮT");
}
delay(200);
}
Code Đọc Ánh Sáng Và Ánh Xạ Ra Lux Ước Tính — Arduino Uno
/*
* LDR — Đọc và ước tính lux từ điện trở
* Board: Arduino Uno
* Kết nối: 5V → [10kΩ] → A0 → [LDR] → GND
*
* Ước tính lux: không chính xác như BH1750 nhưng đủ cho phân loại
* Công thức: lux ≈ 500 / R_LDR(kΩ) — xấp xỉ cho GL5528 ở điều kiện thường
*/
const int LDR_PIN = A0;
const float R_FIXED = 10.0; // kΩ — điện trở cố định trong voltage divider
const float VCC = 5.0; // Volt
void setup() {
Serial.begin(9600);
Serial.println("=== LDR Quang Trở - Arduino Uno ===");
}
float getLDRResistance(int rawADC) {
// Điện áp tại A0
float voltage = (float)rawADC / 1023.0 * VCC;
if (voltage >= VCC) return 9999.0; // Tối hoàn toàn
// Tính điện trở LDR: R_LDR = R_fixed × voltage / (VCC - voltage)
// Sắp xếp lại: khi voltage tại A0 = VCC × LDR/(R_fixed+LDR)
// → LDR = R_fixed × voltage / (VCC - voltage)
float r_ldr = R_FIXED * voltage / (VCC - voltage);
return r_ldr;
}
// Ước tính lux từ điện trở (xấp xỉ GL5528)
float estimateLux(float r_ldr_kohm) {
// GL5528: R(lux) ≈ C × lux^(-gamma), thường C≈500, gamma≈0.8
// Đảo ngược: lux ≈ (C/R)^(1/gamma) — đây là ước tính thô
if (r_ldr_kohm <= 0) return 100000; // Rất sáng
return pow(500.0 / r_ldr_kohm, 1.0 / 0.8);
}
String getLightCategory(float lux) {
if (lux < 1) return "Đêm tối";
if (lux < 10) return "Ánh trăng / Đèn đường yếu";
if (lux < 100) return "Trong nhà tối";
if (lux < 500) return "Trong nhà sáng";
if (lux < 5000) return "Nhiều mây / Bóng râm";
if (lux < 25000) return "Ánh nắng nhạt";
return "Nắng gắt";
}
void loop() {
int rawADC = analogRead(LDR_PIN);
float r_ldr = getLDRResistance(rawADC);
float lux = estimateLux(r_ldr);
Serial.print("ADC: "); Serial.print(rawADC);
Serial.print(" | R_LDR: "); Serial.print(r_ldr, 2); Serial.print("kΩ");
Serial.print(" | ~Lux: "); Serial.print(lux, 0);
Serial.print(" | "); Serial.println(getLightCategory(lux));
delay(1000);
}
Code ESP32 — Điều Khiển Độ Sáng LED Theo Ánh Sáng Phòng
/*
* LDR + ESP32 — Điều chỉnh độ sáng LED tỷ lệ nghịch với ánh sáng phòng
* Board: ESP32 DevKit V1
* Kết nối:
* LDR voltage divider: 3V3 → [10kΩ] → GPIO32 → [LDR] → GND
* LED + điện trở 220Ω: GPIO5 (PWM) → [220Ω] → LED → GND
*
* Logic: Phòng sáng → LED mờ | Phòng tối → LED sáng hơn
* Ứng dụng: màn hình tự điều chỉnh độ sáng theo môi trường
*/
const int LDR_PIN = 32; // ADC1_CH4
const int LED_PIN = 5; // PWM GPIO
// Giới hạn độ sáng LED: LEDC_MIN = sáng tối thiểu, LEDC_MAX = sáng tối đa
const int LED_MIN = 10; // 0-255 (pwm min)
const int LED_MAX = 255; // pwm max
// ADC boundaries — đo thực tế khi tối và khi sáng
// Thay bằng giá trị thực sau khi test
const int ADC_BRIGHT = 500; // ADC khi ánh sáng phòng rất sáng
const int ADC_DARK = 3500; // ADC khi tối
void setup() {
Serial.begin(115200);
analogSetAttenuation(ADC_11db); // Đọc 0-3.3V
analogReadResolution(12); // 12-bit: 0-4095
// Cấu hình PWM cho ESP32
ledcAttachPin(LED_PIN, 0); // Pin, Channel
ledcSetup(0, 5000, 8); // Channel, Tần số 5kHz, 8-bit resolution
Serial.println("=== LDR Tự Điều Chỉnh Độ Sáng - ESP32 ===");
}
void loop() {
// Đọc 10 mẫu lấy trung bình
int total = 0;
for (int i = 0; i < 10; i++) {
total += analogRead(LDR_PIN);
delay(5);
}
int ldrADC = total / 10;
// Map ADC → độ sáng LED (tỷ lệ nghịch — tối thì LED sáng hơn)
// ADC cao (tối) → LED sáng (LED_MAX)
// ADC thấp (sáng) → LED mờ (LED_MIN)
int brightness = map(ldrADC, ADC_BRIGHT, ADC_DARK, LED_MIN, LED_MAX);
brightness = constrain(brightness, LED_MIN, LED_MAX);
ledcWrite(0, brightness); // Đặt PWM
Serial.printf("ADC LDR: %4d | Độ sáng LED: %3d/255\n", ldrADC, brightness);
delay(100);
}
Kết Quả Mong Đợi
=== LDR Tự Điều Chỉnh Độ Sáng - ESP32 ===
ADC LDR: 3521 | Độ sáng LED: 255/255 ← Tối (che LDR)
ADC LDR: 2100 | Độ sáng LED: 148/255 ← Ánh sáng phòng vừa
ADC LDR: 620 | Độ sáng LED: 10/255 ← Sáng (đèn flash chiếu)
Ứng Dụng Thực Tế
| Ứng dụng | Chi tiết |
|---|---|
| Đèn đường tự bật khi tối | LDR + relay, ngưỡng twilight ~50-100 lux |
| Điều chỉnh độ sáng màn hình | Tỷ lệ nghịch với ánh sáng phòng |
| Cảnh báo cửa mở khi có ánh sáng | Tủ, két khi đóng = tối, khi mở = sáng |
| Logger ánh sáng theo thời gian | Kết hợp DS3231 RTC và SD card |
| Tưới cây theo chu kỳ ngày đêm | Ngắt tưới ban đêm (tối) |
| Đèn đọc sách thông minh | Tự điều chỉnh độ sáng theo ánh sáng phòng |
Lưu Ý Khi Sử Dụng
1. Chọn R_fixed phù hợp với dải ánh sáng quan tâm
Công thức tối ưu: Rfixed = √(Rdark × Rbright). Ví dụ: tối tối=1MΩ, sáng phòng=10kΩ → Rfixed ≈ 3.16kΩ. Trong thực tế, 10kΩ là lựa chọn phổ biến vì cân bằng giữa các điều kiện ánh sáng phòng.
2. Hiệu chỉnh ADC thực tế trước khi dùng
Mỗi LDR có đặc tuyến khác nhau. Đặt LDR ở điều kiện tối nhất và sáng nhất của ứng dụng thực tế → đo ADC → dùng map() với 2 giá trị đó. Không dùng ngưỡng cố định từ internet.
3. Hysteresis cho điều khiển ON/OFF
Không dùng 1 ngưỡng: đèn sẽ bật/tắt liên tục khi ánh sáng dao động ở ngưỡng (ví dụ: mây lướt qua mặt trời). Dùng 2 ngưỡng: BẬT khi
4. Tốc độ đáp ứng chậm
LDR có lag 10-100ms. Không dùng đo ánh sáng tần số cao hoặc phát hiện nhấp nháy LED. Với ứng dụng điều khiển đèn/mái che: hoàn toàn ổn.
5. CdS chứa Cadmium — Không phải RoHS
CdS chứa cadmium (Cd), kim loại nặng hạn chế theo RoHS. Không thải bỏ vào rác thông thường. Với ứng dụng cần RoHS compliance: dùng photodiode silicon (BH1750, TEMT6000) thay thế.


