Series
Series ESP32 & Cảm Biến
  1. Series ESP32 & Cảm biến: Từ đọc dữ liệu cơ bản tới – Theo dõi realtime
  2. 1Series ESP32 & Cảm biến: Bài 1 – Hướng dẫn đọc cảm biến Quang trở (LDR) bằng ESP32: đo cường độ ánh sáng & theo dõi realtime
  3. 2Series ESP32 & Cảm biến: Bài 2 – Đọc cảm biến PIR HC-SR501: phát hiện chuyển động & theo dõi realtime
  4. 3Series ESP32 & Cảm biến: Bài 3 – Hướng dẫn đọc cảm biến Reed Switch (công tắc từ cửa): phát hiện đóng/mở cửa & theo dõi realtime
  5. 4Series ESP32 & Cảm biến: Bài 4 – Đọc cảm biến SW-420 bằng ESP32: phát hiện rung/va chạm & theo dõi realtime
  6. 5Series ESP32 & Cảm biến: Bài 5 – Đọc cảm biến nghiêng (Tilt Switch): phát hiện nghiêng/đổ & theo dõi realtime
  7. 6Series ESP32 & Cảm biến: Bài 6 – Đọc cảm biến Hall A3144: phát hiện từ trường & theo dõi realtime
  8. 7Series ESP32 & Cảm biến: Bài 7 – Đọc cảm biến lửa Flame Sensor: phát hiện ngọn lửa & theo dõi realtime
  9. 8Series ESP32 & Cảm biến: Bài 8 – Đọc cảm biến mưa Rain Sensor: phát hiện mưa & theo dõi realtime
  10. 9Series ESP32 & Cảm biến: Bài 9 – Đọc cảm biến Water Level Sensor: đo mực nước & theo dõi realtime
  11. 10Series ESP32 & Cảm biến: Bài 10 – Đọc cảm biến Sound Sensor (KY-038/KY-037): đo mức âm thanh & theo dõi realtime
  12. 11Series ESP32 & Cảm biến: Bài 11 – Hướng dẫn đọc cảm biến BH1750: đo độ sáng (Lux) & theo dõi realtime
  13. 12Series ESP32 & Cảm biến: Bài 12 – Đọc cảm biến DHT11: đo nhiệt độ/độ ẩm & theo dõi realtime
  14. 13Series ESP32 & Cảm biến: Bài 13 – Đọc cảm biến DHT22 (AM2302): đo nhiệt độ/độ ẩm chính xác hơn & theo dõi realtime
  15. 14Series ESP32 & Cảm biến: Bài 14 – Đọc cảm biến AHT20 (I2C): đo nhiệt độ/độ ẩm ổn định & theo dõi realtime
  16. 15Series ESP32 & Cảm biến: Bài 15 – Hướng dẫn đọc cảm biến SHT31: đo nhiệt độ/độ ẩm chất lượng cao & theo dõi realtime
  17. 16Series ESP32 & Cảm biến: Bài 16 – Hướng dẫn đọc cảm biến BMP280: đo áp suất/độ cao ước tính & theo dõi realtime
  18. 17Series ESP32 & Cảm biến: Bài 17 – Đọc cảm biến BME280: đo nhiệt độ/độ ẩm/áp suất & theo dõi realtime
  19. 18Series ESP32 & Cảm biến: Bài 18 – Đọc cảm biến DS3231 (RTC): giữ thời gian chính xác & theo dõi realtime
  20. 19Series ESP32 & Cảm biến: Bài 19 – Đọc cảm biến DS18B20: đo nhiệt độ (1-Wire) & theo dõi realtime
  21. 20Series ESP32 & Cảm biến: Bài 20 – Đọc cảm biến RC522 (RFID): quét thẻ RFID & theo dõi realtime
  22. 21Series ESP32 & Cảm biến: Tổng kết Level 1 – từ linh kiện cơ bản tới IoT realtime
  23. 22Series ESP32 & Cảm biến: Giới thiệu Level 2 – ESP32 & Cảm biến I2C: từ đọc đơn lẻ tới tư duy hệ thống
  24. 23Series ESP32 & Cảm biến: Bài 21 – Đọc thẻ nhớ MicroSD: ghi log dữ liệu & theo dõi realtime
  25. 24Series ESP32 & Cảm biến: Bài 22 – Đọc cảm biến TCS34725: đo màu sắc RGB & theo dõi realtime
  26. 25Series ESP32 & Cảm biến: Bài 23 – Đọc cảm biến IR Receiver (VS1838B / TSOP): nhận tín hiệu remote & theo dõi realtime
  27. 26Series ESP32 & Cảm biến: Bài 24 – Đọc cảm biến HC-SR04: đo khoảng cách siêu âm & theo dõi realtime
  28. 27Series ESP32 & Cảm biến: Bài 25 – Đọc cảm biến JSN-SR04T: đo khoảng cách chống nước & theo dõi realtime
  29. 28Series ESP32 & Cảm biến: Bài 26 – Đọc cảm biến VL53L0X: đo khoảng cách ToF chính xác & theo dõi realtime
  30. 29Series ESP32 & Cảm biến: Bài 27 – Đọc cảm biến RCWL-0516: phát hiện chuyển động bằng radar & theo dõi realtime
  31. 30Series ESP32 & Cảm biến: Bài 28 – Đọc cảm biến Soil Moisture (capacitive): đo độ ẩm đất & theo dõi realtime
  32. 31Series ESP32 & Cảm biến: Bài 29 – Đọc cảm biến Water Flow YF-S201: đo lưu lượng nước & theo dõi realtime
  33. 32Series ESP32 & Cảm biến: Bài 30 – Đọc cảm biến MQ-2: phát hiện khói/gas & theo dõi realtime
  34. 33Series ESP32 & Cảm biến: Bài 31 – Đọc cảm biến MQ-135: đo chất lượng không khí & theo dõi realtime
  35. 34Series ESP32 & Cảm biến: Bài 35 – Đọc cảm biến GPS NEO-6M: lấy toạ độ GPS & theo dõi realtime
  36. 35Series ESP32 & Cảm biến: Bài 36 – Đọc cảm biến PZEM-004T: đo điện năng AC & theo dõi realtime
  37. 36Series ESP32 & Cảm biến: Bài 37 – Đọc cảm biến INA226: đo dòng, áp DC & theo dõi realtime
  38. 37Series ESP32 & Cảm biến: Bài 38 – Đọc cảm biến vân tay AS608 / R307: nhận diện vân tay & theo dõi realtime
  39. 38Series ESP32 và Cảm biến: Tổng kết Level 2 – Cảm biến I2C & tư duy hệ thống IoT
