基于STM32+微波雷达设计的非接触式睡眠监控系统

简介: 本项目开发一种非接触式的睡眠监控系统,该系统利用先进的60GHz毫米波雷达技术和STM32微控制器,实现了对人体在睡眠过程中的存在感知、运动感知以及生理指标如呼吸频率、心率的实时监测。系统能够自动评估睡眠质量,并在用户睡眠周期结束时提供睡眠评分。为了确保用户能够在任何地点了解自己的睡眠状况,系统集成了Wi-Fi模块,可以将收集到的数据上传至华为云物联网平台,并通过专门设计的移动应用程序供用户远程访问。此外,系统还具备超阈值报警功能,当检测到异常的生理指标时会发出警报提醒。本地1.44寸TFT LCD显示屏用于实时显示监测到的信息,包括生理指标和环境数据。为了全面监测用户的健康状况,系统还加入了

一、前言

1.1 项目介绍

项目设计里用到的全部工具软件都可以在这里下载。

https://pan.quark.cn/s/145a9b3f7f53

【1】项目开发背景

随着现代生活节奏的加快,人们对于健康管理的需求日益增长,尤其是对于睡眠健康的关注度显著提升。良好的睡眠质量不仅关系到个人的精神状态,更直接影响着工作和学习效率乃至整体生活质量。然而,快节奏的生活压力、不规律的生活作息等因素导致越来越多的人遭受睡眠障碍的困扰。传统的睡眠监测方式通常需要佩戴设备或接触式传感器,这可能会干扰到用户的自然睡眠状态,从而影响监测结果的准确性。因此,开发一种非接触式的睡眠监测系统,成为了提高睡眠质量研究的重要方向之一。

非接触式睡眠监测技术的发展,得益于近年来毫米波雷达技术的进步。毫米波雷达具有高精度、强穿透力的特点,可以在不直接接触人体的情况下,精准地捕捉到人体微动,如呼吸和心跳等细微动作。这种技术的应用,不仅可以避免传统监测手段可能带来的不适感,还能在用户不知情的状态下进行连续监测,保证了数据的真实性和有效性。与此同时,物联网技术的发展使得数据的远程传输与分析成为可能,进一步推动了智能健康监测系统的普及。

本项目正是基于这样的背景下展开的。它利用了60GHz毫米波雷达技术,结合高性能的STM32微控制器,设计了一套完整的非接触式睡眠监控系统。该系统不仅能准确地获取用户的睡眠信息,还能通过Wi-Fi连接云端,让用户可以通过手机应用程序随时查看自己的睡眠报告。此外,系统还具备异常生理指标报警功能,能够在第一时间提醒用户注意健康状况,为用户提供了一个全方位、智能化的健康管理方案。通过这一创新性的解决方案,期望能够帮助更多人改善睡眠质量,提升生活质量。

image-20240910171618588

image-20240910171632403

image-20240910171602544

设备安装角度:

image-20240910164203002

【2】设计实现的功能

(1)人体存在感知与运动感知:通过使用60GHz频段的毫米波雷达模块,系统能够感知房间内是否存在人体以及人体的微小运动,如呼吸和心跳的变化。

(2)睡眠状态监测:系统能够根据睡眠过程中身体的运动幅度变化和呼吸心率的变化,实时判断目标的睡眠状态,并在睡眠周期结束后提供一个综合的睡眠评分。

(3)生理指标检测:系统能够检测并记录睡眠者的心率、呼吸频率等重要生理指标,这些数据有助于分析睡眠质量。

(4)远程数据上传与查看:系统集成了Wi-Fi模块,可以将监测到的睡眠数据上传到华为云物联网平台,用户可以通过智能手机应用程序远程查看每天的睡眠质量报告和其他生理指标。

(5)异常情况报警:当检测到的生理指标超出预设的安全阈值时,系统会触发报警机制,及时通知用户或监护人可能存在健康风险。

(6)本地数据显示:系统配备了1.44寸SPI协议的TFT LCD显示屏,用于实时显示监测到的生理指标及环境相关信息,便于用户即时查看。

(7)体温检测:通过集成MLX90614红外体温传感器,系统能够检测人体体温,并将其作为一项重要的生理参数纳入睡眠质量评估体系中。

【3】项目硬件模块组成

(1)主控单元:选用STM32F103RCT6微控制器作为核心处理单元,负责接收来自各传感器的数据,并处理和控制系统的各项功能。

(2)毫米波雷达模块:采用60GHz频段的R60ABD1毫米波雷达模块,用于非接触式地检测人体的存在、呼吸频率和心率等生理信号。

(3)无线通信模块:集成ESP8266-Wi-Fi模块,实现数据的无线传输功能,确保睡眠数据能够实时上传至华为云物联网平台。

(4)显示模块:采用1.44寸TFT LCD显示屏,分辨率为128x128像素,通过SPI协议与主控单元通讯,用于显示监测到的生理指标和环境信息。

(5)体温检测模块:采用MLX90614红外体温传感器,用于无接触地测量人体体温,提供额外的健康监测数据。

(6)电源管理模块:采用外置的5V稳压电压,包括电源转换电路和电池管理电路,确保整个系统能够稳定运行,并为各个模块提供所需电压。

(7)报警模块:设计蜂鸣器声音形式的报警装置,当系统检测到异常生理指标时,能够及时提醒用户。

【4】需求总结

项目:基于STM32+微波雷达设计的非接触式睡眠监控系统

1. 可以实现 人体存在感知、人体运动感知、根据睡眠过程中的身体运动幅度变化和呼吸心率变化,对目标的睡眠状态、呼吸心跳频率进行实时判断,在一段睡眠过程结束后输出睡眠评分呼吸、能够检测心率、睡眠时长、睡眠质量等生理指标
(此功能采用60GHz频段的毫波雷达来实现)
2. 可以实现能将数据通过
    ESP8266-WIFI上传到华为云物联网云平台、设计手机APP可以远程查看每天的睡眠质量、生理指标、环境相关信息。
