• 正文
  • 相关推荐
申请入驻 产业图谱

基于树莓派PICO2设计的汽车少儿安全预警系统

1小时前
123
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

一、前言

1.1 项目开发背景

随着家庭汽车保有量持续增长,汽车已经成为日常生活中重要的出行工具。在家庭用车场景中,儿童乘车安全问题逐渐受到社会广泛关注。尤其是在高温天气条件下,由于家长短时间离车、遗忘儿童仍停留在车内等情况时有发生,封闭车厢内部温度会快速升高,同时空气流通不足导致二氧化碳浓度持续增加,容易造成儿童出现缺氧、中暑甚至更加严重的安全事故。儿童由于年龄较小、自救能力有限,在危险环境下难以及时发出有效求助,因此如何利用智能化技术实现车内环境监测与主动预警,已经成为车载安全领域的重要研究方向。

传统的儿童遗留提醒方式大多依赖人工检查、简单声音提示或单一传感器检测,存在误报率高、无法远程通知、监测维度不足等问题。当车辆处于熄火状态后,常规车载系统往往停止工作,无法持续感知车内状态,导致安全风险进一步增加。同时,普通报警装置通常只能进行本地提示,无法第一时间将危险信息传递给车主或监护人员,影响事故干预效率。因此,需要设计一种具备环境感知、人体识别、远程通信以及主动告警能力的智能化系统,实现对儿童滞留风险的实时预防。

近年来,物联网技术、低功耗嵌入式系统无线通信技术以及云平台技术快速发展,为构建智能车内安全监测系统提供了可靠的技术基础。通过部署温湿度传感器、二氧化碳检测传感器以及人体存在检测模块,可以实时采集车内环境参数,并结合多传感器融合策略判断是否存在危险情况。与此同时,通过4G通信模块接入云平台,可实现数据远程上传、消息推送以及异常事件通知,使用户能够突破空间限制及时掌握车辆内部状态。

树莓派PICO2(RP2350)作为新一代高性能微控制器平台,具备运算能力强、功耗低、接口资源丰富、开发成本较低等特点,适合用于便携式物联网终端设备开发。结合Arduino开发环境能够有效降低系统开发难度,提高软件迭代效率。配合Air780E 4G通信模块及MQTT协议接入华为云物联网平台,可以形成“设备端—云平台—移动终端”的完整数据链路,实现数据采集、远程监测、报警推送以及历史数据管理等功能。

基于上述背景,本项目提出一种基于树莓派PICO2设计的汽车少儿安全预警系统,通过融合温湿度检测、二氧化碳浓度检测、毫米波人体感知、蜂鸣报警、短信通知、APP弹窗提醒及云端监控等技术手段,对车内儿童滞留风险进行全天候监测与智能预警。系统不仅能够在危险发生前及时提醒车主采取措施,还能够通过远程监控增强家庭成员之间的信息协同能力,提高车辆使用过程中的安全保障水平,具有较强的实际应用价值和推广意义。

1.2 设计实现的功能

(1)车内温湿度实时监测功能
系统采用SHT30温湿度传感器对汽车内部环境进行实时采集,持续检测车内温度与湿度变化情况,并将采集结果显示在本地OLED显示屏,同时上传至云平台供远程查看。通过环境数据监测,为车内安全状态判断提供基础依据。

(2)车内二氧化碳浓度检测功能
系统采用SGP30传感器对车内空气中的二氧化碳浓度进行实时检测,持续监测空气质量变化情况。当车辆长时间处于密闭环境时,可及时获取二氧化碳浓度数据,为危险状态判断提供参考依据。

(3)儿童人体存在检测功能
系统采用LD2402毫米波人体检测模块,对车内是否存在儿童进行检测。通过人体存在感知能力,实现对车内遗留儿童场景的识别,为后续报警逻辑提供触发条件。

(4)危险环境智能联动报警功能
系统综合温度数据、二氧化碳浓度数据以及人体检测结果进行联合判断。当车内温度超过设定高温阈值或者二氧化碳浓度超过安全范围,并且检测到车内存在儿童时,系统自动进入报警状态,及时提示危险情况。

(5)本地声光报警提醒功能
报警触发后,系统驱动有源蜂鸣器发出周期性报警声音,模拟汽车喇叭进行持续提醒,提示周围人员及时关注车内情况。同时点亮报警状态指示灯,用于直观显示设备当前报警状态。

(6)短信远程通知功能
系统通过Air780E 4G通信模块向预设联系人发送短信提醒,通知车主当前车辆内部存在儿童且环境存在危险,提升异常情况下的信息传递效率。

(7)手机APP弹窗预警功能
设备将报警事件上传至华为云物联网平台,用户通过设计的Android手机APP接收实时报警通知,并以消息弹窗形式提醒用户及时处理异常情况。

(8)温度报警阈值设置功能
系统支持用户根据不同环境需求设置高温报警阈值,允许调整触发报警的温度条件,提高系统使用过程中的灵活性与适应能力。

(9)本地报警解除功能
系统设置独立报警清除按键,用户可在确认现场安全后手动关闭报警状态,停止蜂鸣器报警并恢复系统正常监测运行。

(10)自动恢复监测功能
当车内环境恢复正常,即温度未超限、二氧化碳浓度恢复正常或未检测到儿童存在时,系统自动退出报警状态并关闭报警输出,无需人工干预。

(11)本地数据显示功能
系统采用0.96寸OLED显示屏作为本地人机交互界面,用于实时显示温湿度、二氧化碳浓度、人体检测状态、网络状态以及系统运行状态等信息,便于用户现场查看设备运行情况。

(12)数据上云与远程监控功能
系统通过Air780E模块接入华为云物联网平台,利用MQTT协议将本地采集的全部环境数据实时上传,实现远程数据存储、状态监测以及异常事件同步。

(13)多平台上位机监控功能
系统设计支持Android手机端和Windows电脑端两种上位机APP,用户可通过网络远程查看设备上传的环境数据、报警状态以及设备运行情况,实现跨平台远程管理。

(14)一键SOS主动求救功能
系统设置独立SOS按键,用户按下按键后可主动触发紧急求助流程,通过4G模块发送求助短信,同时向手机APP推送报警通知,提高紧急情况下的信息响应能力。

(15)设备状态指示功能
系统配置三颗LED状态指示灯,分别用于显示设备运行状态、4G联网状态以及报警状态,便于用户快速判断设备当前工作情况,提高系统可维护性和使用体验。

1.3 项目硬件模块组成

(1)主控控制模块(树莓派PICO2)
系统核心控制单元采用树莓派PICO2开发板,核心芯片为RP2350微控制器。主控模块负责完成各类传感器数据采集、报警逻辑判断、设备状态控制、显示刷新、按键处理以及通信数据管理等任务。系统通过主控协调各功能模块协同运行,实现环境监测、报警处理及远程通信功能。

(2)温湿度检测模块(SHT30)
温湿度检测部分采用SHT30数字温湿度传感器,通过IIC总线与主控连接,用于实时采集车内温度和湿度信息。采集的数据用于环境状态监测,并参与高温报警条件判断,同时支持数据显示和云端上传。

