一个人的家,也有守卫 ——面向独居女性的居家与随身双端安全报警系统

2026-11-123
精华项目

一个人的家,也有守卫

——面向独居女性的居家与随身双端安全报警系统

做中学·探未来|科学教育实践案例 方向:工程学(物联网与智能硬件)

作品信息

项目

内容

作品名称

一个人的家,也有守卫——面向独居女性的居家与随身双端安全报警系统

主题方向

工程学:物联网通信(MQTT)、传感器应用、移动应用开发、结构设计

适用学段

初中(7—9 年级)

使用对象

独居女性、独居老人;也适用于短期独居的学生宿舍、出租房

主控

FireBeetle ESP32-E(ESP-WROOM-32E,支持 Wi-Fi)

传感器

霍尔磁性 ×2、MQ-2 烟雾、DHT22 温湿度、火焰传感器、SOS 按钮

通信

MQTT 协议,阿里云物联网平台转发

软件

Arduino IDE(C++)、App Inventor 2019 桌面版、3DMAX、3D 打印机

系统规模

1 个产品、3 个设备、3 个自定义 Topic、3 条消息流转规则

 

一、项目介绍

1.1 起因:一条新闻,和一个不敢开的门

这个项目最早不是从“我想做个物联网装置”开始的,是从一件很小的事开始的:有天晚上十点多,我听见门外有持续的脚步声和金属摩擦声,第一反应不是去猫眼看,而是把客厅灯关了、站在门后不动。后来发现是邻居在搬东西。但那十几分钟里,我确实什么也做不了——不敢开门看,也不敢打电话,因为不确定是不是自己想多了。

查资料时我看到,针对独居女性的入室、尾随类案件在报道中有增多趋势。市面上的安防产品其实不少,但调研下来有三个问题:一是整套产品绑死在一家公司的 App 里,功能是人家定好的,我想加“下水道漫水”这种偏门的需求就加不进去;二是要换就得把原有电器、门锁一起换掉,成本很高;三是绝大多数产品只管“家里”,一旦出门就完全失效——而尾随、跟踪恰恰发生在路上。

所以这个项目要做两件事:把家变成一个会报警的地方,再给出门的自己留一个按键。

1.2 我把担心拆成了六个可以测量的疑问

“做个安全系统”这句话没法验证。我把它拆成六个能用“通过/不通过”回答的问题,后面的硬件选型、程序结构和测试记录都围绕这六个问题展开:

编号

探究问题

怎么算通过

Q1

门窗被打开时,装置能不能在几秒内发出报警?

分开磁铁后,现场声光报警在 2 秒内启动,手机端同步显示

Q2

漏水、着火、烟雾三种异常,能不能分别认出来?

三种情况分别触发,且手机端显示的图标与实际情况对应

Q3

报警能不能送到手机上,而不是只在现场响?

人不在家、手机在另一个网络下,仍能收到提示

Q4

离家时能不能远程把报警打开或关掉?

在 App 上点开关,装置端继电器与蜂鸣器的状态随之改变

Q5

按下随身装置的按钮,手机能不能收到?

按键后手机收到一条带时间戳的呼救消息

Q6

断网以后,这套系统还能做什么?

本地声光报警是否仍然有效(本项只做了设计推演,未实测)

 

第六个问题是后来才想明白要加的。做测试时我把路由器关掉试了一次,发现手机上什么都没有,但现场的蜂鸣器还在响——这说明本地报警和远程报警是两条独立的路,断网并不会让装置变成一块砖。这一点后来直接影响了我对“报警”这件事的理解:先保证现场有反应,再谈远程。

1.3 它怎么用:在家看四个数,出门按一个键

整套系统分成两半,共用同一个云平台和同一个手机 App。

· 居家卫士 贴在门框和窗框上的两个霍尔传感器管“有没有被打开”;厨房一侧挂 MQ-2 管燃气和烟雾、DHT22 管温度和水、火焰传感器管明火;主控旁边接蜂鸣器、LED 和一路继电器(可以接灯或排风扇)。

· 随身卫士 一块主控、一个红色 SOS 按钮、一块锂电池,装进 3D 打印的白色圆柱外壳里,可以放进外套口袋。危急时按一下,手机立刻收到呼救。

· 手机 App App 上排着四个状态图标(火焰、燃气、漏水、门窗),绿色是正常、红色是报警;下面一行是“报警开启/关闭”的总开关,出门前打开,回家后关掉。

之所以做成“四个状态图标 + 一个总开关”,而不是一长串传感器数值,是因为这个 App 的使用场景是“在单位突然想看一眼前”和“半夜被提示音吵醒”——这两种情况下,人没有耐心去读数字,只想知道“哪一项红了”。

1.4 背后的五条原理

(1)MQTT 为什么适合“设备—云—手机”

