欢迎光临
我们一直在努力

【基于Arduino的心率计步检测时钟显示设计】

基于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_tempevent.acceleration.z>7)||(Z_tempevent.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[stepscount1]=EEPROM.read(stepscount1);

    // 如果当前在警报状态,按下按钮可以清除警报
    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[heartscount1]=EEPROM.read(30+heartscount1);

    // 如果当前在警报状态,按下按钮可以清除警报
    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[stepscount1] / 100000%10]);
    LcdDataWrite(shu[steps[stepscount1] %100000/ 10000 % 10]);
    LcdDataWrite(shu[steps[stepscount1] % 10000/1000%10]);
    LcdDataWrite(shu[steps[stepscount1] % 1000 / 100%10]);
    LcdDataWrite(shu[steps[stepscount1] % 100/10%10]);
    LcdDataWrite(shu[steps[stepscount1] % 10]);

    LcdCommandWrite(0x80 + 14);
    LcdDataWrite(shu[stepscount / 10]);
    LcdDataWrite(shu[stepscount % 10]);

    LcdCommandWrite(0x80 + 0x40 + 6);
    LcdDataWrite(shu[hearts[heartscount1] / 100%10]);
    LcdDataWrite(shu[hearts[heartscount1] % 100/10%10]);
    LcdDataWrite(shu[hearts[heartscount1] % 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 板等方式提升系统性能,进一步扩展无线通信、个性化健康分析等功能,并拓展至健身场所、老年健康监测等多元场景,增强系统的实用性与应用范围,为用户提供更全面的智能健康管理服务。

    赞(0)
    未经允许不得转载:171主机测评 » 【基于Arduino的心率计步检测时钟显示设计】
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址