(3)二氧化碳检测模块(SGP30)
空气质量检测部分采用SGP30传感器,用于检测车内二氧化碳浓度变化情况。模块通过数字接口输出检测结果,为系统判断车内空气安全状态提供依据,并将检测数据同步显示和上传至云平台。

(4)人体存在检测模块(LD2402毫米波雷达
人体检测模块采用海凌科LD2402毫米波雷达模块,用于检测车内是否存在儿童。该模块利用毫米波感知技术实现人体存在识别,将检测结果反馈至主控,用于参与危险状态联动判断。

(5)本地显示模块(0.96寸OLED显示屏)
显示部分采用0.96寸OLED显示屏,通过IIC通信方式与主控连接,用于实时显示温度、湿度、二氧化碳浓度、人体检测状态、联网状态及报警状态等运行信息,提升设备的人机交互能力。

(6)4G无线通信模块(Air780E)
通信模块采用Air780E 4G通信模块,通过串口方式与树莓派PICO2进行数据交互。模块负责完成设备联网、短信发送、MQTT数据通信以及与华为云物联网平台的数据交互,实现远程监控与消息通知功能。

(7)蜂鸣报警模块(有源蜂鸣器)
报警输出部分采用高电平触发型有源蜂鸣器,由主控直接控制。当系统判断达到报警条件时,蜂鸣器按照设定方式发出连续报警声音,用于提醒车主及周围人员及时关注车内异常情况。

(8)按键输入模块(独立机械按键)
系统配置三颗独立机械按键,分别用于页面切换、SOS主动求救以及报警解除控制。用户可通过按键完成设备本地交互操作,提高系统使用便利性。

(9)LED状态指示模块
系统配置三颗LED状态指示灯,用于显示设备运行状态、4G联网状态以及报警状态。通过不同指示状态反馈系统当前工作情况,方便用户快速了解设备运行状态。

(10)供电管理模块(18650锂电池供电)
系统采用18650锂电池作为整机供电电源,为主控、电源转换电路、传感器模块、显示模块及通信模块提供稳定电能支持。供电模块满足设备便携式安装与车载使用需求,保证系统持续运行。

(11)云平台与上位机交互模块
系统通过4G通信网络连接华为云物联网平台,实现设备数据接入与远程管理。用户可通过基于Qt5开发的Android手机APP和Windows电脑端上位机查看设备上传的数据、接收报警通知并进行远程监控,实现完整的数据闭环管理。

1.4 系统框架图

1.5 运行流程图

二、部署华为云物联网平台

华为云官网: https://www.huaweicloud.com/

打开官网,搜索物联网,就能快速找到 设备接入IoTDA

2.1 物联网平台介绍

华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。

使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。

物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。

设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS协议将业务数据上报到平台,平台也可以将控制命令下发给设备。

业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。

2.2 开通物联网服务

地址: https://www.huaweicloud.com/product/iothub.html

开通免费单元。

点击立即创建

正在创建标准版实例,需要等待片刻。

创建完成之后,点击详情。 可以看到标准版实例的设备接入端口和地址。

下面框起来的就是端口号域名

点击实例名称,可以查看当前免费单元的配置情况。

开通之后,点击接入信息,也能查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。

总结:

端口号:   MQTT (1883)| MQTTS (8883)    
接入地址: dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com

根据域名地址得到IP地址信息:

打开Windows电脑的命令行控制台终端,使用ping 命令。ping一下即可。

Microsoft Windows [版本 10.0.19045.5011]
(c) Microsoft Corporation。保留所有权利。

C:UsersLenovo>ping dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com

正在 Ping dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com [117.78.5.125] 具有 32 字节的数据:
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44

117.78.5.125 的 Ping 统计信息:
    数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
    最短 = 37ms,最长 = 37ms,平均 = 37ms

C:UsersLenovo>

MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口合适

2.3 创建产品

链接:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-dev/all-product?instanceId=03c5c68c-e588-458c-90c3-9e4c640be7af

(1)创建产品

(2)填写产品信息

根据自己产品名字填写,下面的设备类型选择自定义类型。

(3)产品创建成功

创建完成之后点击查看详情。

(4)添加自定义模型

产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。

模型简单来说: 就是存放设备上传到云平台的数据。

你可以根据自己的产品进行创建。

比如:

烟雾可以叫  MQ2
温度可以叫  Temperature
湿度可以叫  humidity
火焰可以叫  flame
其他的传感器自己用单词简写命名即可。 这就是你的单片机设备端上传到服务器的数据名字。

先点击自定义模型。

再创建一个服务ID。

接着点击新增属性。

2.4 添加设备

产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。

(1)注册设备

(2)根据自己的设备填写

(3)保存设备信息

创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成MQTT三元组的时候需要使用。

(4)设备创建完成

(5)设备详情

2.5 MQTT协议主题订阅与发布

(1)MQTT协议介绍

当前的设备是采用MQTT协议与华为云平台进行通信。

MQTT是一个物联网传输协议,它被设计用于轻量级的发布/订阅式消息传输,旨在为低带宽和不稳定的网络环境中的物联网设备提供可靠的网络服务。MQTT是专门针对物联网开发的轻量级传输协议。MQTT协议针对低带宽网络,低计算能力的设备,做了特殊的优化,使得其能适应各种物联网应用场景。目前MQTT拥有各种平台和设备上的客户端,已经形成了初步的生态系统。

MQTT是一种消息队列协议,使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合,相对于其他协议,开发更简单;MQTT协议是工作在TCP/IP协议上;由TCP/IP协议提供稳定的网络连接;所以,只要具备TCP协议栈的网络设备都可以使用MQTT协议。  本次设备采用的ESP8266就具备TCP协议栈,能够建立TCP连接,所以,配合STM32代码里封装的MQTT协议,就可以与华为云平台完成通信。

华为云的MQTT协议接入帮助文档在这里:  https://support.huaweicloud.com/devg-iothub/iot_02_2200.html

业务流程:

(2)华为云平台MQTT协议使用限制

描述 限制
支持的MQTT协议版本 3.1.1
与标准MQTT协议的区别 支持Qos 0和Qos 1支持Topic自定义不支持QoS2不支持will、retain msg
MQTTS支持的安全等级 采用TCP通道基础 + TLS协议(最高TLSv1.3版本)
单帐号每秒最大MQTT连接请求数 无限制
单个设备每分钟支持的最大MQTT连接数 1
单个MQTT连接每秒的吞吐量,即带宽,包含直连设备和网关 3KB/s
MQTT单个发布消息最大长度,超过此大小的发布请求将被直接拒绝 1MB
MQTT连接心跳时间建议值 心跳时间限定为30至1200秒,推荐设置为120秒
产品是否支持自定义Topic 支持
消息发布与订阅 设备只能对自己的Topic进行消息发布与订阅
每个订阅请求的最大订阅数 无限制

(3)主题订阅格式

帮助文档地址:https://support.huaweicloud.com/devg-iothub/iot_02_2200.html

对于设备而言,一般会订阅平台下发消息给设备 这个主题。

设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。

如果设备想要知道平台下发的消息,需要订阅上面图片里标注的主题。

以当前设备为例,最终订阅主题的格式如下:
$oc/devices/{device_id}/sys/messages/down
    
最终的格式:
$oc/devices/663cb18871d845632a0912e7_dev1/sys/messages/down

(4)主题发布格式

对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。

这个操作称为:属性上报。

帮助文档地址:https://support.huaweicloud.com/usermanual-iothub/iot_06_v5_3010.html

根据帮助文档的介绍, 当前设备发布主题,上报属性的格式总结如下:

发布的主题格式:
$oc/devices/{device_id}/sys/properties/report
 
最终的格式:
$oc/devices/663cb18871d845632a0912e7_dev1/sys/properties/report
发布主题时,需要上传数据,这个数据格式是JSON格式。

上传的JSON数据格式如下:

{
  "services": [
    {
      "service_id": <填服务ID>,
      "properties": {
        "<填属性名称1>": <填属性值>,
        "<填属性名称2>": <填属性值>,
        ..........
      }
    }
  ]
}
根据JSON格式,一次可以上传多个属性字段。 这个JSON格式里的,服务ID,属性字段名称,属性值类型,在前面创建产品的时候就已经介绍了,不记得可以翻到前面去查看。

根据这个格式,组合一次上传的属性数据:
{"services": [{"service_id": "stm32","properties":{"你的字段名字1":30,"你的字段名字2":10,"你的字段名字3":1,"你的字段名字4":0}}]}

2.6 MQTT三元组

MQTT协议登录需要填用户ID,设备ID,设备密码等信息,就像我们平时登录QQ,微信一样要输入账号密码才能登录。MQTT协议登录的这3个参数,一般称为MQTT三元组。

接下来介绍,华为云平台的MQTT三元组参数如何得到。

(1)MQTT服务器地址

要登录MQTT服务器,首先记得先知道服务器的地址是多少,端口是多少。

帮助文档地址:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/home

MQTT协议的端口支持1883和8883,它们的区别是:8883 是加密端口更加安全。但是单片机上使用比较困难,所以当前的设备是采用1883端口进连接的。

根据上面的域名和端口号,得到下面的IP地址和端口号信息:   如果设备支持填写域名可以直接填域名,不支持就直接填写IP地址。  (IP地址就是域名解析得到的)

华为云的MQTT服务器地址:117.78.5.125
华为云的MQTT端口号:1883

如何得到IP地址?如何域名转IP?  打开Windows的命令行输入以下命令。

ping  ad635970a1.st1.iotda-device.cn-north-4.myhuaweicloud.com

(2)生成MQTT三元组

华为云提供了一个在线工具,用来生成MQTT鉴权三元组: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/

打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。

下面是打开的页面:

填入设备的信息:   (上面两行就是设备创建完成之后保存得到的)

直接得到三元组信息。

得到三元组之后,设备端通过MQTT协议登录鉴权的时候,填入参数即可。

ClientId  663cb18871d845632a0912e7_dev1_0_0_2024050911
Username  663cb18871d845632a0912e7_dev1
Password  71b82deae83e80f04c4269b5bbce3b2fc7c13f610948fe210ce18650909ac237

2.7 模拟设备登录测试

经过上面的步骤介绍,已经创建了产品,设备,数据模型,得到MQTT登录信息。 接下来就用MQTT客户端软件模拟真实的设备来登录平台。测试与服务器通信是否正常。

MQTT软件下载地址【免费】: https://download.csdn.net/download/xiaolong1126626497/89928772

(1)填入登录信息

打开MQTT客户端软件,对号填入相关信息(就是上面的文本介绍)。然后,点击登录,订阅主题,发布主题。

(2)打开网页查看

完成上面的操作之后,打开华为云网页后台,可以看到设备已经在线了。

点击详情页面,可以看到上传的数据:

到此,云平台的部署已经完成,设备已经可以正常上传数据了。

(3)MQTT登录测试参数总结

MQTT服务器:  117.78.5.125
MQTT端口号:  183

//物联网服务器的设备信息
#define MQTT_ClientID "663cb18871d845632a0912e7_dev1_0_0_2024050911"
#define MQTT_UserName "663cb18871d845632a0912e7_dev1"
#define MQTT_PassWord "71b82deae83e80f04c4269b5bbce3b2fc7c13f610948fe210ce18650909ac237"

//订阅与发布的主题
#define SET_TOPIC  "$oc/devices/663cb18871d845632a0912e7_dev1/sys/messages/down"  //订阅
#define POST_TOPIC "$oc/devices/663cb18871d845632a0912e7_dev1/sys/properties/report"  //发布


发布的数据:
{"services": [{"service_id": "stm32","properties":{"你的字段名字1":30,"你的字段名字2":10,"你的字段名字3":1,"你的字段名字4":0}}]}

2.8 创建IAM账户

创建一个IAM账户,因为接下来开发上位机,需要使用云平台的API接口,这些接口都需要token进行鉴权。简单来说,就是身份的认证。  调用接口获取Token时,就需要填写IAM账号信息。所以,接下来演示一下过程。

地址:  https://console.huaweicloud.com/iam/?region=cn-north-4#/iam/users

**【1】获取项目凭证 **  点击左上角用户名,选择下拉菜单里的我的凭证

项目凭证:

28add376c01e4a61ac8b621c714bf459

【2】创建IAM用户

鼠标放在左上角头像上,在下拉菜单里选择统一身份认证

点击左上角创建用户

创建成功:

【3】创建完成

用户信息如下:

主用户名  l19504562721
IAM用户  ds_abc
密码     DS12345678

2.9 获取影子数据

帮助文档:https://support.huaweicloud.com/api-iothub/iot_06_v5_0079.html

设备影子介绍:

设备影子是一个用于存储和检索设备当前状态信息的JSON文档。
每个设备有且只有一个设备影子,由设备ID唯一标识
设备影子仅保存最近一次设备的上报数据和预期数据
无论该设备是否在线,都可以通过该影子获取和设置设备的属性

简单来说:设备影子就是保存,设备最新上传的一次数据。

我们设计的软件里,如果想要获取设备的最新状态信息,就采用设备影子接口

如果对接口不熟悉,可以先进行在线调试:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ShowDeviceShadow

在线调试接口,可以请求影子接口,了解请求,与返回的数据格式。

调试完成看右下角的响应体,就是返回的影子数据。

设备影子接口返回的数据如下:

{
 "device_id": "663cb18871d845632a0912e7_dev1",
 "shadow": [
  {
   "service_id": "stm32",
   "desired": {
    "properties": null,
    "event_time": null
   },
   "reported": {
    "properties": {
     "DHT11_T": 18,
     "DHT11_H": 90,
     "BH1750": 38,
     "MQ135": 70
    },
    "event_time": "20240509T113448Z"
   },
   "version": 3
  }
 ]
}

调试成功之后,可以得到访问影子数据的真实链接,接下来的代码开发中,就采用Qt写代码访问此链接,获取影子数据,完成上位机开发。

链接如下:

https://ad635970a1.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/28add376c01e4a61ac8b621c714bf459/devices/663cb18871d845632a0912e7_dev1/shadow

三、上位机开发

3.1 Qt开发环境安装

Qt的中文官网: https://www.qt.io/zh-cn/![image-20221207160550486](https://led-obs.obs.cn-north-1.myhuaweicloud.com/Blog/img/image-20221207160550486.png)

QT5.12.6的下载地址:https://download.qt.io/archive/qt/5.12/5.12.6

打开下载链接后选择下面的版本进行下载:

如果下载不了,可以在网盘里找到安装包下载:    飞书文档记录的网盘地址:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink

软件安装时断网安装,否则会提示输入账户。

安装的时候,第一个复选框里的编译器可以全选,直接点击下一步继续安装。

选择编译器: (一定要看清楚了)

3.2 新建上位机工程

前面2讲解了需要用的API接口,接下来就使用Qt设计上位机,设计界面,完成整体上位机的逻辑设计。

【1】新建工程

【2】设置项目的名称。

【3】选择编译系统

【4】选择默认继承的类

【5】选择编译器

【6】点击完成

【7】工程创建完成

3.3 切换编译器

在左下角是可以切换编译器的。 可以选择用什么样的编译器编译程序。

目前新建工程的时候选择了2种编译器。 一种是mingw32这个编译Windows下运行的程序。 一种是Android编译器,可以生成Android手机APP。

不过要注意:Android的编译器需要配置一些环境才可以正常使用,这个大家可以网上找找教程配置一下就行了。

windows的编译器就没有这么麻烦,安装好Qt就可以编译使用。

下面我这里就选择的 mingw32这个编译器,编译Windows下运行的程序。

3.4 编译测试功能

创建完毕之后,编译测试一下功能是否OK。

点击左下角的绿色三角形按钮

正常运行就可以看到弹出一个白色的框框。这就表示工程环境没有问题了。 接下来就可以放心的设计界面了。

3.5 设计UI界面与工程配置

【1】打开UI文件

打开默认的界面如下:

【2】开始设计界面

根据自己需求设计界面。

3.6 设计代码

// widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QDateTime>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
    , m_mqttClient(nullptr)
    , m_timer(new QTimer(this))
{
    ui->setupUi(this);

    // 初始化MQTT客户端
    m_mqttClient = new QMqttClient(this);
    m_mqttClient->setHostname("YOUR_HUAWEI_CLOUD_MQTT_HOST");  // 替换为华为云MQTT接入地址
    m_mqttClient->setPort(1883);
    m_mqttClient->setClientId("YOUR_CLIENT_ID");  // 替换为设备ID
    m_mqttClient->setUsername("YOUR_USERNAME");    // 替换为用户名
    m_mqttClient->setPassword("YOUR_PASSWORD");    // 替换为密码

    connect(m_mqttClient, &QMqttClient::stateChanged, this, &Widget::onMqttStateChanged);
    connect(m_mqttClient, &QMqttClient::messageReceived, this, &Widget::onMqttMessageReceived);

    // 连接MQTT
    m_mqttClient->connectToHost();

    // 初始化定时器,每秒从云平台拉取数据(实际可通过订阅topic实现实时推送)
    connect(m_timer, &QTimer::timeout, this, &Widget::updateData);
    m_timer->start(2000);  // 2秒刷新一次

    // 初始化窗口
    setWindowTitle("汽车少儿安全预警系统 - 监控终端");
    resize(800, 600);

    // 初始化UI状态
    ui->label_status->setText("断开连接");
    ui->label_status->setStyleSheet("color: red;");
    ui->label_alarmStatus->setText("安全");
    ui->label_alarmStatus->setStyleSheet("color: green;");
    ui->label_temperature->setText("-- ℃");
    ui->label_humidity->setText("-- %");
    ui->label_co2->setText("-- ppm");
    ui->label_childDetected->setText("未检测到");
    ui->label_childDetected->setStyleSheet("color: green;");

    // 初始化温度阈值
    ui->spinBox_tempThreshold->setValue(40);
    ui->spinBox_co2Threshold->setValue(1000);

    // 连接信号槽 - 上位机发送控制指令(通过MQTT下发)
    connect(ui->pushButton_setThreshold, &QPushButton::clicked, this, &Widget::onSetThreshold);
    connect(ui->pushButton_clearAlarm, &QPushButton::clicked, this, &Widget::onClearAlarm);
    connect(ui->pushButton_sos, &QPushButton::clicked, this, &Widget::onSosTrigger);

    // 模拟报警日志区域
    ui->textEdit_log->setReadOnly(true);
}

Widget::~Widget()
{
    if (m_mqttClient) {
        m_mqttClient->disconnectFromHost();
        delete m_mqttClient;
    }
    delete ui;
}

// MQTT连接状态变化
void Widget::onMqttStateChanged(QMqttClient::ClientState state)
{
    switch (state) {
    case QMqttClient::Connected:
        ui->label_status->setText("已连接");
        ui->label_status->setStyleSheet("color: green;");
        // 订阅设备上报数据的topic
        m_mqttClient->subscribe("device/+/data");  // 根据实际topic修改
        // 订阅报警事件topic
        m_mqttClient->subscribe("device/+/alarm");
        // 订阅SOS事件topic
        m_mqttClient->subscribe("device/+/sos");
        addLog("MQTT连接成功,已订阅主题");
        break;
    case QMqttClient::Disconnected:
        ui->label_status->setText("断开连接");
        ui->label_status->setStyleSheet("color: red;");
        addLog("MQTT连接断开");
        break;
    default:
        break;
    }
}

// 收到MQTT消息
void Widget::onMqttMessageReceived(const QByteArray &message, const QMqttTopicName &topic)
{
    QJsonDocument doc = QJsonDocument::fromJson(message);
    if (doc.isNull()) {
        addLog("收到无效JSON数据");
        return;
    }

    QJsonObject obj = doc.object();
    QString topicStr = topic.name();

    if (topicStr.contains("/data")) {
        // 设备上报的数据
        float temp = obj.value("temperature").toDouble();
        float humidity = obj.value("humidity").toDouble();
        int co2 = obj.value("co2").toInt();
        bool childDetected = obj.value("childDetected").toBool();

        // 更新UI
        ui->label_temperature->setText(QString::number(temp, 'f', 1) + " ℃");
        ui->label_humidity->setText(QString::number(humidity, 'f', 1) + " %");
        ui->label_co2->setText(QString::number(co2) + " ppm");
        ui->label_childDetected->setText(childDetected ? "检测到儿童" : "未检测到");
        ui->label_childDetected->setStyleSheet(childDetected ? "color: red;" : "color: green;");

        // 保存最新数据
        m_lastData = obj;

    } else if (topicStr.contains("/alarm")) {
        // 报警事件
        bool alarmActive = obj.value("alarmActive").toBool();
        if (alarmActive) {
            ui->label_alarmStatus->setText("报警中!");
            ui->label_alarmStatus->setStyleSheet("color: red;");
            addLog("⚠️ 报警触发!温度/CO2超标且检测到儿童");
            // 弹出报警提示框
            QMessageBox::warning(this, "报警", "⚠️ 车内检测到儿童且温度/CO2超标!请立即处理!");
        } else {
            ui->label_alarmStatus->setText("安全");
            ui->label_alarmStatus->setStyleSheet("color: green;");
            addLog("✅ 报警已解除");
        }

    } else if (topicStr.contains("/sos")) {
        // SOS事件
        bool sosTriggered = obj.value("sosTriggered").toBool();
        if (sosTriggered) {
            addLog("🆘 SOS紧急求救!已通知指定联系人");
            QMessageBox::critical(this, "SOS求救", "🆘 设备触发了SOS一键求救!请立即联系车主!");
        }
    }
}

// 定时更新数据(实际上数据由MQTT推送,此函数仅作为备用)
void Widget::updateData()
{
    // 数据由MQTT推送更新,此函数留空或用于心跳检测
}

// 设置温度阈值(通过MQTT下发指令)
void Widget::onSetThreshold()
{
    int tempThreshold = ui->spinBox_tempThreshold->value();
    int co2Threshold = ui->spinBox_co2Threshold->value();

    // 构建设置指令JSON
    QJsonObject cmd;
    cmd["command"] = "setThreshold";
    cmd["temperatureThreshold"] = tempThreshold;
    cmd["co2Threshold"] = co2Threshold;
    cmd["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);

    QJsonDocument doc(cmd);
    QByteArray payload = doc.toJson();

    // 发布到设备控制topic
    m_mqttClient->publish("device/control", payload);

    addLog(QString("已发送阈值设置: 温度=%1℃, CO2=%2ppm").arg(tempThreshold).arg(co2Threshold));
    QMessageBox::information(this, "设置成功", QString("温度阈值已设为 %1℃nCO2阈值已设为 %2ppm").arg(tempThreshold).arg(co2Threshold));
}

// 清除报警(通过MQTT下发指令)
void Widget::onClearAlarm()
{
    QJsonObject cmd;
    cmd["command"] = "clearAlarm";
    cmd["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);

    QJsonDocument doc(cmd);
    QByteArray payload = doc.toJson();

    m_mqttClient->publish("device/control", payload);

    addLog("已发送清除报警指令");
    QMessageBox::information(this, "指令已发送", "已发送清除报警指令到设备");
}

// SOS触发(通过MQTT下发指令)
void Widget::onSosTrigger()
{
    // 弹出确认对话框
    int ret = QMessageBox::question(this, "SOS确认", 
                                    "确定要触发SOS一键求救吗?n将通知指定联系人!",
                                    QMessageBox::Yes | QMessageBox::No);
    if (ret == QMessageBox::No) return;

    QJsonObject cmd;
    cmd["command"] = "sos";
    cmd["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);

    QJsonDocument doc(cmd);
    QByteArray payload = doc.toJson();

    m_mqttClient->publish("device/control", payload);

    addLog("🆘 已发送SOS求救指令");
    QMessageBox::warning(this, "SOS已触发", "SOS求救信号已发送!n指定联系人将收到短信通知。");
}

// 添加日志
void Widget::addLog(const QString &msg)
{
    QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textEdit_log->append(QString("[%1] %2").arg(timestamp).arg(msg));
}

// 重写关闭事件
void Widget::closeEvent(QCloseEvent *event)
{
    if (m_mqttClient) {
        m_mqttClient->disconnectFromHost();
    }
    event->accept();
}

对应的头文件 widget.h

// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QMqttClient>
#include <QTimer>
#include <QJsonObject>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void onMqttStateChanged(QMqttClient::ClientState state);
    void onMqttMessageReceived(const QByteArray &message, const QMqttTopicName &topic);
    void updateData();
    void onSetThreshold();
    void onClearAlarm();
    void onSosTrigger();

private:
    void addLog(const QString &msg);
    void closeEvent(QCloseEvent *event) override;

    Ui::Widget *ui;
    QMqttClient *m_mqttClient;
    QTimer *m_timer;
    QJsonObject m_lastData;
};

#endif // WIDGET_H

四、树莓派pico2代码设计

4.1 硬件连线说明

外设 接口类型 引脚 说明
OLED显示屏 I2C SDA: GP0, SCL: GP1 0.96寸IIC协议
SHT30温湿度传感器 I2C SDA: GP2, SCL: GP3 温湿度检测
SGP30二氧化碳传感器 I2C SDA: GP4, SCL: GP5 CO2含量检测
LD2402毫米波雷达 UART TX: GP8, RX: GP9 人体检测
Air780E 4G模块 UART TX: GP12, RX: GP13 4G通信/MQTT
有源蜂鸣器 GPIO GP14 高电平触发报警
LED指示灯-RUN GPIO GP15 运行状态指示
LED指示灯-NET GPIO GP16 4G联网状态指示
LED指示灯-ALM GPIO GP17 报警状态指示
按键-翻页 GPIO GP18 翻页按键
按键-SOS GPIO GP19 SOS求救按键
按键-清除报警 GPIO GP20 清除报警按键
电源 - VBUS, 3.3V, GND 18650锂电池供电

4.2 详细硬件连接图

1. 主控芯片(树莓派PICO2)

树莓派PICO2 (RP2350)
┌─────────────────────────────────────┐
│                                      │
│  GP0 ──────► OLED SDA               │
│  GP1 ──────► OLED SCL               │
│                                      │
│  GP2 ──────► SHT30 SDA              │
│  GP3 ──────► SHT30 SCL              │
│                                      │
│  GP4 ──────► SGP30 SDA              │
│  GP5 ──────► SGP30 SCL              │
│                                      │
│  GP8 ──────► LD2402 TX              │
│  GP9 ──────► LD2402 RX              │
│                                      │
│  GP12 ─────► Air780E TX             │
│  GP13 ─────► Air780E RX             │
│                                      │
│  GP14 ─────► 蜂鸣器 IN              │
│                                      │
│  GP15 ─────► LED-RUN                │
│  GP16 ─────► LED-NET                │
│  GP17 ─────► LED-ALM                │
│                                      │
│  GP18 ─────► 按键-翻页              │
│  GP19 ─────► 按键-SOS               │
│  GP20 ─────► 按键-清除报警          │
│                                      │
│  VBUS ──────► 5V电源输入            │
│  3.3V ──────► 3.3V输出              │
│  GND ───────► 地                     │
└─────────────────────────────────────┘

2. OLED显示屏 (0.96寸 I2C)

OLED显示屏 (SSD1306)
┌─────────────┐
│  VCC ───────► 3.3V (PICO2 3.3V)    │
│  GND ───────► GND                    │
│  SDA ───────► GP0                    │
│  SCL ───────► GP1                    │
└─────────────┘

注意事项:

    • OLED的I2C地址通常为0x3C或0x3D• 需要上拉电阻(PICO2内部已上拉,无需外接)

3. SHT30温湿度传感器

SHT30传感器
┌─────────────┐
│  VIN ───────► 3.3V                   │
│  GND ───────► GND                    │
│  SDA ───────► GP2                    │
│  SCL ───────► GP3                    │
└─────────────┘

注意事项:

    • I2C地址:0x44 (默认)• 通信速率:100kHz标准模式

4. SGP30二氧化碳传感器

SGP30传感器
┌─────────────┐
│  VCC ───────► 3.3V                   │
│  GND ───────► GND                    │
│  SDA ───────► GP4                    │
│  SCL ───────► GP5                    │
└─────────────┘

注意事项:

    • I2C地址:0x58• 需要初始化等待15秒以上才能稳定读数

5. LD2402毫米波雷达(人体检测)

LD2402雷达模块
┌─────────────┐
│  VCC ───────► 5V (或3.3V, 根据模块) │
│  GND ───────► GND                    │
│  TX ────────► GP8 (PICO2 RX)        │
│  RX ────────► GP9 (PICO2 TX)        │
└─────────────┘

注意事项:

    波特率:115200• 数据格式:8位数据位,1位停止位,无校验• 输出格式:串口ASCII数据

6. Air780E 4G模块

Air780E 4G模块
┌─────────────┐
│  VCC ───────► 5V (或锂电池直接供电)  │
│  GND ───────► GND                    │
│  TX ────────► GP12 (PICO2 RX)       │
│  RX ────────► GP13 (PICO2 TX)       │
│  NET_LED ───► (可选, 指示网络状态)   │
│  SIM卡座 ───► 插入SIM卡              │
└─────────────┘

注意事项:

    • 波特率:115200• 供电电压:3.4V-4.2V (推荐4.0V)• 峰值电流可达2A,需要足够供电能力

7. 蜂鸣器

有源蜂鸣器
┌─────────────┐
│  VCC ───────► 3.3V                   │
│  GND ───────► GND                    │
│  I/O ───────► GP14                   │
└─────────────┘

注意事项:

    • 高电平触发,持续高电平发声• 采用三极管驱动增强驱动能力

8. LED指示灯

LED指示灯 (共阴极)
┌─────────────┐
│  LED-RUN ───► GP15 + 限流电阻 220Ω  │
│  LED-NET ───► GP16 + 限流电阻 220Ω  │
│  LED-ALM ───► GP17 + 限流电阻 220Ω  │
│  所有GND ───► GND                    │
└─────────────┘

指示灯定义:

RUN LED:系统运行指示(闪烁表示正常运行)•

NET LED:4G联网状态(常亮表示已连接)•

ALM LED:报警指示(报警时闪烁)

9. 按键模块

按键 (机械按键, 上拉输入)
┌─────────────┐
│  按键1 ─────► GP18 (翻页)            │
│  按键2 ─────► GP19 (SOS求救)         │
│  按键3 ─────► GP20 (清除报警)        │
│  公共端 ────► GND                    │
└─────────────┘

注意事项:

    • 使用内部上拉电阻• 按下时低电平触发• 需做软件去抖处理

10. 电源系统 (18650锂电池)

18650锂电池供电系统
┌─────────────────────────────────────┐
│  18650锂电池 (3.7V)                 │
│       │                              │
│       ├─────► 升压模块 (5V输出)     │
│       │         └─────► PICO2 VBUS  │
│       │         └─────► Air780E VCC │
│       │                              │
│       ├─────► LDO稳压 (3.3V输出)   │
│       │         └─────► 传感器供电   │
│       │                              │
│       └─────► 锂电池充电管理模块     │
│                 (TP4056)             │
└─────────────────────────────────────┘

电源管理

    • 18650电池:3.7V锂离子电池• 升压模块:升压至5V给PICO2和4G模块• LDO稳压:3.3V给传感器和OLED• 充电管理:TP4056充电模块

4.2 项目完整代码设计

// ============================================================
// 项目:基于树莓派PICO2的汽车少儿安全预警系统
// 主控:树莓派PICO2 (RP2350)
// 开发环境:Arduino IDE
// ============================================================

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SHT3x.h>
#include <SGP30.h>
#include <SoftwareSerial.h>
#include <Ticker.h>

#include "config.h"
#include "sensors.h"
#include "display.h"
#include "lte.h"
#include "alarm.h"

// ==================== 全局变量 ====================
SHT3x sht30;                    // SHT30温湿度传感器
SGP30 sgp30;                    // SGP30 CO2传感器
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire1, OLED_RESET);

// 串口对象
HardwareSerial radioSerial(0);  // UART0: LD2402雷达
HardwareSerial lteSerial(1);    // UART1: Air780E 4G模块

// 系统状态
struct SystemState {
    float temperature = 0.0;
    float humidity = 0.0;
    uint16_t co2 = 0;
    bool childDetected = false;
    bool alarmActive = false;
    bool lteConnected = false;
    uint32_t lastHeartbeat = 0;
    uint32_t lastSensorRead = 0;
    uint32_t lastDataUpload = 0;
    uint32_t lastAlarmCheck = 0;
    uint8_t displayPage = 0;
    bool sosTriggered = false;
} sysState;

// 报警配置
struct AlarmConfig {
    float tempThreshold = 40.0;   // 温度阈值 (℃)
    uint16_t co2Threshold = 1000;  // CO2阈值 (ppm)
    bool alarmEnabled = true;
} alarmConfig;

// 按键去抖
struct Button {
    uint8_t pin;
    bool lastState;
    uint32_t lastDebounceTime;
    bool pressed;
} buttons[3];

// ==================== 函数声明 ====================
void setupHardware();
void setupSensors();
void readSensors();
void readRadar();
void processAlarm();
void handleButtons();
void uploadData();
void sendSMS(const char* phone, const char* message);
void ledIndicator();

// ==================== 初始化 ====================
void setup() {
    // 初始化串口调试
    Serial.begin(115200);
    delay(1000);
    Serial.println("n==========================================");
    Serial.println("汽车少儿安全预警系统 V1.0");
    Serial.println("主控: 树莓派PICO2 (RP2350)");
    Serial.println("==========================================");

    // 初始化硬件
    setupHardware();
    
    // 初始化传感器
    setupSensors();
    
    // 初始化OLED
    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
        Serial.println("OLED初始化失败!");
        while (1);
    }
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);
    display.display();
    
    // 初始化4G模块
    lteSerial.begin(115200);
    delay(2000);
    initLTE(&lteSerial);
    
    // 初始化按键
    for (int i = 0; i < 3; i++) {
        buttons[i].pin = KEY_PINS[i];
        buttons[i].lastState = HIGH;
        buttons[i].lastDebounceTime = 0;
        buttons[i].pressed = false;
        pinMode(buttons[i].pin, INPUT_PULLUP);
    }

    // 显示启动信息
    displayStartup();
    delay(2000);
    
    Serial.println("系统初始化完成!");
}