这套系统的三端分别在三个地方:传感器在家里,人在外面,云在机房。要让它们互相说话,最直接的想法是“手机直接连家里的板子”,但这样有两个绕不过去的麻烦:家里的宽带大多是动态 IP,手机关掉再打开,地址就变了;而且板子要一直开着端口等外面连进来,既费电又不安全。

MQTT 换了个思路:不让设备互相对话,而是让它们都跟一个“邮局”说话。设备把消息贴上主题标签(Topic)投进邮局,谁订阅了这个主题,邮局就把消息转给谁。这样三端都不需要知道对方的地址,只需要知道邮局在哪。MQTT 还有一个特点:它的报文头最小只有 2 个字节,对用电池供电、算力很小的单片机非常友好——这也是它在物联网里被广泛使用的原因。

(2)云平台不是“服务器”,是“登记处 + 邮局”

阿里云物联网平台做了两件事。第一件是登记:每台设备在平台上都有一个名字和一把钥匙,叫“三元组”——ProductKey(产品编号)、DeviceName(设备名)、DeviceSecret(设备密钥)。设备连上来时要拿这三样算一个签名,平台核对无误才允许通信。

第二件才是转发:管理员事先在平台上写好“谁的消息转给谁”的规则,叫消息流转。我们这个项目配了三条:居家的传感器数据转给手机、随身的呼救转给手机、手机的开关键转给居家主控。写规则这一步完全在浏览器里点鼠标完成,不需要写代码——这也是我选云平台而不是自己搭服务器的原因。

(3)传感器输出的是数字,阈值是人定的

很多人以为传感器会直接告诉你“着火了”。实际上它只给一个数:火焰传感器给的是 0 或 1,MQ-2 给的是 0 到 4095 之间的一个数,DHT22 给的是摄氏温度和百分比湿度。“多少算异常”这件事,机器不知道,必须由人先定一条线。

本项目定的线是:烟雾大于 400、火焰传感器给出信号、温度高于 45 ℃、湿度高于 85 %、霍尔传感器断开。前四条来自反复试出来的经验值——比如温度线如果定在 35 ℃,夏天开空调前就会误报;湿度线如果定在 70 %,南方回南天会天天响。这些数字没有“标准答案”,只有“在你家够不够用”。

这是整个项目里最需要人来判断的地方,也是它区别于“买个成品”的地方——阈值捏在自己手里,才可以按自己家的环境去调。

(4)为什么“发布”和“订阅”要分开写

程序里每台设备都同时做两件事:把自己采集到的数据发布出去,同时订阅别人发给自己的指令。居家主控发布的主题是传感器数据,订阅的主题是手机下发的开关指令;手机反过来。两条路的代码是分开写的,互不干扰——这一点很重要,因为报警上报是“每两秒一次”的定时动作,而接收指令是“随时可能来”的中断动作,混在一起写就容易互相卡住。

(5)3D 打印:外壳不是装饰,是结构

随身装置要放进口袋,就必须解决三个问题:按钮不能被误按、电池不能晃动、板子不能短路。我先在 3DMAX 里按三个零件的实际尺寸(主控 25.4×20.3 mm、锂电池 60×18 mm、按钮直径 12 mm)建了一个圆柱壳,把按钮顶在最上面并做了一圈凸起的护边——这样在口袋里被钥匙顶到也不会触发。打印时用的是 0.2 mm 层高、20 % 填充,打完发现按钮孔小了 0.3 mm,用锉刀扩了一下才装进去。这是“图纸尺寸”和“实物尺寸”的差别,光看模型是发现不了的。

1.5 和已有的做法比,差在哪

做查新时用了三个库:往届大赛获奖作品、中国知网、百度搜索引擎,检索词包括“居家安全卫士”“智能家居报警”“随身一键报警”“便携报警装置”等,筛出六篇最接近的文献。结论整理成下表:

文献做法

它做到了什么

它缺了什么

基于 GSM 的火灾检测报警(STC89C52 + SIM800C)

可燃气体与烟雾监测,短信远程报警

只针对火警,没有防盗,也没有随身部分

51 单片机 + 温度/烟雾的火灾报警

超阈值时语音播报、指示灯亮

同上,功能单一

单片机 + 多传感器 + 手机客户端的火灾报警

室内数据上传手机显示

仍是单场景,没有远程控制

OneNET 云平台的简易智能家居

上传数据、下发指令、按键控灯

用的是平台私有协议 EDP,只能配该平台

STM32 + 多传感器的智能家居

环境数据上云、联动控制

主控成本高,无防盗与随身报警

Arduino Mega2560 无线安防

一氧化碳、温湿度、声音、液位多路监测

只有本地提醒,没有远程报警

 

对比下来,这六篇的共同点是“一个场景 + 一类报警”,而本项目的差异在三点:对象是独居女性这个具体人群;功能上把居家和随身连成一套;技术上用 MQTT 这个公开协议,换平台不用重写程序。检索中没有发现与本课题完全相同的报道。

1.6 对应哪些课程内容

学科

对应内容

信息科技