Kiến thức

Series ESP32 & Cảm biến: Bài 1 – Hướng dẫn đọc cảm biến Quang trở (LDR) bằng ESP32: đo cường độ ánh sáng & theo dõi realtime

Th1 2026
Series ESP32 & Cảm biến: Bài 1 – Hướng dẫn đọc cảm biến Quang trở (LDR) bằng ESP32: đo cường độ ánh sáng & theo dõi realtime

Bài 1 (Level 1) sẽ dùng LDR (quang trở) để đo mức sáng dạng tương đối (0–100%). LDR rất dễ làm nhưng bạn cần mắc mạch chia áp đúng để ESP32 đọc được analog ổn định.

1) Chuẩn bị

Phần cứng

  • ESP32-C3 SuperMini (Tenstar Robot)
  • 1x LDR (GL5528 hoặc tương tự)
  • 1x điện trở 10kΩ (khuyến nghị)
  • Breadboard + dây

Chân dùng trong bài

  • ADC_PIN = GPIO4 (analog đọc giá trị)
  • 3V3 và GND

Lưu ý: ESP32 chỉ chịu 3.3V ở chân GPIO/ADC. Không đưa 5V vào chân ADC.

2) Nối dây (mạch chia áp)

Bạn mắc như sau (đọc ở điểm giữa):

Cách A (phổ biến, sáng lên → giá trị tăng/giảm tuỳ bạn chọn):

3V3 → LDR → (điểm đo) → R 10kΩ → GND

  • Điểm đo nối vào GPIO4