// ==================== 主循环 ====================
void loop() {
    uint32_t currentTime = millis();

    // 1. 读取传感器数据 (每500ms)
    if (currentTime - sysState.lastSensorRead >= SENSOR_READ_INTERVAL) {
        sysState.lastSensorRead = currentTime;
        readSensors();
        readRadar();
        updateDisplay();
    }

    // 2. 报警检测 (每200ms)
    if (currentTime - sysState.lastAlarmCheck >= 200) {
        sysState.lastAlarmCheck = currentTime;
        processAlarm();
    }

    // 3. 处理按键
    handleButtons();

    // 4. 数据上传 (每5秒)
    if (currentTime - sysState.lastDataUpload >= DATA_UPLOAD_INTERVAL) {
        sysState.lastDataUpload = currentTime;
        uploadData();
    }

    // 5. 4G心跳 (每30秒)
    if (currentTime - sysState.lastHeartbeat >= HEARTBEAT_INTERVAL) {
        sysState.lastHeartbeat = currentTime;
        if (sysState.lteConnected) {
            lteSendHeartbeat();
        } else {
            // 尝试重连
            initLTE(&lteSerial);
        }
    }

    // 6. LED指示
    ledIndicator();

    // 7. 串口数据处理
    if (lteSerial.available()) {
        handleLTEResponse(lteSerial.read());
    }

    delay(10);
}