物联网的三层结构(感知、网络、应用);MQTT 发布—订阅模型;JSON 数据格式;顺序、分支、循环与中断;移动应用的界面与事件

物理

电路的串联与并联;上拉电阻与开关量;传感器的信号形式(模拟量与数字量);电池与功耗

数学

阈值与区间;数据的记录与统计;用表格比较方案

劳动 / 综合实践

元件焊接与接线;3D 建模与打印;产品外壳的结构设计;测试与迭代

道德与法治 / 安全

独居安全与自我保护;报警信息的真实性与责任;技术不能替代报警电话

 

二、运行演示

2.1 居家卫士:一面墙上的七个器件

图 1 是用来做测试的模型房间:左边是厨房操作台,中间是窗户,右边是门。主控和蜂鸣器、LED、继电器装在操作台上方的板子上,几个传感器分散贴在需要监测的位置——门上和窗上各一个霍尔传感器,燃气和烟雾传感器挂在灶台上方,温湿度传感器放在水槽边,火焰传感器放在台面角落。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_1.webp 

图 1 居家卫士的安装位置:门窗各一个霍尔传感器,灶台上方为 MQ-2,水槽边为 DHT22,台面角落为火焰传感器

把这些东西分开装而不是集中在一个盒子里,是被实际情况逼出来的:烟雾会往上飘,所以必须挂在灶台上方;水往低处流,所以温湿度传感器要贴着地面放;火焰传感器的探测角只有 60 度,得斜对着可能起火的台面。“传感器装在哪”和“传感器买哪个”一样重要,位置错了,再好的传感器也测不到。

2.2 随身卫士:一个白色圆柱和一个按键

图 2 是随身卫士的成品。外壳是 3D 打印的,顶面那颗黑色按钮就是 SOS 键,按钮周围有一圈凸起的护边,防止在口袋里被误按。里面装着一块 FireBeetle ESP32-E、一块 3.7 V 锂电池和一个小蜂鸣器。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_2.webp 

图 2 随身卫士成品:3D 打印外壳、顶部 SOS 按钮(带防误触护边)、内置锂电池

它没有屏幕、没有扬声器、没有 GPS——这些都是故意的。第一,口袋里的东西越小越好,功能越多越容易坏;第二,真正的呼救场景里,人多半已经慌了,能做的动作只有一个;第三,少一个部件就少一处耗电,锂电池能撑更久。原计划还要发一条短信到 12110 并附上当前位置,这一部分没有做完,原因写在第六章。

有人问过:手机上不是也能一键报警吗,为什么还要多带一个东西?试过之后我的答案是——手机的一键报警要先解锁、找到图标、点进去、按住不放,四步;而这个装置是一步。更重要的是,手机在那种时刻往往不在手上:可能掉在地上,可能被抢走,可能早就没电。把“报警”这件事从手机上分离出来,本身就是这个装置存在的理由。

2.3 开门:磁铁一分开,两边同时响

霍尔传感器的工作原理是“磁铁靠近时输出一种电平,离开时输出另一种”。把它的一半贴在门框上、另一半贴在门上,门一开,两半分开,电平就变了——这比机械触点开关的好处是不怕磨损,也不会因为门框变形而接触不良。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_3.webp 

图 3 开门测试现场:门被打开后,主控板上的 LED 点亮、蜂鸣器持续鸣响

图 4 是同一次测试中手机端的截图。左边是开门时的状态:门窗那一格变成红色并显示“门开了”,下面的“报警开启”开关是打开的;右边是另一次着火测试的画面,火焰图标变红。两个图标的排布、颜色是一致的,红色只代表这一项异常,绿色代表正常。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_4.webp 

图 4 手机端在两种报警下的界面:左为门窗被打开,右为检测到火焰;红色代表该项异常

2.4 漏水:往传感器上喷水雾

漏水这件事很难“真的漏一次”来验证,因为模型房间没有上下水。测试时用喷壶对着 DHT22 喷了几下,湿度读数迅速升高并越过 85 % 的阈值,装置随即报警,手机端对应图标变红(图 5 左)。这个做法有局限:真实的漏水是“地面出现积水”,而湿度上升只是“空气变潮”,两者不完全等价。更可靠的做法是在地面放一对金属触点,靠水的导电性导通——这一条已列入改进项。

着火测试则用了真实的明火:在火焰传感器前方点燃一小片纸,传感器在 1 米内、60 度角范围内能识别到,装置立刻报警(图 5 右)。测试时旁边备了一杯水,全程有人看着。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_5.webp 

图 5 左:向温湿度传感器喷水雾模拟漏水;右:点燃纸片模拟着火,火焰传感器报警

2.5 随身:按一下,手机就响

随身卫士的测试最直接:把装置放进口袋,走开几米,按一下顶部的按钮,看手机有没有反应。图 6 是按键后手机端的样子——App 弹出告警,同时振动并播放提示音。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_6.webp 

图 6 按下随身卫士的 SOS 按钮后,手机端收到呼救并弹出提示