3. 可以实现当检测到的生理指标数据超过阈值时,系统发出报警提醒。 
4. 可以实现能在本地LCD显示屏显示监测到的生理指标、环境相关信息。
5. 支持检测人体体温。


硬件选型:
主控芯片选择 STM32F103RCT6
LCD显示屏采用1.44寸 SPI协议的 TFT显示屏,分辨率是128x128。
人体体温检测采用MLX90614红外体温传感器。
人体的呼吸、心率、采用60G毫米波 生物感知雷达R60ABD1模块来实现检测。呼吸睡眠雷达基于毫米波雷达体制实现人体生物存在感知及人体运动感知,持续记录人体存在情况,根据睡眠
过程中的身体运动幅度变化和呼吸心率变化,对目标的睡眠状态、呼吸心跳频率进行实时判断,在一段睡眠过程结
束后输出睡眠评分,根据相关睡眠参数的输出结合到健康康养的应用上。

1.2 设计思路

设计思路源于对现代人睡眠健康需求的关注以及对现有睡眠监测技术局限性的思考。在设计之初,注意到传统的睡眠监测手段往往依赖于接触式的穿戴设备,这种方式虽然能够提供较为精确的数据,但却有可能影响用户的自然睡眠状态。因此,设计目标是创造一个非侵入式的睡眠监控系统,能够让用户在自然的睡眠环境中得到准确而有效的监测。

为了实现这一目标,选择了60GHz频段的毫米波雷达技术作为主要的监测手段。毫米波雷达具有非接触、高分辨率和强穿透性等特点,非常适合用来监测人体微弱的生理信号,如呼吸和心跳。通过算法优化,能够从雷达回波中提取出稳定的呼吸和心跳信号,并据此评估睡眠质量和生理指标。

考虑到用户体验的重要性,决定将系统与互联网技术相结合,通过ESP8266-Wi-Fi模块将睡眠数据上传至云端,方便用户通过智能手机应用程序随时随地查看自己的睡眠报告。同时,为了应对突发状况,设计了阈值报警机制,当检测到异常生理指标时,系统能够立即向用户发出警告,以确保用户的安全。

硬件选型方面,选择了性能稳定且广泛使用的STM32F103RCT6作为主控芯片,以确保系统的可靠性和可扩展性。为了直观展示数据,选用了1.44寸的TFT LCD显示屏,它可以清晰地显示监测到的各项生理指标和环境信息。此外,还加入了MLX90614红外体温传感器,以便系统能够监测用户的体温变化,进一步完善健康监测功能。

总体的设计思路是在不干扰用户正常生活的情况下,利用先进的毫米波雷达技术和物联网平台,创建一个能够全天候监测睡眠状态、生理指标,并及时反馈给用户的智能系统。这样不仅能够帮助用户更好地了解自己的睡眠质量,还能在出现异常时提供及时的帮助,从而提升整体的生活品质。

1.3 系统功能总结

功能类别 描述
人体存在感知 利用60GHz毫米波雷达检测房间内是否有人存在。
运动感知 感知人体的微小运动,如呼吸和心跳。
睡眠状态监测 根据身体运动幅度变化和呼吸心率变化实时判断睡眠状态。
生理指标检测 记录并分析心率、呼吸频率等重要生理指标。
远程数据上传 通过ESP8266-Wi-Fi模块将监测数据上传至华为云物联网平台。
移动端查看 用户可以通过手机应用程序远程查看睡眠质量报告和其他生理指标。
异常报警 当检测到的生理指标超过设定阈值时,系统会发出报警提醒。
本地数据显示 通过1.44寸TFT LCD显示屏实时显示监测到的生理指标和环境信息。
体温检测 使用MLX90614红外体温传感器检测人体体温,并将其纳入健康监测数据中。

1.4 开发工具的选择

【1】设备端开发

STM32的编程语言选择C语言,C语言执行效率高,大学里主学的C语言,C语言编译出来的可执行文件最接近于机器码,汇编语言执行效率最高,但是汇编的移植性比较差,目前在一些操作系统内核里还有一些低配的单片机使用的较多,平常的单片机编程还是以C语言为主。C语言的执行效率仅次于汇编,语法理解简单、代码通用性强,也支持跨平台,在嵌入式底层、单片机编程里用的非常多,当前的设计就是采用C语言开发。

开发工具选择Keil,keil是一家世界领先的嵌入式微控制器软件开发商,在2015年,keil被ARM公司收购。因为当前芯片选择的是STM32F103系列,STMF103是属于ARM公司的芯片构架、Cortex-M3内核系列的芯片,所以使用Kile来开发STM32是有先天优势的,而keil在各大高校使用的也非常多,很多教科书里都是以keil来教学,开发51单片机、STM32单片机等等。目前作为MCU芯片开发的软件也不只是keil一家独大,IAR在MCU微处理器开发领域里也使用的非常多,IAR扩展性更强,也支持STM32开发,也支持其他芯片,比如:CC2530,51单片机的开发。从软件的使用上来讲,IAR比keil更加简洁,功能相对少一些。如果之前使用过keil,而且使用频率较多,已经习惯再使用IAR是有点不适应界面的。

image-20221210225339928

【2】上位机开发

上位机的开发选择Qt框架,编程语言采用C++;Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏,Qt很容易扩展,并且允许真正地组件编程。Qt能轻松创建具有原生C++性能的连接设备、用户界面(UI)和应用程序。它功能强大且结构紧凑,拥有直观的工具和库。

image-20230218001243591

image-20230218001219105

1.5 模块的技术详情介绍

【1】ESP8266-WIFI模块