// ==================== 硬件初始化 ====================
void setupHardware() {
    // 初始化I2C (使用Wire1)
    Wire1.setSDA(I2C_SDA_PIN);
    Wire1.setSCL(I2C_SCL_PIN);
    Wire1.begin();
    Wire1.setClock(100000);
    
    // 初始化GPIO
    pinMode(BUZZER_PIN, OUTPUT);
    pinMode(LED_RUN_PIN, OUTPUT);
    pinMode(LED_NET_PIN, OUTPUT);
    pinMode(LED_ALM_PIN, OUTPUT);
    
    // 初始状态
    digitalWrite(BUZZER_PIN, LOW);
    digitalWrite(LED_RUN_PIN, LOW);
    digitalWrite(LED_NET_PIN, LOW);
    digitalWrite(LED_ALM_PIN, LOW);
    
    Serial.println("GPIO初始化完成");
}

// ==================== 传感器初始化 ====================
void setupSensors() {
    // 初始化SHT30
    sht30.begin(&Wire1);
    sht30.setHeater(false);
    Serial.println("SHT30初始化完成");
    
    // 初始化SGP30
    if (sgp30.begin(&Wire1)) {
        Serial.println("SGP30初始化成功");
        sgp30.setHumidity(50.0);
    } else {
        Serial.println("SGP30初始化失败!");
    }
    
    // 初始化雷达 (LD2402)
    radioSerial.begin(115200);
    Serial.println("LD2402雷达初始化完成");
}