这一路上最花时间的其实不是程序,而是“按一下只发一次”。最初的写法是检测到按下就发,结果按住不放会连续发几十条,手机被刷屏。后来改成“按住也只算一次,两次之间至少隔 3 秒”,这个问题才解决——这就是程序里的“去抖”,一个在课本上只要一句话、在实物上却能卡住一整天的问题。

2.6 App:四个图标和一个总开关

图 7 是 App 的整体界面,也是我给它写的“说明书”。最上面是联网状态和“选择”下拉框;中间是四个状态图标(火焰、燃气、漏水、门窗);下面一排是报警总开关、以及“发送短信到 12110”的按钮位置。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_7.webp 

图 7 手机 App 界面与各部分的作用说明

做界面时我犯过一个错:一开始把四个传感器数值直接显示在屏幕上,觉得“数据越多越专业”。给家里人试用时,他们的第一反应是“这些数字是什么意思”。后来才改成图标 + 颜色,把数字藏进二级页面。这让我明白一件事:给普通人做的界面,判断结论要比原始数据靠前。

三、探究过程与结果

3.1 选板子:为什么是 FireBeetle ESP32-E

主控要满足四个条件:带 Wi-Fi(要连 MQTT)、引脚够用(至少 5 路传感器 + 3 路输出)、体积小(要塞进圆柱外壳)、便宜。比较了三种常见方案之后选了 FireBeetle ESP32-E:它是 ESP-WROOM-32E 双核芯片,支持 Wi-Fi 和蓝牙双模,板载充电电路(可以直接给锂电池充电,省掉一个充电模块),而且完全兼容 Arduino 开发环境。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_8.webp 

图 8 主要元件:FireBeetle ESP32-E 主控、火焰传感器、DHT22 温湿度传感器、霍尔磁性传感器

四个传感器的分工是这样的:霍尔传感器测“门窗开合”,MQ-2 测“燃气和烟雾”,DHT22 一身二用——温度测“高温”、湿度测“漏水”,火焰传感器测“明火”。这里有一个当时没想到的取巧:一根 DHT22 顶了两个功能。原因很朴素——多一个传感器就多一路线、多一处故障点、多一份钱,能复用的就复用。

3.2 系统怎么搭:三端一云

整套系统的数据流是“两端只和云说话,彼此不直接连接”(图 9)。居家卫士和随身卫士都把消息发布到各自的 Topic 上,阿里云按事先配好的规则转给手机;手机下发的开关指令也先到云,再由云转给居家主控。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_9.webp 

图 9 系统结构:居家端、随身端、云平台、手机 App 四部分,实线为发布,虚线为订阅

三个 Topic 的分工:sas_sensorData 走居家的传感器数据,sas_SOS 走随身的呼救,sas_setAlarm 走手机的开关指令。分开而不是合用一条,是因为三条消息的“性格”不同——传感器数据是高频、定时的,呼救是低频、突发的,指令是人工触发的。混在一条主题里,订阅方就没法只挑自己想要的那一类。

这里还有一个当时纠结了很久的选择:阿里云提供了两种上报方式,一种是“物模型”(先把温度、湿度这些属性在平台上登记成标准格式,再按格式上报),一种是“自定义 Topic”(自己定主题名,内容自己写)。物模型的好处是平台能自动生成看板、能做数据存储,但要求每个字段先定义清楚类型和范围;自定义 Topic 更自由,改字段不用回平台改配置。最后选了后者,原因是这个项目还在频繁改数据结构——先定死格式,反而每改一次都要去平台上动一遍。

3.3 居家的判断逻辑

图 10 是居家主控的程序流程。核心只有一个判断:五个读数里有没有任何一个越过阈值。有就立刻在现场声光报警,同时把数据打包发到云上;没有就照常上报,让 App 显示“正常”。这里我特意把“现场报警”放在“上报”前面——因为网络随时可能断,而人在家里的时间最多。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_10.webp 

图 10 居家卫士的判断流程:先本地报警,再上报云端;手机的关闭指令从右侧单独下发

监测项

传感器

判据

阈值

为什么是这条线

门窗被打开

霍尔 ×2

电平变化(磁铁离开)

断开即报警

只有开/关两个状态,不需要阈值

燃气、烟雾

MQ-2

模拟量(0—4095)

> 400

厨房正常烹饪油烟约 150—300,取 400 留余量

明火

火焰传感器

数字量

有信号即报警

探测角 60°、1 米内,避开灶台直射

高温

DHT22 温度

摄氏温度

> 45 ℃

低于此值夏天不开空调也会误报

漏水

DHT22 湿度

相对湿度

> 85 %

南方回南天可到 80 %,取 85 % 减少误报

 

3.4 随身的判断逻辑

随身端的程序比居家端短得多(图 11):每 100 毫秒看一次按钮,按下了就蜂鸣一声、发一条 sas_SOS。没有“确认识别”“重试”“上传位置”这些步骤——不是没想到,而是在这个场景里,多一步就多一次失败的机会。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_11.webp 

