💡 划重点
- 这个项目用 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 的 OUT 接 GPIO13,VCC 接 5V,GND 与 ESP32-CAM 共地。
- 补光 LED 通过 220Ω 电阻接 GPIO4;AI Thinker 板上 GPIO4 通常也连着板载闪光灯。
- ESP32-CAM 下载程序时 GPIO0 接 GND,运行时 GPIO0 悬空。
- 5V 电源要有足够余量,摄像头启动和 Wi-Fi 发包时电流尖峰明显,电脑 USB 口经常导致棕断重启。
结构上把 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);
}
}
调试步骤
- 先只连接 ESP32-CAM 和 USB-TTL,确认能烧录 CameraWebServer 示例。
- 再接 PIR,用串口打印 GPIO13 的高低电平,确认触发距离和延迟时间。
- 加入补光 LED,只让它在拍照前亮 100-150ms,避免长亮发热。
- 打开浏览器访问 ESP32-CAM 的 IP,确认
/latest能返回 JPEG。 - 连续触发 30 次,观察是否重启;如果重启,优先换 5V 2A 电源和短线。
设计图
ESP32-CAM 视觉门铃结构flow
1
PIR 检测到有人靠近
→
2
ESP32-CAM 启动抓拍
→
3
GPIO4 短时补光
→
4
JPEG 存入缓存或 SD 卡
→
5
WebServer 提供最新照片