ESP8266是一款广受欢迎的低成本、低功耗的Wi-Fi模块,广泛应用于物联网(IoT)领域。它由乐鑫科技(Espressif Systems)开发,最初作为一款简单易用的无线模块推向市场,但因其强大的功能和灵活性迅速获得了开发者们的青睐。ESP8266内置了Tensilica L106超低功耗32位微处理器,主频最高可达160MHz,并且拥有512KB的SRAM,这使得它不仅能够作为一个简单的Wi-Fi模块使用,还可以作为独立的微控制器来执行复杂的任务。

ESP8266模块支持IEEE 802.11 b/g/n标准,能够工作在2.4GHz频段上。它具有多种工作模式,包括Station模式(客户端)、Access Point模式(热点)以及Station+AP模式(同时作为客户端和热点)。这意味着它可以连接到现有的Wi-Fi网络,也可以自己创建一个Wi-Fi热点供其他设备连接,极大地增加了其在不同应用场景中的适用性。

对于开发者而言,ESP8266的一个重要优势在于其丰富的开发资源和支持。乐鑫科技提供了详细的开发文档,包括硬件接口说明、固件升级指南和API参考手册等。此外,ESP8266还支持多种编程语言,如C/C++和Lua,同时还有成熟的开发框架如Arduino IDE的支持,使得开发者能够快速上手,并利用各种库函数简化开发流程。

ESP8266的低功耗特性也是一大亮点,它提供了多种省电模式,可以根据实际应用需求调整工作状态,以延长电池寿命。这对于那些依赖电池供电的物联网设备来说尤为重要。

ESP8266凭借其出色的性价比、强大的功能、易于开发的特性以及广泛的社区支持,已经成为许多DIY项目、智能家居设备和小型物联网应用的理想选择。无论是作为独立的微控制器还是作为Wi-Fi模块,ESP8266都能够满足大多数物联网项目的需求。

【2】MLX90614红外体温传感器

MLX90614红外体温传感器是由Melexis公司生产的一款高性能、非接触式温度测量传感器。这款传感器集成了红外温度测量功能与环境温度测量功能于一体,适用于需要快速、准确测量物体表面温度的应用场合。由于其非接触式的特点,MLX90614特别适合用于医疗领域,如监测人体体温,以及其他工业或商业用途,例如食品温度检测、设备过热保护等。

MLX90614的工作原理基于红外辐射理论。所有物体都会发射红外辐射,其强度与物体的温度成正比。MLX90614通过检测目标物体发射的红外辐射能量,并结合传感器所在环境的温度,计算出目标物体的表面温度。这款传感器具有较高的灵敏度,能够检测到非常微小的温度变化,并且具有较好的响应速度。

从硬件角度来看,MLX90614采用了SMD(Surface Mount Device)封装,使其易于集成到各种设备中。它具有数字I²C接口,可以方便地与微控制器或其他数字系统进行通信。此外,MLX90614还提供了不同的视场角版本,允许用户根据具体的应用需求选择最适合的角度,从而获得最准确的测量结果。

在使用MLX90614时,需要注意几个关键参数。首先是距离系数(Distance-to-Spot Size Ratio),即传感器与目标之间的距离与目标面积直径之比。这个参数决定了传感器的有效测量区域大小。其次是传感器的温度测量范围,一般为-70°C至+380°C,足以覆盖大部分日常应用。此外,MLX90614还具备较高的测温精度,通常在±0.5°C左右,这使得它在医疗和工业应用中具有很高的实用性。

对于开发人员来说,MLX90614的另一个优点是其易于集成。Melexis提供了详尽的技术文档和支持,包括电路设计指南、编程示例等资源,使得开发者能够快速地将MLX90614集成到他们的产品中。此外,市面上也有许多现成的开发板和库文件,可以帮助开发者简化开发流程,加速产品的上市时间。

综上所述,MLX90614红外体温传感器以其高精度、非接触式测量的特点,在多个行业中得到了广泛应用,尤其是在需要快速、准确温度读数的场合。

【3】微波雷达模块

生物感知雷达R60ABD1模块是一款基于60GHz毫米波雷达技术的产品,专为人体呼吸心率感知及睡眠评估而设计。它采用FMCW(调频连续波)雷达体制,能够针对特定场合内的人员进行呼吸心率频率的输出,并结合长时间的睡眠姿态体动采集,及时上报人员的睡眠状态和历史记录。模块的一发三收天线形式使得它适合于置顶安装模式,能够精准扫描人体全身的动作层析,实现人体动静态时的睡眠探测和不同姿态下的呼吸心率采集。

该模块的工作原理基于雷达天线发射电磁波信号,并接收目标反射回来的回波信号。通过雷达处理器解析不同接收天线回波信号的波形参量之间的相位差和能量变化,从而反馈目标运动的微动能量变化、距离、方向和速度等信息。这使得R60ABD1模块能够探测目标的运动状态和胸腔呼吸起伏的频次状态。在雷达探测范围内,即便是轻微的手部晃动或呼吸引起的胸腔起伏等微小运动,也能够被模块捕捉到。

R60ABD1模块具有多种功能,包括运动检测、呼吸探测、呼吸心率频率采集等功能。模块能够检测到诸如走动或小幅度手晃动等运动,并触发有人状态的指示。当人处于静止状态时,模块也能检测到由呼吸引起的胸腔起伏,并维持有人状态的输出。更重要的是,它能统计呼吸心跳引起的胸腔起伏,并输出每分钟的呼吸心跳数值。这些功能使得该模块在全屋智能、智能家电、区域人员探测和睡眠看护等领域有着广泛的应用前景。

该模块的电气特性包括工作电压在4.6V至6V之间,典型工作电流为93mA,工作温度范围从-20°C至+60°C,存储温度范围则从-40°C至+105°C。其RF性能方面,工作频率位于61GHz至61.5GHz区间,发射功率不超过6dBm。天线增益为4dBi,水平和垂直波束宽度均为20°(-3dB点)。

R60ABD1模块提供了标准的UART通信接口,并支持涂鸦协议,便于与其他设备集成。模块尺寸小巧,体积仅为35mm×31mm×7.5mm,并配有双排插针接口,接口间距为2.0mm。这些接口包括电源输入、地、串口接收和发送端、以及多个可定义的通用I/O引脚。其中,部分引脚可用于输出有人/无人状态、活跃/静止状态、体征参数等信息。