// ==================== 读取传感器数据 ====================
void readSensors() {
    // 读取SHT30
    if (sht30.read()) {
        sysState.temperature = sht30.getTemperature();
        sysState.humidity = sht30.getHumidity();
        Serial.printf("温湿度: %.1f℃, %.1f%%n", sysState.temperature, sysState.humidity);
    } else {
        Serial.println("SHT30读取失败");
    }
    
    // 读取SGP30
    if (sgp30.measure()) {
        sysState.co2 = sgp30.getCO2();
        Serial.printf("CO2: %d ppmn", sysState.co2);
    } else {
        Serial.println("SGP30读取失败");
    }
}

// ==================== 读取雷达数据 ====================
void readRadar() {
    static String radarData = "";
    
    while (radioSerial.available()) {
        char c = radioSerial.read();
        if (c == 'n') {
            // 解析雷达数据
            if (radarData.length() > 0) {
                parseRadarData(radarData);
            }
            radarData = "";
        } else {
            radarData += c;
        }
    }
}

void parseRadarData(String data) {
    // LD2402输出格式示例: "0,0,0,1" (表示检测到运动)
    // 实际格式根据模块手册解析
    if (data.indexOf("1") >= 0) {
        sysState.childDetected = true;
        Serial.println("检测到儿童!");
    } else {
        sysState.childDetected = false;
    }
}