图 11 随身卫士的流程:只有一个判断,按下即上报;短信与位置发送尚未完成

为了省电,程序里没有用“中断”,而是用“定时轮询 + 长间隔”。理论上中断更省电,但在实测中发现按钮的机械抖动会让中断误触发十几次,反而更费电。把轮询间隔放到 100 毫秒,实际响应延迟还不到人能感觉到的程度。

3.5 云平台:四步配置

云平台的配置全部在浏览器里完成,一共四步(图 12):创建产品、添加设备、定义 Topic、配置消息流转。这四步的顺序不能颠倒——没有产品就没有设备,没有设备就没法给 Topic 授权,没有 Topic 就没法写流转规则。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_12.webp 

图 12 阿里云物联网平台的四步配置:产品 SandS、三台设备、三个自定义 Topic、三条流转规则

三台设备的命名有讲究:sas_esp32 是居家主控,sas_sos_esp32 是随身主控,sas_app 是手机。把手机也当成一台“设备”登记进平台,是我做完才想明白的做法——这样手机和两块板子在平台眼里是平等的,都能发布、都能订阅,流转规则写起来也就对称了。

3.6 App 怎么和云说话

App 端用的是 App Inventor,全部是拖积木,不写代码。核心只有三块积木:

· 第一块 连上阿里云:填入 MQTT 服务器地址、端口、ClientID、用户名和密码(由三元组算出来),点“连接”;连上以后订阅 sas_sensorData 和 sas_SOS 两个主题。

· 第二块 收到消息:当 MQTT 组件收到消息时,把 JSON 拆开,逐个判断哪个字段是 1,对应图标就换成红色、播放提示音、手机振动。

· 第三块 发出指令:点总开关时,向 sas_setAlarm 发布一条 {"alarm":1} 或 {"alarm":0}。

这三块积木对应的是“连接—接收—发送”三个动作,和单片机端的结构完全一致。我一开始不理解为什么要这么对称,后来改代码时才明白:两端结构一样,出问题时就能一处一处对着查,不用在两种思维之间来回切换。

3.7 外壳:从尺寸到实物

随身装置的外壳在 3DMAX 里建模,按三个零件的实际尺寸确定内腔:主控板 25.4×20.3 mm、锂电池 60×18 mm、按钮直径 12 mm,壁厚取 2 mm。模型里特意留了两处:一处是按钮孔上方的护边,一处是电池仓的横向筋条——前者防误按,后者防晃动。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_13.webp 

图 13 左:3D 打印完成的零件(壳体、顶盖、按键帽);右:装配后的壳体与顶盖

打印用 0.2 mm 层高、20 % 填充。第一次打印失败了两回:第一回按钮孔小了 0.3 mm,按键按下去弹不回来;第二回壳体太薄,装电池时裂了一道缝。第二版把壁厚从 1.6 mm 加到 2 mm、孔放大 0.3 mm 才装得上。这件事让我第一次意识到:图纸上的尺寸和手里的实物之间,隔着打印机的误差。

3.8 组装

图 14 是居家卫士背面的接线。走线是这个项目里最不“高级”但最耗时的部分:五路传感器、三路输出,加上电源,一共十几根线,全部挤在一块 8 cm 宽的板子后面。第一版走线是“就近插”,结果烟雾传感器的线被门夹断过一次;第二版改成“按功能捆扎、留出活动余量”,才稳定下来。

一个人的家,也有守卫
——面向独居女性的居家与随身双端安全报警系统_image_14.webp 

图 14 居家卫士背面接线:主控、扩展板与各路传感器的连线按功能捆扎

装的时候总结出三条经验,写在这里给复刻的人:电源线(红黑)单独走一边,不要和信号线绞在一起;每根线两端都贴标签,否则拆一次就再也认不出来;接插件比直接焊更可靠——这个项目里有三个传感器是换过的,用接插件换起来只要十秒。

3.9 测试记录

测试采用白盒测试法:已知程序里每个判断条件的位置,逐个构造能触发它的条件,看输出是否符合预期。七项测试的记录如下:

序号

测试项

怎么触发

现场反应

手机端

结论

1

门窗被打开

把门上霍尔传感器的磁铁移开

蜂鸣器持续鸣响、LED 亮

门窗图标变红

通过

2

漏水

用喷壶向 DHT22 喷水雾

同上

漏水图标变红

通过

3

着火

在传感器前点燃纸片

同上

火焰图标变红

通过

4

随身呼救

按一下随身装置的 SOS 键

蜂鸣器短鸣一声

收到呼救并弹窗

通过

5

远程开关

在 App 上点总开关

蜂鸣器状态随之改变

开关状态同步

通过

6

燃气泄漏

——

——

——

未测

7

高温

——

——

——

未测

 