此外,模块还支持多种参数设置,如人体存在开关、呼吸探测开关、心跳探测开关、睡眠探测开关以及探测模式切换开关(实时探测/睡眠模式)。这些设置使得模块可以根据不同应用场景的需求进行灵活配置。在安装方面,R60ABD1模块推荐倾斜安装,并且平行于扫描面的距离不超过1.5米。特别是用于睡眠呼吸心跳探测时,雷达应安装在床头正上方1米的高度,向下倾斜45°对着床中间,确保雷达与人体胸腔的距离在1.5米范围内,以确保雷达正常进行探测。

1.6 微波雷达安装说明

生物感知雷达R60ABD1模块的安装需要遵循特定的指导原则以确保其最佳性能。首先,雷达模块应该朝向为丝印标识的方向进行安装,这意味着在安装时需要确保雷达的正面朝向正确。为了达到理想的探测效果,R60ABD1雷达模块建议采用倾斜安装的方式,倾斜角度应在30到45度之间。这种安装方式有助于雷达的波束覆盖到所需的探测区域,并且可以减少因环境因素引起的误报。

在确定雷达的具体安装位置时,建议将雷达安装在床头正上方大约1米的高度处,这样可以确保雷达的主要波束能够覆盖到床的中心区域。这样做是为了确保雷达可以有效地探测到床上人的呼吸和心跳活动,同时也能够监测到人体的其他微动。安装高度的选择也是基于雷达波束覆盖范围的考虑,以确保人体存在检测的最大距离为约2.5米,而人体呼吸频率检测的最大距离约为1.5米。

除了正确的安装位置和角度外,还需要注意避免雷达前方出现明显的金属或电解质遮挡物。这是因为毫米波雷达的探测机制依赖于雷达波的反射,如果存在金属或电解质遮挡物,则可能会影响雷达波的反射路径,从而影响到雷达的探测准确性。因此,在安装雷达时,应确保雷达前方没有诸如金属窗帘条、风扇、空调电机等潜在的干扰源。

在实际安装过程中,还应当注意雷达模块的安装高度和角度会影响到其探测效果。为了使雷达的主波束能够覆盖到整个睡眠区域,雷达的安装高度应该保持在与床面的高度差在0.9米左右,误差不超过0.2米。同时,雷达模块的安装需要保证其前方没有明显的遮挡物,尤其是金属材质的物体,因为这些物体可能会反射雷达波,造成干扰。

为了确保雷达能够正常工作,安装完成后还需注意雷达模块的供电稳定性。雷达模块对电源品质有一定的要求,需要无门限毛刺或纹波现象,并且需要有效屏蔽来自附近设备的电源噪声。为了保证模块内部VCO电路的正常工作,雷达模块需要+5V到+6V的供电,且电压纹波不能超过100mV。外部电源还需要提供足够的电流输出能力和瞬态响应能力,以防止由于电源不稳定导致的探测距离缩短或误报率增加等问题。

1.7 微波雷达的完整功能概述(快速上手)

R60ABD1呼吸睡眠雷达模组是一款基于60GHz毫米波雷达技术设计的非接触式生物感知设备,主要用于人体存在感知及运动感知。它能够根据睡眠过程中身体运动幅度变化和呼吸心率变化,实时判断目标的睡眠状态、呼吸心跳频率,并在睡眠结束后输出睡眠评分。该模组的探测功能不受温度、湿度、噪声气流、尘埃、光照和人体完全静止等因素的影响,适合安装在室内顶部使用。

模组具备多种功能,包括有人/无人状态检测、人体静止/活跃状态切换检测、人体距离主动上报、体动幅度参数输出、人体方位上报、心跳数值及波形输出、呼吸数值及波形输出、入床/离床状态判断、睡眠状态(清醒/浅睡/深睡)识别、清醒/浅睡/深睡时长统计、睡眠质量评分、睡眠异常上报、异常挣扎上报、无人计时上报以及睡眠质量评级上报等。这些功能通过不同的数据点(DP)以特定的时间间隔或状态变化时上报。

为了确保雷达的准确探测,模组的安装需要遵循一定的规范。雷达应该安装在床头正上方1米的高度,向下倾斜30至45度,以确保主波束能够覆盖到睡眠区域。此外,雷达前方不应有明显的金属或电解质遮挡物,以免影响探测效果。在安装过程中,还需确认雷达探测范围内是否存在干扰源,如空调、风扇等,并尽可能移除这些干扰源。

模组的引脚包括电源输入、地、串口接收与发送端、以及若干备用扩展引脚。其中,部分引脚可以根据用户需求重新定义功能。为了便于用户操作,在官方的文档还介绍了如何准备必要的工具,如TTL串口工具、杜邦线、PC电脑、串口助手终端和Radar-EVB demo板,并给出了上电及工作的数据上报规则和睡眠模式检测逻辑。

在官方的文档最后提供了主要功能测试指引,包括睡眠质量状态判断测试、入离床状态判断测试、离床状态判断测试、呼吸频率测试以及心跳频率测试等。每项测试都有明确的操作步骤和判定标准,以帮助用户验证模组的功能是否正常。此外,官方的文档还对体动幅度参数的输出进行了详细说明,并附带了相关的测试表格格式,便于用户记录和分析测试结果。

二、微波雷达调试过程

2.1 接线说明

image-20240126145448206

2.2 安装说明

image-20240126145600771

倾斜安装: * 确保雷达探测准确性,建议安装在床头上方,以 45°斜向下安装! R60ABD1-呼吸睡眠雷达倾斜安装,倾斜角度为 30~45°,安装在床头上方,雷达安装高度建议为高于床面 0.8-1m;保证雷达主波束覆盖探测区域;雷达前面无明显(金属/电解质)遮挡物及覆盖物。受雷达安装高度及雷达波束范围影响,在该安装模式下,人体存在检测最大距离 L3 ≈ 2.5 米;睡眠检测最大距离 L2 ≈ 2.5 米;人体呼吸频率检测最大距离 L1 ≈ 1.5 米。