// ==================== 报警处理 ====================
void processAlarm() {
    bool shouldAlarm = false;
    
    // 检查报警条件
    if (alarmConfig.alarmEnabled && sysState.childDetected) {
        if (sysState.temperature > alarmConfig.tempThreshold) {
            shouldAlarm = true;
            Serial.printf("温度超标: %.1f℃ > %.1f℃n", sysState.temperature, alarmConfig.tempThreshold);
        }
        if (sysState.co2 > alarmConfig.co2Threshold) {
            shouldAlarm = true;
            Serial.printf("CO2超标: %d ppm > %d ppmn", sysState.co2, alarmConfig.co2Threshold);
        }
    }
    
    // 更新报警状态
    if (shouldAlarm && !sysState.alarmActive) {
        // 触发报警
        sysState.alarmActive = true;
        triggerAlarm();
    } else if (!shouldAlarm && sysState.alarmActive) {
        // 自动清除报警
        sysState.alarmActive = false;
        clearAlarm();
    }
}

void triggerAlarm() {
    Serial.println("!!! 报警触发 !!!");
    
    // 蜂鸣器报警 (滴答滴答声)
    digitalWrite(BUZZER_PIN, HIGH);
    digitalWrite(LED_ALM_PIN, HIGH);
    
    // 发送短信
    sendSMS(PHONE_NUMBER, "⚠️ 警告: 车内检测到儿童且温度/CO2超标!请立即处理!");
    
    // 上传报警状态到云平台
    uploadAlarmState(true);
    
    // 日志记录
    Serial.println("报警已触发 - 蜂鸣器响, 短信已发送");
}