第 6、7 两项没测,原因很实在:模型房间里没有燃气,也不可能把房间加热到 45 ℃ 以上。这两项的代码和电路都已经接好,但“接好”和“验过”是两件事——所以这一栏我写的是“未测”,而不是“通过”。同样地,结论一章里也不再写“已实现漏气报警”,改成“已实现、未实测”。

四、软硬件清单与制作步骤

4.1 元件与工具

类别

名称

数量

说明

主控

FireBeetle ESP32-E

2

居家、随身各一块;板载锂电池充电电路

传感器

霍尔磁性传感器

2

门窗各一;磁铁与探头分离安装

传感器

MQ-2 烟雾/燃气传感器

1

模拟量输出,需接 A 口

传感器

DHT22 温湿度传感器

1

数字量输出,一根线顶两个功能

传感器

火焰传感器

1

数字量输出,探测角 60°

输入

SOS 按钮(带帽)

1

随身装置顶部;建议用带护边的安装方式

输出

有源蜂鸣器、LED、继电器模块

各 1—2

继电器可接灯或排风扇,做联动

电源

3.7 V 锂电池(2800 mAh)

1—2

随身装置供电

结构

3D 打印外壳(PLA)

1 套

0.2 mm 层高,20 % 填充,壁厚 2 mm

工具

面包板、杜邦线、电烙铁、热熔胶

若干

接线与固定

软件

Arduino IDE、App Inventor 2019、3DMAX

——

均为免费软件

 

4.2 接线

元件

接到主控

引脚类型

备注

霍尔传感器(门)

D5

数字输入

磁铁靠近/离开各一种电平,安装前先用万用表确认

霍尔传感器(窗)

D6

数字输入

同上

MQ-2 烟雾/燃气

A0

模拟输入

需要用螺丝刀调板上的电位器,先测无烟时的底数

DHT22 温湿度

D4

数字输入

数据脚需 4.7 kΩ 上拉到 3.3 V

火焰传感器

D2

数字输入

板上有电位器可调灵敏度

蜂鸣器

D7

数字输出

有源蜂鸣器,给高电平即响

LED

D8

数字输出

串 220 Ω 限流电阻

继电器

D9

数字输出

接灯或排风扇,注意市电部分必须断电操作

SOS 按钮(随身)

D3

数字输入

启用内部上拉,按下为低电平

 

4.3 十二步制作流程

第 1 步 读协议:先把 MQTT 的“发布—订阅”弄明白,画一遍三端数据流。这一步不做,后面全是盲抄。

第 2 步 配云:在阿里云创建产品、三台设备、三个自定义 Topic,记下三元组(只记在本地,不要外传)。

第 3 步 点灯:先只让主控连上 Wi-Fi 并成功连到 MQTT,能在平台上看到“在线”,再做别的。

第 4 步 单路上报:接一个传感器,把读数发布到 sas_sensorData,在平台的日志里能看到 JSON。

第 5 步 加传感器:逐个加:霍尔、MQ-2、DHT22、火焰。每加一个,单独验证一次底数与阈值。

第 6 步 本地报警:加蜂鸣器和 LED,先让“本地”完整可用——断网也能响,这是底线。

第 7 步 搭 App:在 App Inventor 里先只做“连接 + 接收”,把收到的 JSON 原样显示出来。

第 8 步 做图标:把 JSON 里的字段映射成四个图标的红/绿,加提示音与振动。

第 9 步 下发指令:做总开关,测试从 App 到主控的反向链路。

第 10 步 做随身端:同一套代码删掉传感器部分,只留按钮与 sas_SOS 发布,加去抖。

第 11 步 打外壳:先打一个“试装件”,确认孔位和螺纹,再打成品。不要一次打全套。

第 12 步 整机测试:按第三章的七项逐条测,把“通过/未测/不通过”如实记下来。

4.4 想复刻的话,可以更便宜

这套东西的成本主要在两块主控上。如果只是想复现功能、不追求体积,可以换成更便宜的替代:

· 主控替代 ESP32 开发板(约二十元)或 ESP8266,同样支持 Wi-Fi 和 MQTT,Arduino 代码几乎不用改,代价是体积变大、没有板载充电电路。

· 云平台替代 阿里云物联网平台有免费额度,个人做实验够用;也可以换成开源的 EMQX 自建服务器,但要自己解决公网访问和外网穿透。

· App 替代 App Inventor 可以换成 MIT 的另一款工具或直接用手机浏览器打开一个网页,后者最省事但拿不到振动和后台提醒。

· 外壳替代 没有 3D 打印机可以用现成的塑料盒开孔,或者用亚克力板 + 铜柱。外壳first作用是“防止误触和短路”,美观是第二位的。

五、开源代码

以下代码按“连接—判断—下发”三段整理,与实际烧录的程序一致,只是把三元组和 Wi-Fi 密码换成了占位符。复刻时请填入自己的参数,不要使用本文档中的任何示例值。

5.1 居家端(一):连接 Wi-Fi 与 MQTT

#include <WiFi.h>

#include <PubSubClient.h>

#include "DHT.h"

 

// ---- 配网信息:复刻时改成自己的 ----