image-20240126145701021

image-20240126150859704

2.3 连接电脑调试

将60G毫米波雷达模块与电脑连接,调试模块是否正常可以运行,60G毫米波雷达模块默认的波特率是115200

GND-----GND
VCC-----VCC
TX------RX
RX------TX

image-20240126211201418

image-20240126211215514

串口调试助手返回的数据:

image-20240126212034175

睡眠雷达上位机:

image-20240126212151180

image-20240126212338673

三、华为云服务器部署与上位机APP开发

这里直接看视频,可以了解的更加清楚。

(1)华为云物联网开发(一)设备上云:https://www.bilibili.com/video/BV1Md4y1v7m5

(2)华为云物联网云平台对应的上位机开发步骤:https://www.bilibili.com/video/BV1mr421c75S

四、STM32代码开发(微波雷达模块数据处理)

4.1 微波雷达数据处理(头文件)

#ifndef _DATAHANDLE_H
#define _DATAHANDLE_H

#include "stdint.h"

/* 定义包头及指令信息 */
//帧头
#define HEADER1 0x53
#define HEADER2 0x59
//控制字
#define CMD_TICK 0x01               //心跳包
#define CMD_PRODUCT_INFO 0x02       //产品信息
#define CMD_OTA 0x03                // OTA升级
#define CMD_WORK_STATE 0x05         //工作状态
#define CMD_RADAR_DETECT_RANGE 0x07 //雷达探测范围
#define CMD_BODY_EXIST_DETECT 0x80  //人体存在检测
#define CMD_BREATH_DETECT 0x81      //呼吸检测
#define CMD_SLEEP_DETECT 0x84       //睡眠检测
#define CMD_HEART_DETECT 0x85       //心率检测
//帧尾
#define END1 0x54
#define END2 0x43

/* 枚举读取数据报文的状态 */
typedef enum
{
   
    IDLE,
    SEEN_HEADER1,
    SEEN_HEADER2,
    SEEN_CONTROL,
    SEEN_COMMAND,
    SEEN_LENGTH,
    SEEN_DATA,
    SEEN_SUM,
    SEEN_END1,
    SEEN_END2
} rx_datagram_state_t;

/* 枚举控制模式 */
typedef enum
{
   
    MODE_IDLE,
    MODE_SEND_TICK,
    MODE_SEND_PRODUCT_INFO,
    MODE_SEND_OTA,
    MODE_SEND_WORK_STATE,
    MODE_SEND_RADAR_DETECT_RANGE,
    MODE_SEND_BODY_EXIST_DETECT,
    MODE_SEND_BREATH_DETECT,
    MODE_SEND_SLEEP_DETECT,
    MODE_SEND_HEART_DETECT
} control_mode_t;

/*******************************************************************************/
//人体存在功能
typedef struct
{
   
    uint8_t body_exist_flag;   //有人无人检测标志
    uint8_t work_state;        //运动状态
    uint8_t body_move_param;   //体动参数
    uint16_t body_distance;    //人体距离
    uint8_t body_direction[3]; //人体方位
} body_exist_detect_t;

//呼吸检测功能
typedef struct
{
   
    uint8_t breath_detect_switch; //开关呼吸功能
    uint8_t breath_detect_state;  //呼吸检测状态
    uint8_t breath_detect_value;  //呼吸检测值
    uint8_t breath_wave_data[5];  //呼吸波形
} breath_detect_t;

//睡眠评分
typedef struct
{
   
    uint8_t sleep_detail_exist; //睡眠详细状态
    uint8_t sleep_detail_state; //睡眠详细评分

    uint8_t sleep_detail_score;            //睡眠评分
    uint16_t sleep_detail_time;            //睡眠时间
    uint8_t sleep_detail_awake;            //清醒时长占比
    uint8_t sleep_detail_light;            //浅睡时长占比
    uint8_t sleep_detail_deep;             //深睡时长占比
    uint8_t sleep_detail_away;             //离床时长占比
    uint8_t sleep_detail_away_times;       //离床次数
    uint8_t sleep_detail_turn_over_times;  //翻身次数
    uint8_t sleep_detail_avg_breath;       //平均呼吸
    uint8_t sleep_detail_avg_heart;        //平均心率
    uint8_t sleep_detail_breath_stoptimes; //呼吸停顿次数
    uint8_t sleep_detail_turn_over_L;      //大动作次数
    uint8_t sleep_detail_turn_over_S;      //小动作次数
} sleep_detail_t;

//睡眠检测功能
typedef struct
{
   
    uint8_t sleep_detect_switch; //开关睡眠功能
    uint8_t sleep_bed_state;     //入床/离床状态
    uint8_t sleep_detect_state;  //睡眠检测状态

    uint8_t sleep_wake_hour;  //清醒时间
    uint8_t sleep_light_hour; //浅睡时长
    uint8_t sleep_deep_hour;  //深睡时长

    uint8_t sleep_score;                 //睡眠质量评分
    sleep_detail_t sleep_score_detail;   //睡眠质量评分详情
    sleep_detail_t sleep_score_detail_1; //睡眠质量评分详情1
    uint8_t sleep_score_detail_err;      //睡眠质量评分详情2
} sleep_detect_t;

//心率检测功能
typedef struct
{
   
    uint8_t heart_detect_switch; //开关心率功能
    uint8_t heart_detect_value;  //心率检测值
    uint8_t heart_wave_data[5];  //心率波形
} heart_detect_t;

//数据变化标志
typedef struct
{
   
    unsigned data_change_body_exist : 1;
    unsigned data_change_breath : 1;
    unsigned data_change_sleep : 1;
    unsigned data_change_heart : 1;
} data_change_t;