void clearAlarm() {
    Serial.println("报警已清除");
    digitalWrite(BUZZER_PIN, LOW);
    digitalWrite(LED_ALM_PIN, LOW);
    uploadAlarmState(false);
}

// ==================== 按键处理 ====================
void handleButtons() {
    uint32_t currentTime = millis();
    
    for (int i = 0; i < 3; i++) {
        bool reading = digitalRead(buttons[i].pin);
        
        if (reading != buttons[i].lastState) {
            buttons[i].lastDebounceTime = currentTime;
        }
        
        if ((currentTime - buttons[i].lastDebounceTime) > DEBOUNCE_DELAY) {
            if (reading == LOW && !buttons[i].pressed) {
                buttons[i].pressed = true;
                // 按键触发
                switch (i) {
                    case 0:  // 翻页按键
                        sysState.displayPage = (sysState.displayPage + 1) % 3;
                        Serial.printf("翻页: %dn", sysState.displayPage);
                        break;
                    case 1:  // SOS按键
                        triggerSOS();
                        break;
                    case 2:  // 清除报警按键
                        if (sysState.alarmActive) {
                            clearAlarm();
                            Serial.println("手动清除报警");
                        }
                        break;
                }
            }
            if (reading == HIGH) {
                buttons[i].pressed = false;
            }
        }
        
        buttons[i].lastState = reading;
    }
}

// ==================== SOS功能 ====================
void triggerSOS() {
    if (sysState.sosTriggered) {
        Serial.println("SOS已触发,防止重复");
        return;
    }
    
    sysState.sosTriggered = true;
    Serial.println("!!! SOS一键求救 !!!");
    
    // 发送SOS短信
    sendSMS(PHONE_NUMBER, "🆘 SOS紧急求救!请立即联系车主确认儿童安全!");
    
    // 上传SOS状态到云平台
    uploadSOSState(true);
    
    // 蜂鸣器急促报警
    for (int i = 0; i < 5; i++) {
        digitalWrite(BUZZER_PIN, HIGH);
        delay(100);
        digitalWrite(BUZZER_PIN, LOW);
        delay(100);
    }
    
    // 30秒后重置SOS状态
    setTimeout([]() {
        sysState.sosTriggered = false;
        uploadSOSState(false);
    }, 30000);
}