const char* WIFI_SSID = "你的热点名";

const char* WIFI_PASS = "你的热点密码";

 

// ---- 阿里云三元组:复刻时改成自己的 ----

const char* PK  = "你的ProductKey";

const char* DN  = "sas_esp32";

const char* DS  = "你的DeviceSecret";

 

const char* HOST = "你的ProductKey.iot-as-mqtt.cn-shanghai.aliyuncs.com";

const int   PORT = 1883;

// ClientID 格式固定:设备名|安全模式|签名方法|

String CLIENT_ID = String(DN) + "|securemode=3,signmethod=hmacsha1|";

String USERNAME  = String(DN) + "&" + PK;

// 密码由三元组经 HMAC-SHA1 算出,这里填预先算好的字符串

const char* MQTT_PASS = "预先算好的密码";

 

// 三个自定义 Topic(与云平台配置一一对应)

String TOPIC_DATA = String("/") + PK + "/sas_esp32/user/sensorData";

String TOPIC_CMD  = String("/") + PK + "/sas_esp32/user/setAlarm";

 

WiFiClient   net;

PubSubClient client(net);

DHT dht(4, DHT22);        // 温湿度传感器接 D4

 

void connectWifi() {

  WiFi.mode(WIFI_STA);

  WiFi.begin(WIFI_SSID, WIFI_PASS);

  while (WiFi.status() != WL_CONNECTED) {   // 连不上就等,不往下走

    delay(500);

  }

}

 

void connectMqtt() {

  while (!client.connected()) {

    // 密码由三元组经 HMAC-SHA1 算出,这里用预先算好的字符串

    if (client.connect(CLIENT_ID.c_str(), USERNAME.c_str(), MQTT_PASS)) {

      client.subscribe(TOPIC_CMD.c_str());   // 只订阅手机下发的指令

    } else {

      delay(2000);                           // 失败等 2 秒重连,避免刷屏

    }

  }

}

 

5.2 居家端(二):读传感器、判断、上报

#define PIN_HALL   5      // 门窗霍尔

#define PIN_MQ2    A0     // 烟雾/燃气

#define PIN_FLAME  2      // 火焰

#define PIN_DHT    4      // 温湿度

#define PIN_BUZZ   7      // 蜂鸣器

#define PIN_LED    8      // 指示灯

 

const int   SMOKE_TH = 400;    // 烟雾阈值,先测无烟底数再定

const float TEMP_TH  = 45.0;   // 高温阈值(℃)

const float HUMI_TH  = 85.0;   // 漏水阈值(%)

bool armed = true;             // 总开关:离家时开,回家可关

 

unsigned long lastPub = 0;

 

void loop() {

  if (!client.connected()) connectMqtt();

  client.loop();

 

  int   door  = digitalRead(PIN_HALL);

  int   smoke = analogRead(PIN_MQ2);

  int   flame = digitalRead(PIN_FLAME);

  float t     = dht.readTemperature();

  float h     = dht.readHumidity();

 

  // 五个条件任一成立即报警;armed 为假时只上报、不响

  bool alarm = (door == HIGH) || (smoke > SMOKE_TH) || (flame == LOW)

               || (t > TEMP_TH) || (h > HUMI_TH);

  bool ring  = alarm && armed;

 

  digitalWrite(PIN_BUZZ, ring ? HIGH : LOW);

  digitalWrite(PIN_LED,  ring ? HIGH : LOW);

 

  if (millis() - lastPub > 2000) {          // 每 2 秒上报一次

    lastPub = millis();

    char buf[200];

    snprintf(buf, sizeof(buf),

      "{\"door\":%d,\"smoke\":%d,\"flame\":%d,\"temp\":%.1f,\"humi\":%.0f,\"alarm\":%d}",

      door, smoke, flame, t, h, alarm ? 1 : 0);

    client.publish(TOPIC_DATA.c_str(), buf);

  }

}

 

5.3 居家端(三):接收手机的开关指令

// 云把手机的消息转过来时,这个函数会被调用

void callback(char* topic, byte* payload, unsigned int len) {

  String msg = "";

  for (unsigned int i = 0; i < len; i++) msg += (char)payload[i];

 

  if (String(topic) == TOPIC_CMD) {

    // 只认 alarm 这一个字段,其余内容一律忽略

    if (msg.indexOf("\"alarm\":1") >= 0)      armed = true;

    else if (msg.indexOf("\"alarm\":0") >= 0) armed = false;

  }

}

 

void setup() {

  Serial.begin(115200);

  pinMode(PIN_HALL, INPUT_PULLUP);

  pinMode(PIN_FLAME, INPUT);

  pinMode(PIN_BUZZ, OUTPUT);

  pinMode(PIN_LED, OUTPUT);

  dht.begin();

 

  connectWifi();

  client.setServer(HOST, PORT);

  client.setCallback(callback);

  connectMqtt();

}

 

5.4 随身端:一个按键,一条呼救