3V3 --- LDR ---+--- GPIO4 (ADC)
               |
              10k
               |
              GND
  • Khi sáng hơn: điện trở LDR giảm → điện áp tại GPIO4 thường tăng (tuỳ vị trí LDR/R).
    Nếu bạn muốn chiều ngược lại, chỉ cần đổi chỗ LDR và điện trở 10k.

3) Đo “cường độ ánh sáng” thế nào cho đúng?

LDR không đo lux chuẩn như BH1750. Ở bài này ta đo:

  • Raw ADC: 0 → 4095
  • Voltage: 0.0 → 3.3V
  • Light %: chuẩn hoá 0 → 100% (tương đối)

Để ổn định hơn, ta dùng trung bình trượt (moving average).

Ví dụ 1 — Code local đơn giản (đọc LDR + in Serial)

Mục tiêu: cắm dây xong là thấy số, có % độ sáng.

// ===== LDR Local Read (ESP32-C3) =====
const int ADC_PIN = 4;          // GPIO4
const int ADC_MAX = 4095;       // 12-bit
const float VREF = 3.3;         // ESP32 ADC reference ~3.3V

// Moving average
const int N = 10;
int buf[N];
int idx = 0;
long sum = 0;

int readAdcAvg() {
  int x = analogRead(ADC_PIN);
  sum -= buf[idx];
  buf[idx] = x;
  sum += buf[idx];
  idx = (idx + 1) % N;
  return (int)(sum / N);
}

void setup() {
  Serial.begin(115200);
  delay(200);

  // ESP32 Arduino: analogRead default 12-bit on many cores,
  // but keep it explicit if supported:
  // analogReadResolution(12);

  // init avg buffer
  for (int i = 0; i < N; i++) buf[i] = 0;

  pinMode(ADC_PIN, INPUT);
  Serial.println("LDR local read started...");
}

void loop() {
  int raw = readAdcAvg();
  float v = (raw * VREF) / ADC_MAX;

  // Light percent (tương đối)
  // Nếu bạn thấy chiều % bị ngược (sáng lên mà % giảm),
  // hãy đổi: pct = 100 - pct;
  int pct = (raw * 100) / ADC_MAX;

  Serial.print("raw=");
  Serial.print(raw);
  Serial.print("  v=");
  Serial.print(v, 3);
  Serial.print("V  light=");
  Serial.print(pct);
  Serial.println("%");

  delay(500);
}

✅ Kết quả bạn sẽ thấy Serial kiểu:
raw=1234 v=0.994V light=30%

Ví dụ 2 — Gửi IoTLabs Cloud MQTT theo dõi realtime

4) Payload & Topic gợi ý

Topic (chuẩn IoTLabs bạn đang dùng):
iotlabs/<orgId>/devices/<deviceId>/telemetry

Payload JSON:

{
  "ts": 1760000000,
  "metrics": { "light_raw": 1234, "light_pct": 30, "light_v": 0.994 },
  "status": { "ok": true }
}

5) Code MQTT realtime (WiFi + MQTT + gửi JSON)

Ghi chú: Mình để setInsecure() để demo nhanh TLS. Khi production, bạn nên cấu hình CA cert chuẩn.

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <HTTPClient.h>

// ====== CONFIG ======
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_PASS";

// IoTLabs MQTT
const char* MQTT_HOST = "mqtt.iotlabs.vn";
const int   MQTT_PORT = 8883; // TLS
const char* MQTT_USER = "YOUR_MQTT_USER";
const char* MQTT_PASS = "YOUR_MQTT_PASS";
const char* MQTT_TOPIC = "iotlabs/<orgId>/devices/<deviceId>/telemetry";

// Time service (optional but recommended for real epoch ts)
const char* TIME_URL = "https://time.iotlabs.vn/"; // returns ms timestamp as text

// ====== LDR ======
const int ADC_PIN = 4;
const int ADC_MAX = 4095;
const float VREF = 3.3;

const int N = 10;
int buf[N];
int idx = 0;
long sum = 0;

WiFiClientSecure net;
PubSubClient mqtt(net);

unsigned long lastSendMs = 0;
const unsigned long SEND_EVERY_MS = 1000;