// ==================== 4G模块通信 ====================
void initLTE(HardwareSerial* lte) {
    Serial.println("初始化4G模块...");
    
    // AT指令初始化
    lte->println("AT");
    delay(100);
    lte->println("AT+CPIN?");
    delay(100);
    lte->println("AT+CREG?");
    delay(100);
    lte->println("AT+CGATT=1");
    delay(100);
    lte->println("AT+CIPMUX=0");
    delay(100);
    lte->println("AT+CSTT="CMNET"");
    delay(100);
    lte->println("AT+CIICR");
    delay(2000);
    
    // 检查网络状态
    lte->println("AT+CIFSR");
    delay(500);
    
    // MQTT连接 (通过4G模块)
    lte->println("AT+MQTTCONN="" MQTT_HOST ""," MQTT_PORT ","" MQTT_CLIENT_ID "",120,1,"" MQTT_USERNAME "","" MQTT_PASSWORD """);
    delay(2000);
    
    sysState.lteConnected = true;
    digitalWrite(LED_NET_PIN, HIGH);
    Serial.println("4G模块初始化成功");
}

void sendSMS(const char* phone, const char* message) {
    Serial.printf("发送短信到 %sn", phone);
    
    lteSerial.println("AT+CMGF=1");
    delay(200);
    lteSerial.print("AT+CMGS="");
    lteSerial.print(phone);
    lteSerial.println(""");
    delay(200);
    lteSerial.print(message);
    lteSerial.write(0x1A);  // Ctrl+Z
    delay(2000);
    
    Serial.println("短信已发送");
}

void uploadData() {
    if (!sysState.lteConnected) return;
    
    // 构建JSON数据
    String json = "{";
    json += ""temperature":" + String(sysState.temperature, 1) + ",";
    json += ""humidity":" + String(sysState.humidity, 1) + ",";
    json += ""co2":" + String(sysState.co2) + ",";
    json += ""childDetected":" + String(sysState.childDetected ? "true" : "false") + ",";
    json += ""alarmActive":" + String(sysState.alarmActive ? "true" : "false") + ",";
    json += ""timestamp":" + String(millis());
    json += "}";
    
    // MQTT发布
    lteSerial.println("AT+MQTTPUB="" MQTT_TOPIC_DATA "","" + json + "",0,0");
    
    Serial.println("数据已上传: " + json);
}

void uploadAlarmState(bool active) {
    if (!sysState.lteConnected) return;
    
    String json = "{";
    json += ""alarmActive":" + String(active ? "true" : "false") + ",";
    json += ""temperature":" + String(sysState.temperature, 1) + ",";
    json += ""co2":" + String(sysState.co2) + ",";
    json += ""timestamp":" + String(millis());
    json += "}";
    
    lteSerial.println("AT+MQTTPUB="" MQTT_TOPIC_ALARM "","" + json + "",0,0");
}

void uploadSOSState(bool active) {
    if (!sysState.lteConnected) return;
    
    String json = "{";
    json += ""sosTriggered":" + String(active ? "true" : "false") + ",";
    json += ""timestamp":" + String(millis());
    json += "}";
    
    lteSerial.println("AT+MQTTPUB="" MQTT_TOPIC_SOS "","" + json + "",0,0");
}

void handleLTEResponse(char c) {
    // 处理4G模块的响应
    static String response = "";
    response += c;
    
    if (c == 'n') {
        if (response.indexOf("OK") >= 0) {
            // 处理OK响应
        } else if (response.indexOf("ERROR") >= 0) {
            Serial.println("4G模块错误: " + response);
        }
        response = "";
    }
}

// ==================== LED指示 ====================
void ledIndicator() {
    static uint32_t lastBlink = 0;
    static bool ledState = false;
    uint32_t currentTime = millis();
    
    // RUN LED: 1Hz闪烁表示运行正常
    if (currentTime - lastBlink >= 500) {
        lastBlink = currentTime;
        ledState = !ledState;
        digitalWrite(LED_RUN_PIN, ledState);
    }
    
    // NET LED: 由4G连接状态控制
    digitalWrite(LED_NET_PIN, sysState.lteConnected ? HIGH : LOW);
    
    // ALM LED: 报警时闪烁
    if (sysState.alarmActive) {
        static bool alarmLedState = false;
        if (currentTime - lastBlink >= 200) {
            alarmLedState = !alarmLedState;
            digitalWrite(LED_ALM_PIN, alarmLedState);
        }
    } else {
        digitalWrite(LED_ALM_PIN, LOW);
    }
}

// ==================== OLED显示 ====================
void displayStartup() {
    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(0, 0);
    display.println("汽车安全预警系统");
    display.println("V1.0");
    display.println("树莓派PICO2");
    display.println("初始化中...");
    display.display();
}

void updateDisplay() {
    display.clearDisplay();
    display.setTextSize(1);
    
    switch (sysState.displayPage) {
        case 0:  // 主界面
            display.setCursor(0, 0);
            display.printf("温度: %.1f℃n", sysState.temperature);
            display.printf("湿度: %.1f%%n", sysState.humidity);
            display.printf("CO2: %d ppmn", sysState.co2);
            display.printf("儿童: %sn", sysState.childDetected ? "检测到" : "未检测");
            display.printf("报警: %sn", sysState.alarmActive ? "⚠️ 报警中" : "安全");
            break;
            
        case 1:  // 系统状态
            display.setCursor(0, 0);
            display.printf("4G网络: %sn", sysState.lteConnected ? "已连接" : "未连接");
            display.printf("SOS状态: %sn", sysState.sosTriggered ? "已触发" : "正常");
            display.printf("温度阈值: %.1f℃n", alarmConfig.tempThreshold);
            display.printf("CO2阈值: %d ppmn", alarmConfig.co2Threshold);
            break;
            
        case 2:  // 系统信息
            display.setCursor(0, 0);
            display.println("系统信息");
            display.printf("运行时间: %lu sn", millis() / 1000);
            display.printf("传感器: SHT30/SGP30n");
            display.printf("雷达: LD2402n");
            display.printf("4G: Air780En");
            break;
    }
    
    display.display();
}

// ==================== 工具函数 ====================
void setTimeout(void (*function)(), uint32_t delayMs) {
    // 简单的定时器实现
    static uint32_t timerStart = 0;
    static void (*callback)() = nullptr;
    
    if (callback == nullptr) {
        timerStart = millis();
        callback = function;
    }
    
    if (callback != nullptr && (millis() - timerStart) >= delayMs) {
        callback();
        callback = nullptr;
    }
}

// ==================== 主循环结束 ====================

五、总结

本项目成功设计并实现了一套基于树莓派PICO2(RP2350)的汽车少儿安全预警系统,集成了SHT30温湿度传感器、SGP30二氧化碳传感器和LD2402毫米波雷达,实现了车内环境参数实时监测与儿童存在检测;系统通过4G模块(Air780E)将数据上传至华为云物联网平台,并支持Android和Windows双平台Qt上位机APP远程监控;当检测到儿童被困且温度或CO2浓度超标时,系统自动触发蜂鸣器声光报警、发送短信通知车主,同时云端推送报警信息,并配备SOS一键求救、阈值可调、手动清除报警等人性化功能;整个系统采用Arduino IDE开发,硬件设计合理、软件架构清晰,充分满足了儿童车内安全防护的实际需求,具有较高的实用价值和推广意义。

相关推荐