#define PIN_BTN   3      // SOS 按钮,内部上拉,按下为 LOW

#define PIN_BUZZ  7

 

unsigned long lastPress = 0;

 

void setup() {

  Serial.begin(115200);

  pinMode(PIN_BTN, INPUT_PULLUP);   // 不按为高电平,按下为低电平

  pinMode(PIN_BUZZ, OUTPUT);

  connectWifi();

  client.setServer(HOST, PORT);

  connectMqtt();

}

 

void loop() {

  if (!client.connected()) connectMqtt();

  client.loop();

 

  int btn = digitalRead(PIN_BTN);

 

  // 去抖:两次按键之间至少隔 3 秒,避免按住不放刷屏

  if (btn == LOW && millis() - lastPress > 3000) {

    lastPress = millis();

 

    digitalWrite(PIN_BUZZ, HIGH);   // 短鸣一声,告诉使用者"已经发出去了"

    delay(120);

    digitalWrite(PIN_BUZZ, LOW);

 

    client.publish(TOPIC_SOS.c_str(), "{\"sos\":1,\"ts\":0}");

  }

  delay(100);                        // 100 ms 轮询一次

}

 

5.5 App 端:三块积木

App 端全部用 App Inventor 2019 桌面版拖积木完成,不涉及文本代码。三块积木的逻辑如下:

积木

触发时机

做的事

连接

点“连接”按钮

设置 MQTT 组件的服务器地址、端口、ClientID、用户名、密码,调用 Connect;连上后订阅 sas_sensorData 与 sas_SOS

接收

MqttClient.MessageReceived

把收到的字符串按分号或字段名拆开,逐个判断字段值是否为 1;为 1 就把对应图标换成红色图片、播放提示音、调用振动器

发送

点总开关

向 sas_setAlarm 发布 {"alarm":1} 或 {"alarm":0}

 

这三块积木和单片机端的三段代码是一一对应的:连接对连接、接收对回调、发送对发布。两端结构一致,调试时可以在任何一端先确认“消息有没有发出去/收到”,而不用整条链路一起猜。

六、优化记录与失败尝试

下表是这个项目从初版到现在的修改记录,包括还没改完的。写这一章的原因很直接:做的时候犯的错,比做对的部分更有信息量。

问题

当时怎么想的

现在怎么处理

门窗用的是霍尔传感器,报告里却写成“门窗震动报警”

凭印象写的,没对照元件

【已改】统一为“门窗开合检测”——霍尔测的是磁铁分离,测不到震动

同一个主题出现两个名字:正文写 sas_setAlarm,截图里是 sas_stopAlarm

改名后没有回头改截图

【已改】全文统一为 sas_setAlarm,截图重做

图号重复:图 2-4 与图 2-5 各出现两次,顺序也颠倒

边写边插图,没做统一编号

【已改】全部重排为图 1—图 14

3.7.4 标题误写“随着卫士测试”

拼音输入错误,没校对

【已改】改为“随身卫士测试”

摘要说可以“给 12110 发短信报警”,结论又说短信没调通

摘要写的是设想,结论写的是实际

【已改】摘要改为“计划支持”,结论保留“未完成”,两处口径一致

结论列了“已实现高温报警、漏气报警”,测试章却写这两项没测

把“代码写好了”当成“功能实现了”

【已改】统一为“已实现、未实测”

阿里云三元组和 Wi-Fi 密码直接印在截图与代码里

当时不知道这是敏感信息

【已改】材料中全部改为占位符;原密钥建议到控制台重置

报警只在现场响,断网后手机收不到任何提示

默认认为网络总是通的

【已改】把本地声光报警放在上报之前,断网也能响

按钮按住不放会连续发送几十条呼救

没考虑机械抖动和长按

【已改】加 3 秒去抖,两次之间只算一次

阈值写死在代码里,改一个数就要重新烧录

图省事

【待改】下一步把阈值做成云端下发,用 sas_setAlarm 同一条链路配置

漏水用湿度上升来判断,空气潮湿也会误报

模型房间没有上下水,只能这么凑

【待改】改用地面积水触点,靠水的导电性导通

 

还有两件事到现在也没解决,写在这里以免被含糊过去:

一是短信报警。原计划让云平台直接调用短信服务,把告警发到 12110 或事先设置的联系人。实际配置时卡在两点:短信签名和模板需要审核,个人开发者不容易通过;报警短信的内容属于特殊类别,平台有额外限制。所以目前只做到 App 弹窗,短信这一路是空的。

二是位置信息。随身装置没有 GPS 模块,原本打算用手机端上报的位置来代替,但这样又依赖手机在不在身边——而按下随身按钮的前提,往往正是拿不到手机。这个矛盾目前没有好的解法,可能的方案是加一块 NB-IoT 模组直接基站定位,成本和功耗都会上一个台阶。

 

硬件清单

创作许可协议

本项目采用 CC BY(署名) 进行许可。

评论(0)
- 没有更多了 -