尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

“妹妹出没”系统实战:ESP32人体感应与MQTT消息推送

“妹妹出没”系统实战:ESP32人体感应与MQTT消息推送 “妹妹出没”这个名字来自一个很具体的家庭需求想知道孩子或住在同一屋檐下的妹妹几点到家、什么时候出门又不想在门口安装摄像头。把它做成技术项目本质就是一套“人体存在检测 出入事件记录 手机消息推送”的家庭自动化小系统。本文会带你把这条链路从零跑通用一块 ESP32 开发板配合人体感应模块检测门口是否有人把状态变化通过 MQTT 发布到本地 Broker再让 Python 服务订阅消息、写入 SQLite 并推送到手机最后用一个简单的 Flask 页面查看历史出没记录。适合有一定 Arduino 或 Python 基础、想尝试硬件与服务端联调的读者。读完这条实践链路你能掌握 PIR 人体感应模块怎么接 ESP32、MQTT 的发布与订阅模型怎么落地、消息如何落库和推送以及断电重连、误报排查和生产部署时要注意什么。整条链路用到的组件都很常见成本在几十元到一百多元之间适合作为家庭自动化入门项目反复调优。1. 先确认需求边界出没检测到底检测什么1.1 把“谁、几点、进还是出”拆成可检测的事件一个容易犯的错误是拿到“妹妹出没”这个标题后直接想做一个“能识别是谁回家”的系统。这类需求一旦拆开会发现它包含三个难度完全不同的子问题时间段几点发生。状态门口区域有没有人。身份这个人是谁。第一和第二个问题用一只人体感应传感器加一块单片机就能解决。第三个问题则完全不同它需要摄像头加人脸识别、手机定位地理围栏或者多个传感器配合做方向判断。用单只 PIR 模块无法知道“是谁”只能知道“有人”或“没人”。把预期放在正确的层级上后面的技术选型才不会跑偏。因此本文把“妹妹出没”收敛为检测门口或某个公共区域是否存在人体记录状态发生变化的时刻并在变化时推送通知。这是一个明确、可测试、可验证的目标。至于“识别身份”放到扩展方向里讨论。1.2 三条技术路线的取舍路线识别能力硬件成本隐私风险适合场景ESP32 PIR/毫米波 MQTT区域有人/无人不识别身份低几十到一百多元低不采集图像家庭出入提醒、房间占用检测树莓派 摄像头 OpenCV可统计人体活动扩展可识别人中高需要摄像头和算力高涉及图像采集进阶学习、行为分析手机定位地理围栏可直接识别“谁”到了位置中低主要靠手机中后台定位和权限敏感儿童/老人到家提醒需要安装 App本文选择第一条路线。理由很直接它不依赖手机权限不采集图像数据可以全部留在本地而且 MQTT 这条消息链路对后续扩展同样成立。就算以后换成毫米波雷达或摄像头方案Broker、数据库、推送、看板这些服务端部分仍然可以复用。2. 硬件连接和环境准备ESP32、人体感应模块和 MQTT Broker2.1 硬件清单搭建这套最小系统需要以下材料ESP32 开发板一块推荐 DevKitC 或 NodeMCU-32S带 USB 接口。HC-SR501 PIR 人体感应模块一个或 LD2410 毫米波人体存在模块。杜邦线若干母对公即可。5V 供电的 USB 电源和数据线。一台可以运行 Linux 的机器可以是树莓派、旧笔记本、云服务器或本地虚拟机用来跑 MQTT Broker 和 Python 服务。路由器或交换机组成的局域网保证 ESP32 能访问到 Broker 所在机器。HC-SR501 是最常见的入门款价格低输出逻辑简单适合验证整条链路。LD2410 是毫米波方案能检测静止存在误报更少但通信协议更复杂。下面先用 HC-SR501 把系统跑通它的数字输出直接接 GPIO 即可。2.2 接线方式与注意事项ESP32 引脚HC-SR501 模块说明5VVCC模块供电部分型号需 5VGNDGND必须与 ESP32 共地GPIO4OUT数字信号输出检测到人时为高电平接线时有几个细节值得注意。第一HC-SR501 通常需要 5V 供电而它的 OUT 输出高电平接近 3.3V可以直接接入 ESP32 的 GPIO不需要电平转换。第二不同批次的模块输出电平可能有差异如果发现 GPIO 读数异常先用万用表确认 OUT 在触发时是否为 3.3V 左右。第三接线完成后不要急着上电先检查有没有接反或短路。2.3 安装 Arduino 环境并准备开发板支持在 Arduino IDE 2.x 中先打开“文件 - 首选项”在“附加开发板管理器网址”里填入 ESP32 官方支持包地址https://espressif.github.io/arduino-esp32/package_esp32_index.json保存后在“工具 - 开发板 - 开发板管理器”里搜索 esp32安装由 Espressif 提供的开发板支持包。接着在库管理器里安装 PubSubClient 库这个库负责 MQTT 的发布与订阅。安装完成后选择开发板为 ESP32 Dev Module并选择 ESP32 对应的串口。这里有一个常见问题Windows 下如果串口不出现多半是缺少 USB 驱动需要下载对应芯片的驱动Mac 和 Linux 下一般插上就能识别。2.4 安装 MQTT Broker 并验证收发Broker 选择 Mosquitto轻量、稳定、命令行工具齐全。在 Linux 上安装sudo apt update sudo apt install -y mosquitto mosquitto-clients sudo systemctl enable --now mosquitto默认配置下 Mosquitto 只允许本机访问。为了让 ESP32 能从局域网连接需要新增一个监听配置。在/etc/mosquitto/conf.d/local.conf中写入listener 1883 0.0.0.0 allow_anonymous false password_file /etc/mosquitto/passwdallow_anonymous false表示所有客户端必须使用用户名密码。在家庭局域网里这可以防止同一网络中的其他设备随意向主题发布消息。创建账号sudo mosquitto_passwd -c /etc/mosquitto/passwd esp32上面的命令会创建密码文件并添加 esp32 用户。如果需要再加用户去掉-c参数。重启服务sudo systemctl restart mosquitto验证 Broker 是否正常收发。开两个终端一个订阅mosquitto_sub -h 127.0.0.1 -t test/hello -v -u esp32 -P 你设置的密码另一个发布mosquitto_pub -h 127.0.0.1 -t test/hello -m hello -u esp32 -P 你设置的密码订阅端能打印出test/hello hello说明 Broker 工作正常。这一步很重要后面的固件和服务端调试都需要以 Broker 可用为前提。3. ESP32 固件读取传感器状态并发布 MQTT 消息3.1 固件要做的事ESP32 固件承担三件事连接 WiFi、读取 PIR 引脚电平、把状态变化通过 MQTT 发布到指定主题。这里不要在每次读取到高电平时都发消息而只在状态从“无人”变为“有人”、或从“有人”变为“无人”时发布这样服务端收到的是一条条有意义的事件而不是一堆重复数据。为了让服务端在重启后也能知道当前门口状态发布时可以使用 MQTT 的 retained 消息。retained 消息会被 Broker 保留新订阅者一上线就能收到最近一次状态。3.2 完整固件代码#include WiFi.h #include PubSubClient.h const char* WIFI_SSID 你的WiFi名称; const char* WIFI_PASSWORD 你的WiFi密码; const char* MQTT_HOST 192.168.1.100; // 改成 Broker 所在机器 IP const uint16_t MQTT_PORT 1883; const char* MQTT_USER esp32; const char* MQTT_PASSWORD 你的MQTT密码; const char* MQTT_TOPIC home/presence/door; const int PIR_PIN 4; const unsigned long WARMUP_MS 30000; // 传感器预热时间 const unsigned long STATE_HOLD_MS 5000; // 状态保持时间避免抖动 WiFiClient wifiClient; PubSubClient mqttClient(wifiClient); int lastState -1; unsigned long lastStateChangeAt 0; void setup() { Serial.begin(115200); pinMode(PIR_PIN, INPUT); WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(\nwifi connected); Serial.print(ip: ); Serial.println(WiFi.localIP()); mqttClient.setServer(MQTT_HOST, MQTT_PORT); // 传感器上电后有预热期等待 30 秒后再读取初始状态 Serial.println(warming up sensor...); delay(WARMUP_MS); lastState digitalRead(PIR_PIN); Serial.printf(initial state: %s\n, lastState HIGH ? present : away); } void connectMQTT() { while (!mqttClient.connected()) { Serial.print(connect mqtt...); if (mqttClient.connect(esp32-door-01, MQTT_USER, MQTT_PASSWORD)) { Serial.println(ok); } else { Serial.printf(fail rc%d, retry 5s\n, mqttClient.state()); delay(5000); } } } void publishState(int state) { const char* payload (state HIGH) ? present : away; boolean retained true; if (mqttClient.publish(MQTT_TOPIC, payload, retained)) { Serial.printf(publish ok: %s\n, payload); } else { Serial.printf(publish fail: %s\n, payload); } } void loop() { if (!mqttClient.connected()) { connectMQTT(); } mqttClient.loop(); int state digitalRead(PIR_PIN); unsigned long now millis(); if (state ! lastState (now - lastStateChangeAt) STATE_HOLD_MS) { lastStateChangeAt now; publishState(state); lastState state; } delay(200); }代码里有两个客户端 ID 要特别注意。mqttClient.connect(esp32-door-01, ...)中的第一个参数是 MQTT client ID在同一 Broker 下必须唯一否则后连接的客户端会挤掉先连接的。WiFi 账号密码和 MQTT 密码不要直接写死在共享代码里本地练习可以先用常量准备上传到公开仓库前必须改成配置文件或环境变量。3.3 关键逻辑说明预热期的处理是很多人忽略的一步。HC-SR501 上电后约 20 到 60 秒内可能持续输出高电平如果不对预热期做处理系统启动时会立刻上报一次“有人”产生一条假事件。代码里在 setup 中等待 30 秒再读取初始状态是为了让传感器稳定下来。状态保持时间的处理同样重要。PIR 在高电平持续时间可调如果传感器在边界反复触发GPIO 电平会在短时间内抖动。代码里只有在状态变化且距离上一次变化超过STATE_HOLD_MS时才发布能过滤掉大部分短时抖动。发布时使用 retained 消息服务端订阅后能立即拿到当前状态而不是等到下次变化。要注意retained 消息的语义是“Broker 保留最近一条”如果设备离线时间很长新的订阅者可能会收到过期的状态。更严谨的做法是发布时加一个时间戳字段服务端根据时间判断是否过期。对于家庭提醒场景retained 已经够用。3.4 编译烧录与串口验证在 Arduino IDE 中选择 ESP32 Dev Module选择正确的串口编译上传。打开串口监视器波特率选 115200。能看到 WiFi 连接成功、MQTT 连接成功并且在传感器前摆手时打印publish ok: present人离开一段时间后打印publish ok: away。同时可以在电脑上订阅主题来确认消息确实到了 Brokermosquitto_sub -h 127.0.0.1 -t home/presence/door -v -u esp32 -P 你的MQTT密码如果能看到home/presence/door present和home/presence/door away说明硬件到 Broker 这一段已经通了可以进入服务端开发。4. Python 服务端订阅消息、写入 SQLite 并推送手机通知4.1 服务端职责与依赖服务端要做三件事订阅 MQTT 主题、把事件写入 SQLite、调用推送服务通知手机。用 Python 写最直接依赖只有 paho-mqtt、requests 和 flask。安装pip install paho-mqtt requests flask如果是 paho-mqtt 2.x 版本不同版本在Client构造和回调签名上略有差异。安装后可以先打印版本如果遇到 API 兼容提示按官方示例调整Client的构造参数即可回调逻辑不变。4.2 SQLite 表结构出没记录表只需要记录设备、事件类型和发生时间。SQLite 单文件、零配置非常适合这个规模的项目。CREATE TABLE IF NOT EXISTS presence_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, device TEXT NOT NULL, event TEXT NOT NULL, created_at TEXT NOT NULL );event字段只存in和out两种值分别对应有人进入和人员离开。created_at使用本地时间字符串便于直接阅读也方便 SQL 按天统计。如果以后接入多个传感器device字段可以区分门口、客厅、走廊等位置。4.3 订阅与推送代码import sqlite3 import time import requests import paho.mqtt.client as mqtt MQTT_HOST 127.0.0.1 MQTT_PORT 1883 MQTT_TOPIC home/presence/door SENDKEY 你的Server酱SendKey DB_PATH /opt/presence/presence.db def init_db(): conn sqlite3.connect(DB_PATH) conn.execute( CREATE TABLE IF NOT EXISTS presence_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, device TEXT NOT NULL, event TEXT NOT NULL, created_at TEXT NOT NULL ) ) conn.commit() conn.close() def push_notify(event, created_at): if event in: title 妹妹出没检测到有人进入 desp f{created_at} 门口区域检测到人员进入 else: title 妹妹出没检测到人员离开 desp f{created_at} 门口区域人员已离开 url fhttps://sctapi.ftqq.com/{SENDKEY}.send try: resp requests.post(url, data{title: title, desp: desp}, timeout10) print(push response:, resp.text[:200]) except Exception as exc: print(push error:, exc) def on_connect(client, userdata, flags, rc): print(mqtt connected, rc , rc) client.subscribe(MQTT_TOPIC) def on_message(client, userdata, msg): payload msg.payload.decode(errorsignore).strip() if payload present: event in elif payload away: event out else: print(unknown payload:, payload) return created_at time.strftime(%Y-%m-%d %H:%M:%S) conn sqlite3.connect(DB_PATH) conn.execute( INSERT INTO presence_log(device, event, created_at) VALUES (?, ?, ?), (door, event, created_at) )
返回列表