IoTLabs

Nghiên cứu, Sáng tạo và Thử nghiệm

ESP32-S3-DevKitC N16R8 CAM: Bài 14 – Dự án Camera an ninh mini

Giới thiệu

Bài 14 trình bày một dự án thực tế với ESP32-S3-DevKitC N16R8 CAM: Camera an ninh mini. Thiết bị sẽ chụp ảnh (snapshot) khi xảy ra sự kiện, lưu vào thẻ microSD và có thể xem lại qua WiFi. Bài này tập trung vào tư duy thiết kế, độ ổn định và khả năng chạy lâu dài.

Mục tiêu bài học

Sau bài này, bạn sẽ:

  • Hiểu mô hình camera an ninh chụp ảnh theo sự kiện
  • Biết cách thiết kế luồng xử lý ít tốn bộ nhớ
  • Xây dựng hệ thống snapshot ổn định, chạy 24/7
  • Biết cách mở rộng thêm cảm biến hoặc AI sau này

Tổng quan dự án Camera an ninh mini

Khác với camera stream liên tục, camera an ninh mini chỉ chụp ảnh khi cần, ví dụ:

  • Theo thời gian định kỳ (giả lập sự kiện)
  • Theo nút bấm
  • Theo tín hiệu ngoại vi (PIR, công tắc, UART, I2C)

Cách này giúp:

  • Giảm tải PSRAM và heap
  • Tiết kiệm điện
  • Tăng độ ổn định khi chạy lâu

Kiến trúc tổng thể

Luồng hoạt động cơ bản:

  1. ESP32-S3 khởi động
  2. Khởi tạo camera + microSD
  3. Chờ sự kiện xảy ra
  4. Chụp ảnh
  5. Lưu file vào microSD
  6. Ghi log (nếu cần)
  7. Quay về chế độ chờ

Các loại sự kiện có thể áp dụng

Sự kiện giả lập (để test)

  • Chụp ảnh mỗi 30 giây / 1 phút

Sự kiện bằng nút bấm

  • Nhấn nút → chụp ảnh

Sự kiện bằng cảm biến

  • PIR phát hiện chuyển động
  • Công tắc cửa
  • Tín hiệu từ MCU khác qua UART/I2C

Trong bài này, chúng ta dùng sự kiện giả lập bằng timer để dễ theo dõi và debug.


Quy ước đặt tên file và thư mục

Cấu trúc đề xuất:

/snapshots/
  2025-01-01/
    IMG_0001.jpg
    IMG_0002.jpg

Quy ước tên file:

  • Có tiền tố IMG_
  • Tăng dần theo số thứ tự
  • Tránh trùng tên và dễ duyệt

Khởi tạo camera (nhắc lại)

Sử dụng camera_config_t giống Bài 09 và Bài 10, ưu tiên:

  • Frame size: SVGA hoặc VGA
  • jpeg_quality: 12–15
  • fb_count: 2 (nếu có PSRAM)

Code ví dụ hoàn chỉnh: Camera an ninh mini

#include <Arduino.h>
#include "esp_camera.h"
#include "FS.h"
#include "SD_MMC.h"

unsigned long lastShot = 0;
const unsigned long SNAP_INTERVAL = 30000; // 30 giây
int imageIndex = 0;

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

  // Khởi tạo SD
  if (!SD_MMC.begin()) {
    Serial.println("Không thể khởi tạo SD_MMC");
    return;
  }

  // Khởi tạo camera (giả lập, cần map đúng GPIO thực tế)
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_SVGA;
  config.jpeg_quality = 12;
  config.fb_count = 2;

  if (esp_camera_init(&config) != ESP_OK) {
    Serial.println("Camera init thất bại");
    return;
  }

  Serial.println("Camera an ninh mini sẵn sàng");
}

void loop() {
  unsigned long now = millis();

  if (now - lastShot >= SNAP_INTERVAL) {
    lastShot = now;
    takeSnapshot();
  }
}

void takeSnapshot() {
  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Chụp ảnh thất bại");
    return;
  }

  String path = "/snapshots/IMG_" + String(imageIndex++) + ".jpg";
  File file = SD_MMC.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("Không mở được file");
    esp_camera_fb_return(fb);
    return;
  }

  file.write(fb->buf, fb->len);
  file.close();

  Serial.println("Đã lưu: " + path);
  esp_camera_fb_return(fb);
}

Lưu ý để chạy ổn định lâu dài

  • Không chụp ảnh quá liên tục
  • Giảm frame size nếu thấy reset
  • Đảm bảo nguồn cấp ổn định (dòng đủ)
  • Test camera độc lập trước khi kết hợp WiFi

Hướng mở rộng trong tương lai

  • Kết hợp PIR để chụp theo chuyển động
  • Gửi ảnh qua WiFi / MQTT
  • Thêm AI nhận diện (người, vật thể)
  • Kết hợp Xiaozhi AI