基于Arduino的心率计步检测时钟显示设计
文章目录
- 基于Arduino的心率计步检测时钟显示设计
- 前言
- 一、结果展示
- 二.研究背景与行业痛点
-
- 2.研究目标与范围
- 3.需求分析与技术要求
- 代码如下:
- 三. 项目存在问题
-
- 未来优化方向
- 总结
前言
本项目聚焦健康管理与时间管理一体化需求,针对现有健康监测设备功能单一、成本高、数据存储查询不便等痛点,设计并实现了一款基于 Arduino UNO 的心率计步检测时钟显示系统。硬件上整合 ADXL345 倾角传感器、DS3231 时钟模块、PulseSensor 心率传感器等核心部件,软件基于 Arduino IDE 开发,实现心率与步数精准采集、时间实时显示、数据本地存储、按键查询及异常震动提醒等功能。系统采用多模态异常检测技术,融合多传感器数据降低误报率,且具备成本低廉、操作便捷 支持二次开发的优势。测试验证显示,心率测量误差≤±5 次 / 分钟,计步准确率≥90%,可稳定满足日常佩戴、运动健身等场景的健康与时间管理需求。 关键词:Arduino UNO;心率监测;计步功能;时钟显示;数据存储;震动提醒;多模态异常检测
一、结果展示
基于Arduino的心率计步检测时钟显示
二.研究背景与行业痛点
研究背景随着社会经济的快速发展和人们生活水平的不断提高,健康管理意识逐渐深入人心,越来越多的人开始关注自身的生理状态和运动情况。心率、步数作为反映人体健康状况和运动强度的核心指标,其实时监测和记录对个人健康管理具有重要意义。同时,时间管理也是日常生活和工作中不可或缺的一部分,将时间显示与健康数据监测相结合,能够更好地满足人们对便捷化、一体化健康管理工具的需求。在技术发展层面,嵌入式技术、传感器技术的不断进步为智能健康监测设备的研发提供了有力支撑[10]。Arduino作为一款开源的嵌入式开发平台,具有成本低廉、操作简便、兼容性强等优势,被广泛应用于各类智能硬件研发项目中[2]。各类高精度传感器的出现,也使得心率、步数等数据的精准采集成为可能。在此背景下,研发一款基于Arduino的心率计步检测时钟显示系统,兼具实用性、经济性和易扩展性,具有重要的现实意义和应用价值。 行业困境一是功能单一或集成度低。部分设备仅能实现心率监测或计步功能,缺乏时间显示、数据存储等附加功能,无法满足用户一体化的使用需求[10];而部分多功能设备则存在体积庞大、操作复杂等问题,不便携带和日常使用。 二是成本较高。主流的智能手环、智能手表等设备价格偏高[10],对于普通用户或学生群体而言,存在一定的消费门槛;同时,这类设备的维修和维护成本也相对较高,增加了用户的使用负担。 三是数据存储与查询不便。部分低成本监测设备不具备数据存储功能[10],无法记录历史数据供用户后续查看和分析;即使部分设备支持数据存储,也多依赖于手机APP等外部设备,在无手机辅助的情况下,用户无法直接查询历史数据,使用场景受限[12]。 四是缺乏个性化提醒功能。现有设备的提醒功能多集中在来电、短信等通知提醒,心率异常、运动过量需要休息等健康相关的个性化提醒功能较为匮乏,难以真正发挥健康管理的辅助作用。 五是技术门槛高,难以二次开发。多数商用设备的软硬件架构封闭,普通用户或开发者无法根据自身需求进行二次开发和功能扩展,限制了设备的个性化定制和功能升级。 技术适配针对上述行业痛点,本项目选用Arduino UNO核心板作为主控制器,其开源特性和丰富的扩展接口能够有效降低开发成本和技术门槛,方便用户进行二次开发和功能扩展[1]。在传感器选型方面,ADXL345倾角传感器具有高精度、低功耗的特点,能够精准采集人体运动姿态数据,实现可靠计步;DS3231时钟模块具备极高的计时精度,且内置温补晶体振荡器,在宽温度范围内仍能保持稳定计时,可满足时间显示的精准性需求[6];心率传感器采用 plug-and-play 设计,易于与Arduino UNO连接,能够快速实现心率数据的采集;LCD1602液晶显示屏作为一种成熟的显示模块,具有显示清晰、功耗低、成本低廉等优势,可直观呈现心率、步数、时间等信息;震动传感器则能在心率异常或需要休息时及时发出提醒,提升系统的健康管理辅助能力。 此外,Arduino UNO内置的EEPROM可直接用于存储当天步数、心率信息,无需额外添加存储模块,进一步降低了系统成本;按键模块的加入则实现了历史数据的便捷查询,解决了传统低成本设备数据查询不便的问题。各模块之间的技术适配性良好,能够协同工作,构建出一套功能完善、成本低廉、操作简便的心率计步检测时钟显示系统。
2.研究目标与范围
研究目标构建以Arduino UNO为核心的硬件架构,实现各模块(ADXL345倾角传感器、DS3231时钟模块、心率传感器、LCD1602液晶、按键、震动传感器)的有效连接和协同工作。 实现心率、步数、时间信息的精准采集与实时处理,确保心率测量误差在合理范围内,计步准确率达到预期标准,时间显示精准无误。 完成LCD1602液晶显示屏的驱动设计,实现心率、步数、时间等信息的清晰、稳定显示,界面布局合理,易于用户读取。 利用Arduino UNO内置的EEPROM,实现当天步数、心率信息的可靠存储,确保数据在断电后不丢失。 设计按键交互逻辑,实现历史存储数据的便捷查询,支持用户通过按键切换查看不同时段的步数和心率信息。 实现震动提醒功能,当检测到心率异常(过高或过低)或用户连续运动时间过长需要休息时,震动传感器能够及时发出提醒信号。 优化系统性能,降低系统功耗,提升系统的稳定性和可靠性,确保系统能够长时间稳定运行;同时控制系统成本,打造一款性价比高的智能健康监测设备。
研究范围硬件选型与电路设计:根据系统功能需求,完成Arduino UNO核心板、ADXL345倾角传感器、DS3231时钟模块、心率传感器、LCD1602液晶、按键、震动传感器等模块的选型;设计各模块与Arduino UNO的连接电路,绘制电路原理图,确保电路连接的正确性和稳定性。 软件程序开发:基于Arduino IDE开发环境,编写主控制器程序,实现对各传感器数据的采集、处理和解析;开发LCD1602显示驱动程序,实现多信息的合理显示;编写EEPROM数据存储与读取程序,完成当天步数、心率信息的存储和历史数据查询;设计按键中断处理程序,实现按键的精准响应和功能切换;开发震动提醒控制程序,根据心率数据和运动时长判断是否需要发出提醒。 系统集成与调试:将硬件电路与软件程序进行集成,对系统进行整体调试,解决各模块之间的兼容性问题、数据传输延迟问题、显示异常问题等,确保系统能够正常实现各项预设功能。 功能测试与性能验证:设计合理的测试方案,对系统的心率采集精度、计步准确率、时间显示精度、数据存储可靠性、按键响应速度、震动提醒灵敏度等功能和性能指标进行测试,验证系统是否达到预设目标。 系统优化:根据测试结果,对系统的硬件电路和软件程序进行优化,降低系统功耗,提升数据采集精度和系统运行稳定性;优化LCD1602显示界面,提升用户体验;优化按键交互逻辑,使操作更加便捷。 本项目不涉及复杂的无线通信功能(如蓝牙、WiFi数据传输)开发,也不涉及电池续航能力的极致优化(仅考虑基础低功耗设计),重点聚焦于核心功能的实现和基础性能的保障
3.需求分析与技术要求
需求分析 用户需求精准监测需求:用户希望系统能够精准测量心率和步数数据[3],为健康管理和运动规划提供可靠依据。 实时显示需求:用户需要能够直观、实时地查看当前心率、步数和时间信息[3],以便及时了解自身状态。 数据存储与查询需求:用户希望系统能够自动保存当天的心率和步数信息[3],并且能够方便地查询历史数据,了解自身健康和运动状况的变化趋势。 健康提醒需求:用户需要系统具备健康提醒功能,当心率出现异常(过高或过低)或运动时间过长需要休息时,能够及时发出提醒,避免健康风险。 操作便捷需求:用户希望系统的操作方式简单易懂,无需复杂的设置和学习过程,无论是佩戴、启动还是查询数据,都能轻松完成。 成本经济需求:用户希望系统的价格相对低廉,性价比高,能够满足普通消费者的购买能力。 稳定性需求:用户希望系统能够长时间稳定运行[11],在日常使用过程中不出现频繁死机、数据丢失、显示异常等问题。
基础硬件层:软件架构的技术底座规格表
| ADXL345(计步模块) | 1. 三轴加速度检测实现精准计步2. 运动状态识别(静止/行走/跑步) | – 测量范围:±2g/±4g/±8g/±16g(可配置)- 分辨率:13位- 输出数据率:0.1Hz~3200Hz- 计步误差:±5%(常规行走)- 功耗:40μA(工作)/0.1μA(待机) |
PulseSensor(心率模块) | 1. 光电式脉搏检测输出实时心率2. 脉搏波形采集用于HRV分析3. 心率异常阈值报警4. 支持指尖/腕部佩戴 | – 检测范围:30-240BPM- 采样率:100Hz- 响应时间:<1秒- 误差:±2BPM(静息)- 光源:660nm绿光LED
LCD1602(显示模块) | 1. 显示步数、心率、时间等数据2. 异常状态提示3. 菜单交互界面 | – 规格:16×2字符- 显示颜色:蓝底白字/黄绿底黑字- 视角:60°(最佳)- 刷新率:50Hz
DS3231(时钟模块) | 1. 高精度实时时钟(含闰年补偿)2. 数据时间戳标记3. 温度补偿 | – 精度:±2ppm(0-40℃)- 温度范围:-40~+85℃- 温度精度:±3℃- 供电:主电源+CR2032备用电池
SW-420(震动传感器) | 1. 运动有效性判断2. 安全预警(如跌倒检测)3. 震动计数 | – 触发方式:水平/垂直- 灵敏度:可调节- 响应时间:<5ms- 工作温度:-20~+80℃
应用场景需求
- 持续监测心率步数
- 实时时间显示
- 异常心率提醒
- 体积小巧佩戴舒适
- 精准计步和心率监测
- 运动强度分析
- 抗汗水震动干扰
- 时间管理功能
- 久坐提醒
- 状态监测
- 时段数据查询
- 健康分析
- 运动总结
技术需求规范
- 心率误差:±5BPM
- 计步准确率:≥90%
- 时钟误差:≤2ppm
- 心率采集周期:≤1秒
- 计步延迟:≤0.5秒
- 按键响应:≤0.2秒
- 无残影闪烁
- 字符清晰可读
- 信息分区明确
- EEPROM存储24时段数据
- 数据可靠性保障
- 强度适中可感知
- 触发条件可配置
- 工作电流:≤50mA
- 电池续航:≥8小时
- 运行时长:≥24小时
- 抗干扰能力
- 温度范围:0-70℃
核心技术实现方案
- 心率:光电传感器+ADC转换+滤波算法
- 步数:三轴加速度+阈值检测算法
- 时间:RTC模块自动计时
- 数据解析与异常判断
- 步数累计统计
- 时间格式转换
- LCD1602并行驱动
- 信息分区显示
- 动态刷新机制
- EEPROM分段存储
- 增量写入策略
- 历史数据查询
- 双按键设计
- 中断触发机制
- 防抖处理
- 数字控制震动
- 多条件触发
- 参数可配置基础硬件层:软件架构的 “技术底座”标示表
模块名称 核心功能 关键参数 ADXL345(计步) 1.三轴加速度检测,实现计步功能 2.运动状态识别(静止 / 行走 / 跑步) 测量范围:±2g/±4g/±8g/±16g(可配置)[14];分辨率:13位[14];输出数据率:0.1Hz~3200Hz;计步误差:±5%(常规行走场景);功耗:测量模式约40μA,待机约0.1μA[14]
PulseSensor(心率脉搏) 1.光电式脉搏检测,输出实时心率值 2.脉搏波形采集,用于心率变异性(HRV)分析 3.心率异常阈值报警 4. 兼容指尖 / 腕部佩戴检测 1. 检测范围:30~240次/分钟(BPM);2. 采样率:约100Hz;3. 响应时间:<1秒;4. 误差:±2BPM(静息状态);5. 光源:绿光LED(660nm)
LCD1602(显示器) 1.显示实时步数、心率值、时间 / 日期 显示运动时长、卡路里消耗等衍生数据 异常状态提示(如心率过高 / 过低) 菜单交互(参数设置) 1. 显示规格:16列×2行字符;2. 显示颜色:蓝底白字/黄绿底黑字;3. 视角:60°(最佳);4. 刷新率:约50Hz;
DS3231(时钟) 1.实时时钟(RTC):年 / 月 / 日 / 时 / 分 / 秒(含闰年补偿)[6] 计时基准,用于计步 / 心率数据的时间戳[14] 温度补偿(提升计时精度)[14] 1. 计时精度:±2ppm[6](0~40℃),年误差<1分钟;2. 温度测量范围:-40~+85℃;温度精度:±3℃;4. 供电:主电源+备用电池(CR2032);数据保持:备用电池可续航[14]
震动传感器 (SW-420) 震动检测与触发 配合计步模块辅助判断运动有效性(过滤非行走震动) 震动阈值触发中断,实现安全预警(如跌倒提醒) 简单震动计数功能 触发方式:水平/垂直震动触发; 震动灵敏度:可通过螺丝调节; 响应时间:<5ms;恢复时间:<20ms; 工作温度:-20~+80℃
场景需求本系统的主要应用场景包括日常佩戴、运动健身、学习工作等,不同场景下的需求如下: 日常佩戴场景:在日常出行、购物、休闲等场景下,用户需要系统体积小巧、佩戴舒适,能够持续监测心率和步数,实时显示时间,并且在心率异常时及时提醒,不影响正常生活。 运动健身场景:在跑步、散步、健身等运动场景下,用户需要系统能够精准计步和监测运动过程中的心率变化,帮助用户掌握运动强度,避免运动过量;同时,系统需要具备一定的抗干扰能力,能够应对运动过程中的震动、汗水等环境因素影响。 学习工作场景:在学习或工作过程中,用户需要系统显示准确的时间,帮助其合理规划时间;同时,当用户连续学习或工作时间过长时,系统能够发出休息提醒,结合心率数据判断用户状态,辅助用户进行高效的时间和健康管理。 数据回顾场景:用户在一天结束后或特定时间点,需要能够通过简单操作查询当天不同时段的心率和步数数据,对当天的健康和运动状况进行回顾和总结,为后续的健康管理和运动规划提供参考。 技术需求数据采集精度:心率采集范围为60-180次/分钟,测量误差不超过±5次/分钟;计步准确率在正常行走、跑步等运动状态下不低于90%;DS3231时钟模块的计时精度在0°C-70°C范围内误差不超过2ppm。 响应速度:心率数据采集周期不超过1秒,计步响应延迟不超过0.5秒,按键按下后响应时间不超过0.2秒,确保用户操作和数据查看的及时性。 显示效果:LCD1602液晶显示屏显示清晰,无残影、无闪烁,字符尺寸适中,在正常光线条件下用户可轻松读取;界面布局合理,心率、步数、时间信息分区显示,一目了然。 数据存储:EEPROM能够可靠存储当天的心率和步数数据,至少支持存储24个时段的分段数据[9]。 震动提醒:震动传感器的震动强度适中,在用户佩戴状态下能够清晰感知,同时不会对用户造成过度干扰;提醒触发条件可根据实际需求进行设置和调整。 功耗控制:系统在正常工作状态下功耗不超过50mA[11],支持外接5V电源或电池供电,电池供电模式下可连续工作至少8小时。 稳定性:系统在正常使用环境下(温度0°C-70°C,湿度20%-80%)能够连续稳定运行不少于24小时,无死机、数据丢失、显示异常等问题;具备一定的抗干扰能力,能够抵抗日常电磁干扰和运动震动的影响。 核心技术实现 数据采集模块实现:心率数据采集:采用专用心率传感器,通过模拟信号接口与Arduino UNO的模拟输入引脚连接[1]。传感器通过LED发光二极管向人体指尖或耳垂发射光线,利用人体血液对光线的吸收量随心跳变化的原理,通过内置的光敏传感器采集反射光信号,将其转换为模拟电信号[3]。Arduino UNO通过ADC模块对模拟电信号进行模数转换,得到数字化的心率原始数据。为提高采集精度,在程序中加入信号滤波算法[2],去除环境光干扰和人体运动带来的噪声信号,再通过阈值判断法识别心跳峰值[7],计算出实时心率。 步数数据采集:选用ADXL345倾角传感器,通过I2C通信接口与Arduino UNO连接[1]。该传感器能够采集X、Y、Z三个轴的加速度数据,反映人体的运动姿态。系统通过实时采集加速度数据,利用阈值检测算法[5]判断用户是否处于运动状态(如行走、跑步),当检测到加速度超过预设阈值且满足一定的时间条件时[5],判定为一步,实现计步功能[13]。同时,通过对三轴加速度数据的融合处理[4],能够有效区分不同运动状态,减少非运动状态下的误计步[3]。 时间数据采集:采用DS3231时钟模块,通过I2C通信接口与Arduino UNO连接。该模块内置高精度温补晶体振荡器和实时时钟电路,能够自动完成时间的计时。Arduino UNO通过I2C通信协议读取DS3231模块内的时间寄存器数据[6],获取当前的秒、分、时、日、月、年等时间信息[1]。为确保时间的精准性,系统在初始化时对DS3231模块进行时间校准,后续定期读取时间数据,实现时间的实时更新。 数据处理模块实现:数据处理模块是系统的核心,由Arduino UNO主控制器完成。主控制器实时接收来自心率传感器、ADXL345倾角传感器和DS3231时钟模块的数据,进行统一的解析和处理。对于心率数据,除了进行滤波和峰值检测外,还会判断是否处于预设的正常范围(如60-180次/分钟),若超出范围则标记为异常状态;对于步数数据,会进行累计统计,并结合时间数据实现分段步数记录;对于时间数据,会进行格式转换,适配LCD1602的显示要求。同时,主控制器还会根据处理后的数据,判断是否需要触发震动提醒。 显示控制模块实现:采用LCD1602液晶显示屏,通过并行接口与Arduino UNO连接。系统基于LCD1602的HD44780控制器驱动协议[8],编写显示驱动程序。程序中首先对LCD1602进行初始化配置,设置显示模式(16×2字符显示)、光标状态(隐藏或显示)等参数;然后将处理后的心率、步数、时间数据转换为对应的ASCII码,通过控制RS、R/W、E等控制引脚和D0-D7数据引脚,将数据写入LCD1602的显示RAM中,实现数据的分区显示。例如,第一行显示时间信息,第二行显示心率和步数信息。 数据存储模块实现:利用Arduino UNO内置的EEPROM实现数据存储[9]。系统将当天的时间划分为多个时段(如每小时为一个时段),每个时段对应EEPROM的一个存储单元。程序中编写EEPROM写入函数,定期将当前时段的累计步数和平均心率数据写入对应的存储单元;同时编写EEPROM读取函数,当用户通过按键查询历史数据时,根据按键选择的时段,读取对应的存储单元数据。为避免EEPROM过度写入导致寿命缩短,程序中采用增量写入策略,仅在数据发生变化时进行写入操作。 按键交互模块实现:设置2个功能按键,分别连接Arduino UNO的数字输入引脚,并配置内部上拉电阻,避免按键抖动。程序中采用中断触发方式处理按键事件,当用户按下按键时,触发外部中断[2],主控制器暂停当前任务,执行按键处理函数。通过判断按键的类型(如查询键、切换键)和按下的时长,实现不同的功能,如切换显示界面、查询上一时段/下一时段的历史数据等。为提高按键交互的可靠性,程序中加入按键防抖处理,通过延时判断和多次采样确认按键状态。 震动提醒模块实现:选用小型震动传感器,通过数字输出引脚与Arduino UNO连接。主控制器根据处理后的心率数据和步数数据,判断是否需要触发提醒:当心率超出正常范围时,触发震动提醒;当用户连续运动时间过长(通过步数累计和时间判断)时,也触发震动提醒。程序中通过控制Arduino UNO的数字引脚输出高低电平,控制震动传感器的启停,提醒时长和震动频率可通过程序进行设置。
代码如下:
#define USE_ARDUINO_INTERRUPTS true // Set-up low-level interrupts for most acurate BPM math.
#include <PulseSensorPlayground.h> //需要搜索"PulseSensor Playground 添加后方可编译成功
#include <EEPROM.h>
#include <Adafruit_ADXL345_U.h>
#include <Wire.h>
#include <DS3231.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
/*
*/
#include <SoftwareSerial.h>
#include "lcd1602.h"
// 串口
#define _baudrate 115200
#define _rxpin 13
#define _txpin 12
#define WIFI_UART Serial
#define DBG_UART dbgSerial //调试打印串口
//—ADXL345———-//
int Z_temp; //z轴加速度缓存值
int stepout; //步数
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified();
//———按键—————————–//
const int inputPin1 = 10;
const int inputPin2 = 11;
//——震动传感器————————–//
const int VIBRATION_PIN = A3; // 震动传感器/振动马达连接引脚A3
const int BUZZER_PIN = 8; // 可选:蜂鸣器引脚(如果需要声音提醒)
//——心率正常范围定义——————–//
const int HEART_RATE_MIN = 50; // 心率过低阈值
const int HEART_RATE_MAX = 120; // 心率过高阈值
const int REST_REMINDER_INTERVAL = 60; // 每60分钟提醒休息一次(单位:分钟)
const int REST_DURATION = 10; // 建议休息时长(单位:分钟)
//——提醒相关变量————————//
unsigned long lastRestReminderTime = 0; // 上次休息提醒时间
unsigned long lastHeartRateCheck = 0; // 上次心率检查时间
bool isResting = false; // 是否正在休息模式
unsigned long restStartTime = 0; // 休息开始时间
unsigned long lastVibrationTime = 0; // 上次震动时间
bool heartRateAlertActive = false; // 心率警报是否激活
int vibrationInterval = 500; // 震动间隔(毫秒)
//——pulse sensor—————————-//
// Variables
const int PulseWire = 1; // PulseSensor PURPLE WIRE connected to ANALOG PIN 1
const int LED13 = 13; // The on-board Arduino LED, close to PIN 13.
int Threshold = 550; // Determine which Signal to "count as a beat" and which to ignore.
// Use the "Gettting Started Project" to fine-tune Threshold Value beyond default setting.
// Otherwise leave the default "550" value.
byte bpm_temp=0;
PulseSensorPlayground pulseSensor; // Creates an instance of the PulseSensorPlayground object called "pulseSensor"
//——————————————————–//
typedef struct
{
byte data[30];
byte len;
} edp_pkt;
//——-DS3231时钟————————-//
DS3231 Clock;
bool Century=false;
bool h12;
bool PM;
byte ADay, AHour, AMinute, ASecond, ABits;
bool ADy, A12h, Apm;
int year, month, date, DoW,week , hour, minute, second,temperature;
//——————–eeprom 保存————————–//
byte steps[30]; //步数保存数 一个月 30天的数据
byte hearts[60]; //心率保存次数 一小时 60次
byte stepscount=0;
byte heartscount=0;
//————–显示——————–//
char dis1[16]={0},dis2[16]={0};
int TimerCount=0,TimerCount1=0;
byte dismode=0; //0:正常显示 1:查看保存数据
//——显示模式状态——————–//
byte displayState = 0; // 0:正常显示 1:心率异常提醒 2:休息提醒
SoftwareSerial dbgSerial( _rxpin, _txpin ); // 软串口,调试打印
// 函数声明
void CheckHeartRateAlert(int bpm);
void CheckRestReminder();
void ActivateVibration(int duration);
void ShowRestReminder();
void ShowHeartRateAlert(int bpm);
void GetStep()
{
sensors_event_t event;
accel.getEvent(&event);
if((Z_temp–event.acceleration.z>7)||(Z_temp–event.acceleration.z<–7))
{
stepout++;
Serial.print("step= ");
Serial.println(stepout);
Z_temp=event.acceleration.z;
}
}
void SavaData()
{
byte i=0;
if((hour==0)&&(minute==0)&&(second==0)) //一天中的零点
{
delay(1100);
for(i=0;i<30;i++) //现将数据从eeprom 读出
steps[i]= EEPROM.read(i);
for(i=0;i<29;i++)
{
steps[i]= steps[i+1];
}
steps[29]=stepout;
for(i=0;i<30;i++)
{
EEPROM.write(i, steps[i]); //将步数保存在对应当天的eeprom地址 0-29
}
Serial.print("sava steps…\\r\\n");
stepout=0;
}
if((second==0)) //一分钟时
{
delay(1100);
for(i=0;i<60;i++) //现将数据从eeprom 读出
hearts[i]= EEPROM.read(30+i);
for(i=0;i<59;i++)
{
hearts[i]= hearts[i+1];
}
hearts[59]=bpm_temp;
for(i=0;i<60;i++)
{
EEPROM.write(30+i, hearts[i]); //将心率保存在对应当天的eeprom地址 30-89
}
Serial.print("sava hearts…\\r\\n");
}
}
void ReadDS3231() //读取DS3231 参数
{
Wire.begin();
second=Clock.getSecond();
minute=Clock.getMinute();
hour=Clock.getHour(h12,PM);
week=Clock.getDoW();
date=Clock.getDate();
month=Clock.getMonth(Century);
year=Clock.getYear();
temperature=Clock.getTemperature();
}
void get_dis() //1602液晶上每一位上显示的数据
{
dis1[0]='2';
dis1[1]='0';
dis1[2]=0x30+year/10;
dis1[3]=0x30+year%10;
dis1[4]='-';
dis1[5]=0x30+month/10;
dis1[6]=0x30+month%10;
dis1[7]='-';
dis1[8]=0x30+date/10;
dis1[9]=0x30+date%10;
dis1[10]=' ';
dis1[11]=' ';
dis1[12]=' ';
switch(week)
{
case 2: {
dis1[13]='M';
dis1[14]='o';
dis1[15]='n';
}
break;
case 3: {
dis1[13]='T';
dis1[14]='u';
dis1[15]='e';
}
break;
case 4: {
dis1[13]='W';
dis1[14]='e';
dis1[15]='d';
}
break;
case 5: {
dis1[13]='T';
dis1[14]='h';
dis1[15]='u';
}
break;
case 6: {
dis1[13]='F';
dis1[14]='r';
dis1[15]='i';
}
break;
case 7: {
dis1[13]='S';
dis1[14]='a';
dis1[15]='t';
}
break;
case 1: {
dis1[13]='S';
dis1[14]='u';
dis1[15]='n';
}
break;
}
dis2[0]=' ';
dis2[1]=0x30+hour/10;
dis2[2]=0x30+hour%10;
dis2[3]=':';
dis2[4]=0x30+minute/10;
dis2[5]=0x30+minute%10;
dis2[6]=':';
dis2[7]=0x30+second/10;
dis2[8]=0x30+second%10;
dis2[9]=' ';
dis2[10]=' ';
dis2[11]=0x30+temperature/10;
dis2[12]=0x30+temperature%10;
dis2[13]=0xdf;
dis2[14]='C';
dis2[15]=' ';
}
void play() //1602显示完整时间
{
byte k;
get_dis();
LCD_SET_XY( 0,0);
for(k=0;k<16;k++)
LcdDataWrite(dis1[k]);
LCD_SET_XY( 0,1);
for(k=0;k<16;k++)
LcdDataWrite(dis2[k]);
}
void Jiaoshi(byte nian,byte yue,byte ri,byte zhou,byte shi,byte fen ,byte miao)
{
Wire.begin();
Clock.setSecond(miao);//Set the second
Clock.setMinute(fen);//Set the minute
Clock.setHour(shi); //Set the hour
Clock.setDoW(zhou); //Set the day of the week
Clock.setDate(ri); //Set the date of the month
Clock.setMonth(yue); //Set the month of the year
Clock.setYear(nian); //Set the year (Last two digits of the year)
}
// 检查心率是否异常并触发警报
void CheckHeartRateAlert(int bpm)
{
static unsigned long lastAlertTime = 0;
unsigned long currentTime = millis();
if (bpm < HEART_RATE_MIN || bpm > HEART_RATE_MAX)
{
if (currentTime – lastAlertTime > 5000) // 每5秒检查一次
{
heartRateAlertActive = true;
displayState = 1; // 切换到心率异常显示模式
ActivateVibration(1000); // 震动1秒
// 在串口输出警报信息
if (bpm < HEART_RATE_MIN)
{
Serial.println("ALERT: Heart rate too LOW!");
}
else
{
Serial.println("ALERT: Heart rate too HIGH!");
}
lastAlertTime = currentTime;
}
}
else
{
heartRateAlertActive = false;
if (displayState == 1) // 如果当前显示的是心率异常,且心率恢复正常
{
displayState = 0; // 返回正常显示
}
}
}
// 检查是否需要休息提醒
void CheckRestReminder()
{
static unsigned long lastCheckTime = 0;
unsigned long currentTime = millis();
// 每分钟检查一次是否需要休息提醒
if (currentTime – lastCheckTime > 60000)
{
// 检查是否到达提醒时间(假设从0分钟开始计时)
if (minute % REST_REMINDER_INTERVAL == 0 && second < 10)
{
if (!isResting)
{
isResting = true;
restStartTime = currentTime;
displayState = 2; // 切换到休息提醒显示模式
ActivateVibration(2000); // 震动2秒提醒
Serial.println("REMINDER: Time to take a break!");
}
}
// 检查休息时间是否结束
if (isResting && (currentTime – restStartTime > REST_DURATION * 60000))
{
isResting = false;
if (displayState == 2) // 如果当前显示的是休息提醒
{
displayState = 0; // 返回正常显示
}
}
lastCheckTime = currentTime;
}
}
// 激活震动
void ActivateVibration(int duration)
{
digitalWrite(VIBRATION_PIN, HIGH);
delay(duration);
digitalWrite(VIBRATION_PIN, LOW);
lastVibrationTime = millis();
}
// 显示休息提醒信息在LCD上
void ShowRestReminder()
{
LcdCommandWrite(0x01); // 清屏
// 第一行:显示"Take a Break!"
LCD_SET_XY(0, 0);
LcdDataWrite('T');
LcdDataWrite('a');
LcdDataWrite('k');
LcdDataWrite('e');
LcdDataWrite(' ');
LcdDataWrite('a');
LcdDataWrite(' ');
LcdDataWrite('B');
LcdDataWrite('r');
LcdDataWrite('e');
LcdDataWrite('a');
LcdDataWrite('k');
LcdDataWrite('!');
// 第二行:显示建议休息时长
LCD_SET_XY(0, 1);
LcdDataWrite('R');
LcdDataWrite('e');
LcdDataWrite('s');
LcdDataWrite('t');
LcdDataWrite(' ');
LcdDataWrite('f');
LcdDataWrite('o');
LcdDataWrite('r');
LcdDataWrite(' ');
LcdDataWrite(shu[REST_DURATION / 10]);
LcdDataWrite(shu[REST_DURATION % 10]);
LcdDataWrite(' ');
LcdDataWrite('m');
LcdDataWrite('i');
LcdDataWrite('n');
}
// 显示心率异常信息在LCD上
void ShowHeartRateAlert(int bpm)
{
LcdCommandWrite(0x01); // 清屏
// 第一行:显示"HEART RATE ALERT!"
LCD_SET_XY(0, 0);
LcdDataWrite('H');
LcdDataWrite('E');
LcdDataWrite('A');
LcdDataWrite('R');
LcdDataWrite('T');
LcdDataWrite(' ');
LcdDataWrite('R');
LcdDataWrite('A');
LcdDataWrite('T');
LcdDataWrite('E');
LcdDataWrite(' ');
LcdDataWrite('A');
LcdDataWrite('L');
LcdDataWrite('E');
LcdDataWrite('R');
LcdDataWrite('T');
LcdDataWrite('!');
// 第二行:显示当前心率值
LCD_SET_XY(0, 1);
LcdDataWrite('B');
LcdDataWrite('P');
LcdDataWrite('M');
LcdDataWrite(':');
LcdDataWrite(' ');
LcdDataWrite(shu[bpm / 100 % 10]);
LcdDataWrite(shu[bpm % 100 / 10 % 10]);
LcdDataWrite(shu[bpm % 10]);
// 根据心率高低显示不同信息
LCD_SET_XY(10, 1);
if (bpm < HEART_RATE_MIN)
{
LcdDataWrite('L');
LcdDataWrite('O');
LcdDataWrite('W');
}
else
{
LcdDataWrite('H');
LcdDataWrite('I');
LcdDataWrite('G');
LcdDataWrite('H');
}
}
void setup()
{
Wire.begin();
WIFI_UART.begin( _baudrate );
DBG_UART.begin( _baudrate );
pinMode(inputPin1, INPUT_PULLUP); //按键
pinMode(inputPin2, INPUT_PULLUP); //按键
pinMode(VIBRATION_PIN, OUTPUT); // 震动马达引脚设置为输出
pinMode(BUZZER_PIN, OUTPUT); // 蜂鸣器引脚设置为输出
// 初始化为低电平
digitalWrite(VIBRATION_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
delay(1000);
DBG_UART.println("Health Monitor with Vibration Alert Started!");
if(!accel.begin())
{
Serial.println("No valid sensor found");
}
// 配置PulseSensor对象
pulseSensor.analogInput(PulseWire);
pulseSensor.blinkOnPulse(LED13); //auto-magically blink Arduino's LED with heartbeat.
pulseSensor.setThreshold(Threshold);
// 确认PulseSensor对象已创建并开始检测信号
if (pulseSensor.begin()) {
Serial.println("PulseSensor Object created successfully!");
}
delay(200);
lcd_init();
lcd_dis();
// 测试震动功能
ActivateVibration(500);
delay(500);
Serial.println("System initialized. Starting monitoring…");
}
/*
* readEdpPkt
* 从串口缓存中读数据到接收缓存
*/
bool readEdpPkt(edp_pkt *p)
{
int tmp;
if ((tmp = Serial.readBytes(p->data + p->len, sizeof(p->data))) > 0 )
{
p->len += tmp;
}
return true;
}
void loop()
{
edp_pkt rcv_pkt;
char comdata [30];
byte datalen;
char num[10];
char datastr[20];
char nian[5],yue[5],ri[5],zhou[5],shi[5],fen[5],miao[5];
// 获取心率值
int myBPM = pulseSensor.getBeatsPerMinute();
if (pulseSensor.sawStartOfBeat()) {
Serial.println("♥ A HeartBeat Happened ! ");
Serial.print("BPM: ");
Serial.println(myBPM);
bpm_temp = myBPM;
// 检查心率是否异常
CheckHeartRateAlert(myBPM);
}
ReadDS3231();
SavaData();
// 检查是否需要休息提醒
CheckRestReminder();
// 处理按键输入
if(digitalRead(inputPin1)==LOW)
{
delay(100);
if(digitalRead(inputPin1)==LOW)
{
if(dismode==0)
{
dismode=1;
heartscount=1;
}
stepscount++;
if(stepscount>30)
stepscount=1;
steps[stepscount–1]=EEPROM.read(stepscount–1);
// 如果当前在警报状态,按下按钮可以清除警报
if (displayState == 1 || displayState == 2)
{
displayState = 0;
heartRateAlertActive = false;
isResting = false;
}
}
}
if(digitalRead(inputPin2)==LOW)
{
delay(100);
if(digitalRead(inputPin2)==LOW)
{
if(dismode==0)
{
dismode=1;
stepscount=1;
}
heartscount++;
if(heartscount>60)
heartscount=1;
hearts[heartscount–1]=EEPROM.read(30+heartscount–1);
// 如果当前在警报状态,按下按钮可以清除警报
if (displayState == 1 || displayState == 2)
{
displayState = 0;
heartRateAlertActive = false;
isResting = false;
}
}
}
if((digitalRead(inputPin2)==LOW)&&(digitalRead(inputPin1)==LOW))
{
while((digitalRead(inputPin2)==LOW)&&(digitalRead(inputPin1)==LOW));
dismode=0;
TimerCount1=60;
// 同时清除所有警报
displayState = 0;
heartRateAlertActive = false;
isResting = false;
}
// 处理串口数据
while (Serial.available())
{
readEdpPkt(&rcv_pkt);
Serial.println(rcv_pkt.len);
sscanf(rcv_pkt.data, "%[^:]:%[^:]:%[^:]:%[^:]:%[^:]:%[^:]:%[^:]:%[^:]", datastr, nian,yue,ri,zhou,shi,fen,miao);
if ((strchr(datastr,'t')!=NULL)&&(rcv_pkt.len==24))
{
Serial.print("Start jiaoshi…");
Serial.println(nian);
Serial.println(yue);
Serial.println(ri);
Serial.println(zhou);
Serial.println(shi);
Serial.println(fen);
Serial.println(miao);
Jiaoshi(atoi(nian),atoi(yue),atoi(ri),atoi(zhou),atoi(shi),atoi(fen),atoi(miao));
}
rcv_pkt.len=0;
}
GetStep();
// 根据显示状态更新LCD
if (displayState == 1)
{
// 显示心率异常提醒
ShowHeartRateAlert(myBPM);
// 如果心率异常,间歇性震动提醒
if (millis() – lastVibrationTime > vibrationInterval * 2)
{
ActivateVibration(300);
}
}
else if (displayState == 2)
{
// 显示休息提醒
ShowRestReminder();
// 休息提醒时,间歇性震动
if (millis() – lastVibrationTime > vibrationInterval * 3)
{
ActivateVibration(500);
}
}
else if(dismode==1)
{
lcd_dis();
dismode=2;
}
else if(dismode==0) //正常显示模式
{
if(TimerCount1==60)
{
LcdCommandWrite(0x01);
delay(200);
}
else if(TimerCount1==80)
{
lcd_dis();
TimerCount1=0;
}
if(TimerCount1<60)
{
LcdCommandWrite(0x80 + 5);
LcdDataWrite(shu[stepout / 100000%10]);
LcdDataWrite(shu[stepout %100000/ 10000 % 10]);
LcdDataWrite(shu[stepout % 10000/1000%10]);
LcdDataWrite(shu[stepout % 1000 / 100%10]);
LcdDataWrite(shu[stepout % 100/10%10]);
LcdDataWrite(shu[stepout % 10]);
LcdCommandWrite(0x80 + 0x40 + 6);
LcdDataWrite(shu[myBPM / 100%10]);
LcdDataWrite(shu[myBPM % 100/10%10]);
LcdDataWrite(shu[myBPM % 10]);
}
else if(TimerCount1<81)
{
play();
}
TimerCount1++;
delay(100);
}
else if(dismode==2)
{
LcdCommandWrite(0x80 + 5);
LcdDataWrite(shu[steps[stepscount–1] / 100000%10]);
LcdDataWrite(shu[steps[stepscount–1] %100000/ 10000 % 10]);
LcdDataWrite(shu[steps[stepscount–1] % 10000/1000%10]);
LcdDataWrite(shu[steps[stepscount–1] % 1000 / 100%10]);
LcdDataWrite(shu[steps[stepscount–1] % 100/10%10]);
LcdDataWrite(shu[steps[stepscount–1] % 10]);
LcdCommandWrite(0x80 + 14);
LcdDataWrite(shu[stepscount / 10]);
LcdDataWrite(shu[stepscount % 10]);
LcdCommandWrite(0x80 + 0x40 + 6);
LcdDataWrite(shu[hearts[heartscount–1] / 100%10]);
LcdDataWrite(shu[hearts[heartscount–1] % 100/10%10]);
LcdDataWrite(shu[hearts[heartscount–1] % 10]);
LcdCommandWrite(0x80 + 0x40 + 14);
LcdDataWrite(shu[heartscount / 10]);
LcdDataWrite(shu[heartscount % 10]);
}
}
## 2.读入数据
代码如下(示例):
```c
data = pd.read_csv(
'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())
用Arduino进行创意开发
三. 项目存在问题
(1)Arduino 开机阶段:设备上电启动时,LCD1602显示屏输出存在乱码现象[8],乱码字符及出现频率数据。 (2)心率传感器数据采集:对心率传感器进行摇晃,采集的心率数值存在异常偏大的情况,异常值的具体范围为250~270。 (3)设备运行稳定性:设备开机正常运行约 2 分钟后,出现死机故障,死机时各模块的工作状态正常,不会伴随数据流失。 (4)硬件连接可靠性:杜邦线连接I2c协议的接口部位存在接触不灵的问题,会出现计步传感器无法正常上传数据。
未来优化方向
技术优化提升数据采集精度:优化心率传感器的信号滤波算法[7],采用更先进的数字滤波技术(如卡尔曼滤波),进一步降低环境光和运动干扰,提高心率测量精度;优化ADXL345倾角传感器的计步算法[4],结合机器学习算法识别不同运动模式[13],减少误计步和漏计步现象[5]。 降低系统功耗:选用更低功耗的传感器和电子元件[11],优化硬件电路设计,减少静态功耗;优化软件程序,采用休眠唤醒机制[11],在系统无操作或低负载时,使主控制器和部分传感器进入休眠状态,降低动态功耗,进一步延长电池续航时间。 提高系统集成度:当前系统采用面包板搭建电路,体积较大,稳定性受限于面包板连接。未来可设计专用的PCB电路板,将所有模块集成到一块电路板上,减小系统体积,提高系统的稳定性和可靠性,同时便于批量生产。 优化显示效果:将LCD1602液晶显示屏替换为OLED显示屏,OLED显示屏具有更高的对比度、更宽的视角、更低的功耗和更薄的厚度,能够提升显示效果,同时进一步减小系统体积。 功能扩展增加无线通信功能:添加蓝牙或WiFi模块,实现系统与手机APP的数据通信[12]。用户可通过手机APP查看更详细的历史数据(如心率变化曲线、步数趋势图),进行数据备份和分享;同时可通过手机APP对系统进行远程设置(如调整心率正常范围、设置休息提醒间隔等)。 扩展数据存储容量:当前系统依赖Arduino UNO内置的EEPROM存储数据,存储容量有限。未来可添加SD卡模块,扩展数据存储容量,支持存储更长时间的历史数据,同时便于数据的导出和分析。 增加个性化健康分析功能:基于采集的心率和步数数据,开发简单的健康分析算法,为用户提供个性化的健康建议,如运动强度评估、休息时间建议等;添加体重、血压等数据的手动输入功能,构建更全面的个人健康档案。 优化交互功能:增加语音交互模块,支持用户通过语音指令查询数据、设置系统;优化LCD显示界面,增加图形化显示元素(如心率状态指示灯、运动进度条等),提升用户体验。 应用场景扩展当前系统主要面向普通个人用户,未来可扩展应用场景,扩大系统的应用范围:健身场所应用:针对健身房、运动场馆等场景,开发多用户版本的系统,支持同时监测多名用户的运动数据,教练可通过系统实时了解学员的运动状态,进行精准指导。 老年健康监测场景:针对老年人的健康管理需求,优化系统的操作界面和提醒功能,增加紧急呼叫功能,当检测到严重心率异常时,自动向家属或医护人员发送报警信息;同时简化佩戴方式,提高系统的易用性。 学生体育教学场景:在学校体育教学中,应用该系统监测学生的运动强度和健康状态,帮助体育老师合理安排教学内容,避免学生运动过量;同时记录学生的体育锻炼数据,为体育成绩的评估提供参考。 职场健康管理场景:针对办公室职员,开发桌面版系统,结合久坐提醒功能,帮助职员合理安排工作和休息时间,预防久坐带来的健康问题;同时记录职员的日常活动数据,为企业开展员工健康管理活动提供数据支持。
总结
本项目成功构建了以 Arduino UNO 为核心的心率计步检测时钟显示系统,全面达成预设研究目标。硬件层面完成各模块的选型与稳定连接,软件层面实现了数据采集、处理、显示、存储、交互及提醒全流程功能,有效解决了传统健康监测设备的诸多行业痛点。通过多模态异常检测与主动交互技术的创新应用,系统在数据精准性与用户体验上实现突破,且成本经济、操作简便,适配普通用户的消费与使用需求[10]。 项目实施过程中,团队成员分工明确、协同高效,完成了各模块的软硬件开发与系统集成调试,形成了完整的技术文档。同时,项目也暴露出开机 LCD 乱码、心率数据异常、设备稳定性不足等问题。未来可通过低功耗元件、设计专用 PCB 板等方式提升系统性能,进一步扩展无线通信、个性化健康分析等功能,并拓展至健身场所、老年健康监测等多元场景,增强系统的实用性与应用范围,为用户提供更全面的智能健康管理服务。



