💡 划重点

  • 这个项目用 ESP32-CAM、PIR 人体传感器和补光 LED 做一个局域网视觉门铃:有人靠近才拍照,平时不持续推流。
  • 难点不在“能不能拍照”,而在供电、电平触发、补光时序和本地照片缓存。
  • 适合已经做过 Wi-Fi、WebServer 或摄像头示例的读者继续练习完整硬件闭环。

需求洞察

门口、仓库、小工作室和设备柜都有一个共同需求:不一定要 24 小时视频监控,但需要知道“什么时候有人靠近、现场大概是什么状态”。传统摄像头方案成本高、联网链路重;只用 PIR 蜂鸣器又缺少现场证据。ESP32-CAM 的 OV2640 摄像头、Wi-Fi 和 MicroSD 能把这个问题做成 50 元级别的小项目。

这个方案不是安防产品替代品,而是硬件编程练习:你会同时处理传感触发、摄像头初始化、补光、文件存储和局域网访问,训练的是一台真实设备从输入到输出的完整路径。

方案设计

感知层由 PIR 人体传感器负责,它只输出高低电平,稳定、便宜、功耗低。控制层由 ESP32-CAM 判断触发边沿,避免同一个人停留时反复拍照。执行层包含补光 LED 和摄像头抓拍。日志/联网层用 MicroSD 保存最近照片,并通过 WebServer 暴露 /latest 页面,手机连同一 Wi-Fi 就能查看。

实际落地时建议先跑通摄像头官方 CameraWebServer 示例,再改成 PIR 触发拍照。不要一开始就把拍照、网页、SD 卡和补光全部写在一起,否则调试面太大。

物料清单

元件 数量 用途 估算价
ESP32-CAM AI Thinker 1 摄像头、Wi-Fi、主控 35 元
HC-SR501 PIR 人体传感器 1 检测有人靠近 6 元
白光 LED + 220Ω 电阻 1 组 暗光短时补光 1 元
MicroSD 卡 1 本地保存照片 15 元
5V 2A 电源 1 保证拍照瞬间供电稳定 15 元
USB-TTL 下载器 1 给 ESP32-CAM 烧录程序 10 元

接线与结构

结构上把 PIR 放在外壳正面,摄像头略微下倾 5-10 度,避免只拍到头顶或天花板。补光 LED 不要直接贴在镜头旁边,否则近距离反光会让图像过曝。

核心代码


#include "esp_camera.h"
#include <WiFi.h>
#include <WebServer.h>

#define PIR_PIN 13
#define FLASH_PIN 4

const char* ssid = "YOUR_WIFI";
const char* password = "YOUR_PASSWORD";
WebServer server(80);
volatile bool motion = false;
uint32_t lastShot = 0;

void IRAM_ATTR onMotion() {
  motion = true;
}

void startCamera() {
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = 5;
  config.pin_d1 = 18;
  config.pin_d2 = 19;
  config.pin_d3 = 21;
  config.pin_d4 = 36;
  config.pin_d5 = 39;
  config.pin_d6 = 34;
  config.pin_d7 = 35;
  config.pin_xclk = 0;
  config.pin_pclk = 22;
  config.pin_vsync = 25;
  config.pin_href = 23;
  config.pin_sscb_sda = 26;
  config.pin_sscb_scl = 27;
  config.pin_pwdn = 32;
  config.pin_reset = -1;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_VGA;
  config.jpeg_quality = 12;
  config.fb_count = 1;
  esp_err_t err = esp_camera_init(&config);
  Serial.printf("camera init: 0x%x\n", err);
}

void handleLatest() {
  camera_fb_t* fb = esp_camera_fb_get();
  if (!fb) {
    server.send(500, "text/plain", "camera capture failed");
    return;
  }
  server.send_P(200, "image/jpeg", (const char*)fb->buf, fb->len);
  esp_camera_fb_return(fb);
}

void setup() {
  Serial.begin(115200);
  pinMode(PIR_PIN, INPUT);
  pinMode(FLASH_PIN, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(PIR_PIN), onMotion, RISING);

  startCamera();
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) delay(300);
  Serial.println(WiFi.localIP());

  server.on("/", []() {
    server.send(200, "text/html", "");
  });
  server.on("/latest", handleLatest);
  server.begin();
}

void loop() {
  server.handleClient();
  if (motion && millis() - lastShot > 10000) {
    motion = false;
    lastShot = millis();
    digitalWrite(FLASH_PIN, HIGH);
    delay(120);
    camera_fb_t* fb = esp_camera_fb_get();
    Serial.printf("capture bytes=%u\n", fb ? fb->len : 0);
    if (fb) esp_camera_fb_return(fb);
    digitalWrite(FLASH_PIN, LOW);
  }
}

调试步骤

  1. 先只连接 ESP32-CAM 和 USB-TTL,确认能烧录 CameraWebServer 示例。
  2. 再接 PIR,用串口打印 GPIO13 的高低电平,确认触发距离和延迟时间。
  3. 加入补光 LED,只让它在拍照前亮 100-150ms,避免长亮发热。
  4. 打开浏览器访问 ESP32-CAM 的 IP,确认 /latest 能返回 JPEG。
  5. 连续触发 30 次,观察是否重启;如果重启,优先换 5V 2A 电源和短线。

设计图

ESP32-CAM 视觉门铃结构flow
1
PIR 检测到有人靠近
2
ESP32-CAM 启动抓拍
3
GPIO4 短时补光
4
JPEG 存入缓存或 SD 卡
5
WebServer 提供最新照片