void ProcessRx(uint8_t *buff,uint8_t size);
// void ProcessRx(void);


extern body_exist_detect_t body_exist_detect;
extern breath_detect_t breath_detect;
extern sleep_detect_t sleep_detect;
extern heart_detect_t heart_detect;

#endif

4.2 微波雷达数据处理(源文件)

#include "datahandle.h"
#include "debug.h"
body_exist_detect_t body_exist_detect = {
   0};
breath_detect_t breath_detect = {
   0};
sleep_detect_t sleep_detect = {
   0};
heart_detect_t heart_detect = {
   0};
data_change_t data_change = {
   0};

/* 定义数据包接收状态的变量,并初始化为空闲状态 */
rx_datagram_state_t rx_datagram_state = IDLE;
control_mode_t control_mode = MODE_IDLE;

/* 协议数据处理函数 */
void ProcessRx(uint8_t *buff, uint8_t size)
{
   
    /****************************************************************************/
    uint8_t receivedbyte, rx_sum, command;
    //数据存储数组
    uint8_t rx_data[10];

    uint16_t rx_dategram_len;
    while (size)
    {
   
        switch (rx_datagram_state)
        {
   
        case IDLE: //在空闲时,判断是否读取帧头1
        {
   
            receivedbyte = *buff;
            if (HEADER1 == receivedbyte)
            {
   
                // printf("HEADER1:%x\n", *buff);
                rx_sum = 0;
                rx_sum += receivedbyte;
                rx_datagram_state = SEEN_HEADER1;
                buff++;
            }
            break;
        }
        case SEEN_HEADER1: //读取第一帧之后,判断是否读取帧头2
        {
   
            receivedbyte = *buff;
            if (HEADER2 == receivedbyte)
            {
   
                // printf("HEADER2:%x\n", *buff);
                rx_sum += receivedbyte;
                rx_datagram_state = SEEN_HEADER2;
                buff++;
            }
            break;
        }

        case SEEN_HEADER2: //读取第二帧后,根据控制字判断数据模式
        {
   
            uint8_t ctrl_mode = *buff; //控制字
            rx_sum += ctrl_mode;

            if (ctrl_mode == CMD_TICK)
                control_mode = MODE_SEND_TICK;
            else if (ctrl_mode == CMD_PRODUCT_INFO)
                control_mode = MODE_SEND_PRODUCT_INFO;
            else if (ctrl_mode == CMD_OTA)
                control_mode = MODE_SEND_OTA;
            else if (ctrl_mode == CMD_WORK_STATE)
                control_mode = MODE_SEND_WORK_STATE;
            else if (ctrl_mode == CMD_RADAR_DETECT_RANGE)
                control_mode = MODE_SEND_RADAR_DETECT_RANGE;
            else if (ctrl_mode == CMD_BODY_EXIST_DETECT)
                control_mode = MODE_SEND_BODY_EXIST_DETECT;
            else if (ctrl_mode == CMD_BREATH_DETECT)
                control_mode = MODE_SEND_BREATH_DETECT;
            else if (ctrl_mode == CMD_SLEEP_DETECT)
                control_mode = MODE_SEND_SLEEP_DETECT;
            else if (ctrl_mode == CMD_HEART_DETECT)
                control_mode = MODE_SEND_HEART_DETECT;
            else
                control_mode = MODE_IDLE;

            rx_datagram_state = SEEN_CONTROL;
            // printf("SEEN_CONTROL:%x\n", ctrl_mode);
            buff++;
        }
        break;

        case SEEN_CONTROL: //读取控制字后,判断命令字
        {
   
            command = *buff; //命令字
            rx_sum += command;
            rx_datagram_state = SEEN_COMMAND;
            // printf("SEEN_COMMAND:%x\n", command);
            buff++;
        }
        break;

        case SEEN_COMMAND: //读取命令字后,识别数据长度
        {
   
            uint8_t len_temp[2];
            len_temp[0] = *buff;
            rx_sum += len_temp[0];

            buff++;
            len_temp[1] = *buff;
            rx_sum += len_temp[1];

            rx_dategram_len = (len_temp[0] << 8) | len_temp[1];
            rx_datagram_state = SEEN_LENGTH;
            // printf("SEEN_LENGTH:%x\n", rx_dategram_len);
            buff++;
        }
        break;

        case SEEN_LENGTH: //读取数据长度后,保存数据
        {
   
            if (size < (int)rx_dategram_len) //判断数据包是否完整
            {
   
                rx_datagram_state = IDLE;
                return;
            }
            uint8_t readlen = rx_dategram_len; //数据包长度
            uint8_t tmp[rx_dategram_len];      //数据包缓存
            uint8_t *ptmp = tmp;               //数据包缓存指针
            while (readlen--)
            {
   
                receivedbyte = *buff;
                *ptmp++ = receivedbyte; //将数据存入缓存
                rx_sum += receivedbyte; //校验和
            }
            // TODO 使用上面操作,可以直接操作rx_data指针,不用拷贝数据到rx_data数组中
            for (uint8_t i = 0; i < rx_dategram_len; i++) //将数据存储到数组中
            {
   
                rx_data[i] = tmp[i];
            }

            rx_datagram_state = SEEN_DATA;
            buff++;
        }
        break;

        case SEEN_DATA: //读取数据后,判断校验和,根据数据、控制字、命令字读取状态
        {
   
            uint8_t getsum = *buff;

            //判断校验和是否正确
            if (getsum != rx_sum)
            {
   
                rx_datagram_state = IDLE;
                return;
            }
            else
            {
   
                //判断控制字模式
                switch (control_mode)
                {
   
                case MODE_SEND_TICK: //心跳包
                {
   
                    printf("MODE_SEND_TICK\n");
                }
                break;

                case MODE_SEND_PRODUCT_INFO: //产品信息
                {
   
                    printf("MODE_SEND_PRODUCT_INFO\n");
                }
                break;

                case MODE_SEND_OTA: // OTA升级
                {
   
                    printf("MODE_SEND_OTA\n");
                }
                break;

                case MODE_SEND_WORK_STATE: //工作状态
                {
   
                    printf("MODE_SEND_WORK_STATE\n");
                }
                break;

                case MODE_SEND_RADAR_DETECT_RANGE: //雷达检测范围
                {
   
                    printf("MODE_SEND_RADAR_DETECT_RANGE\n");
                }
                break;

                case MODE_SEND_BODY_EXIST_DETECT: //人体存在检测
                {
   
                    data_change.data_change_body_exist = 1;
                    switch (command)
                    {
   
                    case 1:
                    {
    //检测人体存在
                        body_exist_detect.body_exist_flag = (0 != rx_data[0]) ? 1 : 0;
                        printf("Body Exist flag State:%d\n", body_exist_detect.body_exist_flag);
                    }
                    break;

                    case 2:
                    {
    //运动状态
                        body_exist_detect.work_state = rx_data[0];
                        printf("Move State:%d\n", body_exist_detect.work_state);
                    }
                    break;

                    case 3:
                    {
    //运动值
                        body_exist_detect.body_move_param = rx_data[0];
                        printf("Body Move Value:%d\n", body_exist_detect.body_move_param);
                    }
                    break;

                    case 4:
                    {
    //人体距离
                        body_exist_detect.body_distance = (rx_data[0] << 8) | rx_data[1];
                        printf("Body Distance:%d\n", body_exist_detect.body_distance);
                    }
                    break;

                    case 5:
                    {
    //人体方位
                        body_exist_detect.body_direction[0] = (rx_data[0] << 8) | rx_data[1];
                        body_exist_detect.body_direction[1] = (rx_data[2] << 8) | rx_data[3];
                        body_exist_detect.body_direction[2] = (rx_data[4] << 8) | rx_data[5];
                    }
                    break;
                    }
                }
                break;

                case MODE_SEND_BREATH_DETECT: //呼吸检测
                {
   
                    data_change.data_change_breath = 1;
                    switch (command)
                    {
   
                    case 1:
                    {
   
                        //呼吸检测状态
                        breath_detect.breath_detect_state = rx_data[0];
                        printf("Breath State:%d\n", breath_detect.breath_detect_state);
                    }
                    break;

                    case 2:
                    {
   
                        //呼吸值
                        breath_detect.breath_detect_value = rx_data[0];
                        printf("Breath Value:%d\n", breath_detect.breath_detect_value);
                    }
                    }
                }
                break;

                case MODE_SEND_SLEEP_DETECT: //睡眠检测
                {
   
                    data_change.data_change_sleep = 1;
                    //判断命令字
                    switch (command)
                    {
   
                    case 1:
                    {
    //入床/离床状态
                        sleep_detect.sleep_bed_state = rx_data[0];
                        printf("Sleep1 State:%d\n", sleep_detect.sleep_bed_state);
                    }
                    break;

                    case 2:
                    {
    //睡眠状态
                        sleep_detect.sleep_detect_state = rx_data[0];
                        printf("Sleep State:%d\n", sleep_detect.sleep_detect_state);
                    }
                    break;

                    case 3:
                    {
    //清醒时长
                        sleep_detect.sleep_wake_hour = (rx_data[0] << 8) | rx_data[1];
                        printf("Sleep Wake Hours:%d\n", sleep_detect.sleep_wake_hour);
                    }
                    break;

                    case 4:
                    {
    //浅睡时长
                        sleep_detect.sleep_light_hour = (rx_data[0] << 8) | rx_data[1];
                        printf("Sleep Light Hours:%d\n", sleep_detect.sleep_light_hour);
                    }
                    break;

                    case 5:
                    {
    //深睡时长
                        sleep_detect.sleep_deep_hour = (rx_data[0] << 8) | rx_data[1];
                        printf("Sleep Deep Hours:%d\n", sleep_detect.sleep_deep_hour);
                    }
                    break;

                    case 0x06:
                    {
    //睡眠质量评分
                        sleep_detect.sleep_score = rx_data[0];
                        printf("Sleep Score:%d\n", sleep_detect.sleep_score);
                    }
                    break;

                    case 0x0c:
                    {
    //睡眠检测结果
                        sleep_detect.sleep_score_detail.sleep_detail_exist = rx_data[0];
                        sleep_detect.sleep_score_detail.sleep_detail_state = rx_data[1];
                        sleep_detect.sleep_score_detail.sleep_detail_avg_breath = rx_data[2];
                        sleep_detect.sleep_score_detail.sleep_detail_avg_heart = rx_data[3];
                        sleep_detect.sleep_score_detail.sleep_detail_turn_over_times = rx_data[4];
                        sleep_detect.sleep_score_detail.sleep_detail_turn_over_L = rx_data[5];
                        sleep_detect.sleep_score_detail.sleep_detail_turn_over_S = rx_data[6];
                        sleep_detect.sleep_score_detail.sleep_detail_breath_stoptimes = rx_data[7];
                    }
                    break;

                    case 0x0d:
                    {
    //睡眠详情
                        sleep_detect.sleep_score_detail_1.sleep_detail_score = rx_data[0];
                        sleep_detect.sleep_score_detail_1.sleep_detail_time = (rx_data[1] << 8) | rx_data[2];
                        sleep_detect.sleep_score_detail_1.sleep_detail_awake = rx_data[3];
                        sleep_detect.sleep_score_detail_1.sleep_detail_light = rx_data[4];
                        sleep_detect.sleep_score_detail_1.sleep_detail_away = rx_data[5];
                        sleep_detect.sleep_score_detail_1.sleep_detail_away_times = rx_data[6];
                        sleep_detect.sleep_score_detail_1.sleep_detail_avg_breath = rx_data[7];
                        sleep_detect.sleep_score_detail_1.sleep_detail_avg_heart = rx_data[8];
                        sleep_detect.sleep_score_detail_1.sleep_detail_breath_stoptimes = rx_data[9];
                    }
                    break;

                    case 0x0e:
                    {
    //异常检测
                        sleep_detect.sleep_score_detail_err = rx_data[0];
                    }
                    break;
                    }
                }
                break;

                case MODE_SEND_HEART_DETECT: //心率检测
                {
   
                    data_change.data_change_heart = 1;
                    switch (command)
                    {
   
                    case 0:
                    {
    //开关心率检测
                    }
                    break;

                    case 2:
                    {
    //心率值
                        heart_detect.heart_detect_value = rx_data[0];
                        printf("Heart rate Value:%d\n", heart_detect.heart_detect_value);
                    }
                    break;

                    case 5:
                    {
    //心率波形
                        heart_detect.heart_wave_data[0] = rx_data[0];
                        heart_detect.heart_wave_data[1] = rx_data[1];
                        heart_detect.heart_wave_data[2] = rx_data[2];
                        heart_detect.heart_wave_data[3] = rx_data[3];
                        heart_detect.heart_wave_data[4] = rx_data[4];
                    }
                    break;
                    }
                }
                break;

                case MODE_IDLE: //空闲
                {
   
                }
                break;
                }
            }
            rx_datagram_state = SEEN_SUM;
            buff++;
        }
        break;

        case SEEN_SUM: //读取校验后,判断帧尾1
        {
   
            rx_datagram_state = (END1 == *buff) ? SEEN_END1 : IDLE;
        }
        break;

        case SEEN_END1: //读取帧尾1后,判断帧尾2
        {
   
            rx_datagram_state = (END2 == *buff) ? SEEN_END2 : IDLE;
        }
        break;

        case SEEN_END2: //判断帧尾2后,设置接收完成标志
        {
   
            //    rx_flag = 1;
            rx_datagram_state = IDLE;
        }
        break;


        default:
        {
   
            receivedbyte = 0;
            rx_datagram_state = IDLE;
            size = 0;
            break;
        }
        }
        size--;
    }
}

