4. 智能家居安防监控
任务目标
在ESP32-S3开发板上接入OV2640摄像头,实现基于帧差法的运动检测。当检测到运动时,点亮LED并触发蜂鸣器报警,同时通过WiFi将报警消息推送到手机(使用Bark推送服务,iOS/Android均可接收)。
硬件与依赖
- ESP32-S3开发板(含PSRAM,推荐ESP32-S3-DevKitC-1)
- OV2640摄像头模块(FPC排线连接)
- LED(GPIO 4)与蜂鸣器(GPIO 5),各串联220Ω电阻
- Arduino IDE 2.x,安装
esp32开发板包(≥2.0.6) - 库:
WiFi.h(内置)、esp_camera.h(内置)
实现原理
核心采用帧差法:连续捕获两帧灰度图像,计算像素差值,超过阈值(20)的像素点计数。若差异像素占比超过画面总像素的0.5%,判定为运动。检测到运动后置位报警标志,持续报警10秒(避免频繁误触发),同时通过HTTPS请求调用Bark服务推送消息到手机。
关键完整代码
#include "esp_camera.h"
#include <WiFi.h>
#include <HTTPClient.h>
// WiFi配置
const char* WIFI_SSID = "your_wifi";
const char* WIFI_PASS = "your_password";
// Bark推送配置(在手机安装Bark应用后,从App获取此URL)
const char* BARK_URL = "https://api.day.app/YOUR_BARK_KEY/";
// 引脚定义
#define LED_PIN 4
#define BUZZER_PIN 5
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 10
#define SIOD_GPIO_NUM 40
#define SIOC_GPIO_NUM 39
#define Y9_GPIO_NUM 48
#define Y8_GPIO_NUM 11
#define Y7_GPIO_NUM 12
#define Y6_GPIO_NUM 14
#define Y5_GPIO_NUM 16
#define Y4_GPIO_NUM 18
#define Y3_GPIO_NUM 17
#define Y2_GPIO_NUM 15
#define VSYNC_GPIO_NUM 38
#define HREF_GPIO_NUM 47
#define PCLK_GPIO_NUM 13
// 运动检测参数
#define DIFF_THRESHOLD 20
#define MOTION_RATIO 0.005
#define ALARM_DURATION_MS 10000
static camera_config_t camera_config = {
.pin_pwdn = PWDN_GPIO_NUM,
.pin_reset = RESET_GPIO_NUM,
.pin_xclk = XCLK_GPIO_NUM,
.pin_sccb_sda = SIOD_GPIO_NUM,
.pin_sccb_scl = SIOC_GPIO_NUM,
.pin_d7 = Y9_GPIO_NUM,
.pin_d6 = Y8_GPIO_NUM,
.pin_d5 = Y7_GPIO_NUM,
.pin_d4 = Y6_GPIO_NUM,
.pin_d3 = Y5_GPIO_NUM,
.pin_d2 = Y4_GPIO_NUM,
.pin_d1 = Y3_GPIO_NUM,
.pin_d0 = Y2_GPIO_NUM,
.pin_vsync = VSYNC_GPIO_NUM,
.pin_href = HREF_GPIO_NUM,
.pin_pclk = PCLK_GPIO_NUM,
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_GRAYSCALE,
.frame_size = FRAMESIZE_QVGA, // 320x240
.jpeg_quality = 12,
.fb_count = 2
};
static bool initCamera() {
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK) {
Serial.printf("摄像头初始化失败: 0x%x\n", err);
return false;
}
return true;
}
static void sendAlarm() {
HTTPClient http;
String url = String(BARK_URL) + "智能家居报警/检测到入侵!";
http.begin(url);
int code = http.GET();
if (code > 0) {
Serial.printf("推送成功,HTTP %d\n", code);
} else {
Serial.printf("推送失败: %s\n", http.errorToString(code).c_str());
}
http.end();
}
static bool detectMotion() {
camera_fb_t* fb1 = esp_camera_fb_get();
if (!fb1) return false;
delay(50); // 间隔50ms捕获第二帧
camera_fb_t* fb2 = esp_camera_fb_get();
if (!fb2) {
esp_camera_fb_return(fb1);
return false;
}
size_t len = fb1->len;
if (len != fb2->len) {
esp_camera_fb_return(fb1);
esp_camera_fb_return(fb2);
return false;
}
int diffCount = 0;
uint8_t* p1 = fb1->buf;
uint8_t* p2 = fb2->buf;
for (size_t i = 0; i < len; i++) {
int diff = abs((int)p1[i] - (int)p2[i]);
if (diff > DIFF_THRESHOLD) diffCount++;
}
esp_camera_fb_return(fb1);
esp_camera_fb_return(fb2);
float ratio = (float)diffCount / len;
Serial.printf("差异像素占比: %.3f%%\n", ratio * 100);
return (ratio > MOTION_RATIO);
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi已连接");
if (!initCamera()) {
Serial.println("摄像头初始化失败,系统停止");
while (1) delay(1000);
}
Serial.println("摄像头就绪,开始监控");
}
void loop() {
if (detectMotion()) {
Serial.println("检测到运动!触发报警");
digitalWrite(LED_PIN, HIGH);
digitalWrite(BUZZER_PIN, HIGH);
sendAlarm();
delay(ALARM_DURATION_MS);
digitalWrite(LED_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
}
delay(200);
}
运行与验证
- 将代码中的
WIFI_SSID、WIFI_PASS替换为实际WiFi信息;BARK_URL中的YOUR_BARK_KEY替换为手机Bark应用分配的密钥。 - Arduino IDE中选择开发板
ESP32S3 Dev Module,开启PSRAM(Tools → PSRAM → Enabled),选择对应串口后上传。 - 打开串口监视器(115200波特率),观察日志:
WiFi已连接
摄像头就绪,开始监控
差异像素占比: 0.012%
差异像素占比: 0.008%
静止场景下占比应长期低于0.5%。
- 在摄像头前挥手或走动,串口应输出:
差异像素占比: 3.245%
检测到运动!触发报警
推送成功,HTTP 200
同时LED点亮、蜂鸣器鸣响约10秒,手机Bark应用收到报警通知。
通过条件:静止时无报警;人为运动时LED/蜂鸣器动作且手机收到推送;串口日志中差异占比与报警信息对应。
常见问题
- 摄像头初始化失败:检查FPC排线是否插紧,确认开发板PSRAM已开启。
- 推送失败:确认Bark URL格式正确,手机与ESP32处于同一网络或ESP32可访问外网。
- 误报频繁:适当提高
MOTION_RATIO(如0.01)或增大DIFF_THRESHOLD。 - 检测不到运动:降低
DIFF_THRESHOLD至15,或缩短两帧间隔(减少delay至30ms)。