long long epochSec = 0; // cached epoch seconds
unsigned long lastTimeSyncMs = 0;
const unsigned long TIME_SYNC_EVERY_MS = 5UL * 60UL * 1000UL; // 5 minutes

int readAdcAvg() {
  int x = analogRead(ADC_PIN);
  sum -= buf[idx];
  buf[idx] = x;
  sum += buf[idx];
  idx = (idx + 1) % N;
  return (int)(sum / N);
}

bool syncTimeEpoch() {
  if (WiFi.status() != WL_CONNECTED) return false;

  HTTPClient http;
  http.begin(TIME_URL);
  int code = http.GET();
  if (code != 200) {
    http.end();
    return false;
  }

  String s = http.getString();
  http.end();

  // service returns ms as text
  long long ms = s.toInt();
  if (ms <= 0) return false;

  epochSec = ms / 1000;
  lastTimeSyncMs = millis();
  return true;
}

void connectWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  Serial.print("WiFi connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(400);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected!");
}

void connectMQTT() {
  // Quick demo: skip cert validation (OK for test)
  net.setInsecure();

  mqtt.setServer(MQTT_HOST, MQTT_PORT);

  Serial.print("MQTT connecting");
  while (!mqtt.connected()) {
    if (mqtt.connect("esp32c3-ldr", MQTT_USER, MQTT_PASS)) {
      Serial.println("\nMQTT connected!");
      break;
    }
    Serial.print(".");
    delay(800);
  }
}

long long nowEpochSec() {
  // If we have epochSec synced, estimate using millis drift:
  if (epochSec > 0) {
    unsigned long dt = (millis() - lastTimeSyncMs) / 1000;
    return epochSec + dt;
  }
  // Fallback (not real epoch): still works for realtime viewing
  return (long long)(millis() / 1000);
}

void setup() {
  Serial.begin(115200);
  delay(200);

  for (int i = 0; i < N; i++) buf[i] = 0;
  pinMode(ADC_PIN, INPUT);

  connectWiFi();

  // time sync once at start (optional)
  if (syncTimeEpoch()) Serial.println("Time synced OK.");
  else Serial.println("Time sync failed (will fallback to uptime seconds).");

  connectMQTT();
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) connectWiFi();
  if (!mqtt.connected()) connectMQTT();
  mqtt.loop();

  // Re-sync time occasionally
  if (millis() - lastTimeSyncMs > TIME_SYNC_EVERY_MS) {
    syncTimeEpoch();
  }

  if (millis() - lastSendMs >= SEND_EVERY_MS) {
    lastSendMs = millis();

    int raw = readAdcAvg();
    float v = (raw * VREF) / ADC_MAX;
    int pct = (raw * 100) / ADC_MAX;

    long long ts = nowEpochSec();

    // Build JSON payload
    // (String is ok for small payload; optimize later if needed)
    String payload = "{";
    payload += "\"ts\":" + String(ts) + ",";
    payload += "\"metrics\":{";
    payload += "\"light_raw\":" + String(raw) + ",";
    payload += "\"light_pct\":" + String(pct) + ",";
    payload += "\"light_v\":" + String(v, 3);
    payload += "},";
    payload += "\"status\":{\"ok\":true}";
    payload += "}";

    bool ok = mqtt.publish(MQTT_TOPIC, payload.c_str());
    Serial.print("Publish: ");
    Serial.print(ok ? "OK  " : "FAIL ");
    Serial.println(payload);
  }
}

6) Lỗi thường gặp & cách xử lý nhanh

  • Giá trị nhảy loạn: dây dài, tiếp xúc kém → dùng moving average (đã có) + rút ngắn dây + kiểm tra GND chung.
  • Sáng/tối bị ngược: đổi vị trí LDR và điện trở 10k, hoặc dùng pct = 100 - pct;
  • Không lên MQTT: kiểm tra host/port/user/pass/topic; test trước bằng mosquitto_pub trên máy.

7) Bài tiếp theo (Level 1)

  • PIR HC-SR501 (phát hiện chuyển động)
  • Reed switch (cửa)
  • SW-420 (rung)