五、总结

本项目开发一种非接触式的睡眠监控系统,该系统利用先进的60GHz毫米波雷达技术和STM32微控制器,实现了对人体在睡眠过程中的存在感知、运动感知以及生理指标如呼吸频率、心率的实时监测。系统能够自动评估睡眠质量,并在用户睡眠周期结束时提供睡眠评分。为了确保用户能够在任何地点了解自己的睡眠状况,系统集成了Wi-Fi模块,可以将收集到的数据上传至华为云物联网平台,并通过专门设计的移动应用程序供用户远程访问。此外,系统还具备超阈值报警功能,当检测到异常的生理指标时会发出警报提醒。本地1.44寸TFT LCD显示屏用于实时显示监测到的信息,包括生理指标和环境数据。为了全面监测用户的健康状况,系统还加入了MLX90614红外体温传感器来检测人体体温。通过集成多种传感器和技术,该项目为健康管理和智能家居应用提供了有力支持。

相关实践学习
钉钉群中如何接收IoT温控器数据告警通知
本实验主要介绍如何将温控器设备以MQTT协议接入IoT物联网平台,通过云产品流转到函数计算FC,调用钉钉群机器人API,实时推送温湿度消息到钉钉群。
阿里云AIoT物联网开发实战
本课程将由物联网专家带你熟悉阿里云AIoT物联网领域全套云产品,7天轻松搭建基于Arduino的端到端物联网场景应用。 开始学习前,请先开通下方两个云产品,让学习更流畅: IoT物联网平台:https://iot.console.aliyun.com/ LinkWAN物联网络管理平台:https://linkwan.console.aliyun.com/service-open
目录
相关文章
|
6月前
|
物联网 开发者 智能硬件
STM32:引领嵌入式系统新时代的微控制器
STM32:引领嵌入式系统新时代的微控制器
|
6月前
|
传感器 监控
基于STM32的智能工厂生产线监控系统设计与实现
基于STM32的智能工厂生产线监控系统设计与实现
362 0
|
6月前
STM32F103标准外设库——SysTick系统定时器(八)
STM32F103标准外设库——SysTick系统定时器(八)
487 0
STM32F103标准外设库——SysTick系统定时器(八)
|
1月前
|
存储 机器学习/深度学习 编解码
基于STM32的车牌识别系统
基于STM32的车牌识别系统
76 0
|
1月前
|
传感器 网络协议 物联网
基于STM32的环境监测系统 (esp8267)(下)
基于STM32的环境监测系统 (esp8267)(下)
90 0
|
1月前
|
传感器 测试技术 芯片
基于STM32的环境监测系统 (esp8266)(上)
基于STM32的环境监测系统 (esp8266)(上)
281 0
|
2月前
|
存储 传感器 Linux
STM32微控制器为何不适合运行Linux系统的分析
总的来说,虽然技术上可能存在某些特殊情况下将Linux移植到高端STM32微控制器上的可能性,但从资源、性能、成本和应用场景等多个方面考虑,STM32微控制器不适合运行Linux系统。对于需要运行Linux的应用,更适合选择ARM Cortex-A系列处理器的开发平台。
237 0
|
6月前
|
内存技术 存储 Android开发
MCU最小系统电路设计(以STM32F103C8T6为例)-3
MCU最小系统电路设计(以STM32F103C8T6为例)
MCU最小系统电路设计(以STM32F103C8T6为例)-3
|
6月前
|
芯片
MCU最小系统电路设计(以STM32F103C8T6为例)-1
MCU最小系统电路设计(以STM32F103C8T6为例)
MCU最小系统电路设计(以STM32F103C8T6为例)-1
|
4月前
|
前端开发 安全
stm32f407探索者开发板(十一)——SystemInit时钟系统初始化剖析
stm32f407探索者开发板(十一)——SystemInit时钟系统初始化剖析
158 0

热门文章

最新文章