什么软件可以测试手长,心率检测专家-可以让你通过按压手指检测出心率的健康app...

心率检测专家app是一款无需登录的Android应用,通过手机摄像头和闪光灯即可检测心率,并提供健康分析、高血压患病率和心肺功能等数据。适合常需检测心率的用户,虽不及专业医疗设备精准,但仍能为健康管理带来便利。该应用无任何付费内容,用户可直接下载使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

心率检测专家是一款健康app,可以让你通过按压手指检测出心率。心率检测专家app中提供的心率检测功能,可以让你通过手指按压手机摄像头闪光灯检测实时心率,支持选择测量时的状态检测,检测出心率后还可以看到健康分析,以及你的高血压患病率和心肺功能耐力及肥胖指数等。适用平台为Android,无需登录,下载打开即可使用。

72667d3325c362434c611b606ddae38b.png

功能特点

1.提供心率检测功能;

2.通过手指按压摄像头得出实时心率数据;

3.可以查看多种数据的健康分析;

4.适用于有经常检测心率需求的人群使用;

5.可以帮助你检测心率查看健康报告。

付费情况

心率检测专家app中没有付费情况。

应用功能介绍

在心率检测专家的主界面中,点击中部功能键就可以开始检测心率,检测心率时,你需要将一根手指头,同时遮住闪光灯与摄像头二十秒,且保持静止直至结束,心率检测专家才会检测出你的心率,检测后你需要选择测量状态,选择对应的测量状态后,你就可以得到检测的准确心率数据和健康分析。

85672a1f6e6215fb64971e6d9090b699.png

d7e1125f71caa0e1f89763111063b3c6.png

bddba54f954d52a9405f870cd194d3a1.png

373456266231934739fcbf9f92a7d4d3.png

在选择测量状态好后,app就会生成健康报告查看,可以让你看到心率数据的健康水平,通过右滑,你还可以看到高血压发病率和心肺功能及肥胖指数等数据,能够为你身体健康带来一定的便利。

a4bef73c93503f78ac621e7df8f6738f.png

56e9d667620790d79530c215e63ec785.png

小编总结

综上所述,心率检测专家app对有需求检测心率的人来说是有着一定帮助的,可以帮助检测出心率数据和健康分析,给你带来身体健康的便利,但需要知道的是,心率检测专家是比不了官方的医疗仪器的。感兴趣的朋友可以在下方官网链接中下载体验。

我的uniapp的黑暗逻辑代码如下: <template> <view class="container"> <!-- 摄像头预览 --> <camera v-if="hasCameraPermission" device-position="back" :flash="flashStatus" class="camera" @ready="handleCameraReady"> </camera> <view v-else class="permission-error"> <text>请允许访问摄像头以使用心率量功能</text> <button @click="requestCameraPermission" open-type="getAuthorize">授权摄像头权限</button> </view> <!-- 环境光状态 --> <view class="status-info"> <view class="light-status"> <text class="status-label">环境光:</text> <text :class="ambientLight > 150 ? 'warning' : ambientLight > 50 ? 'good' : 'warning'"> {{ ambientLight > 150 ? '过强' : ambientLight > 50 ? '适中' : '偏暗' }} </text> </view> </view> <!-- 心率显示区域 --> <view class="heart-rate-display"> <view v-if="!isMeasuring && heartRate > 0"> <text>您的心率是</text> <text class="heart-rate-value">{{ heartRate }}</text> <text>BPM</text> <view class="signal-quality"> 信号质量: {{ signalQuality }}% <progress :percent="signalQuality" stroke-width="4" active-color="#4CAF50"/> </view> <view class="measurement-conditions"> <text>量环境: {{ ambientLight > 150 ? '光线过强' : ambientLight > 50 ? '光线适中' : '光线偏暗' }}</text> </view> </view> <view v-if="isMeasuring" class="measuring-status"> <text>量中...</text> <text>{{ statusText }}</text> <view class="finger-position-hint"> <text>请保持手指稳定覆盖摄像头</text> <text>不要按压太用力</text> </view> <view class="environment-hint" v-if="ambientLight !== 0"> <text v-if="ambientLight > 150">光线过强,建议遮挡部分环境光</text> <text v-else-if="ambientLight < 50">光线偏暗,已自动开启补光</text> <text v-else>光线环境良好,请保持稳定</text> </view> </view> <view v-if="!isMeasuring && heartRate === 0" class="ready-status"> <text>准备量</text> <text>{{ statusText || '请将手指轻轻覆盖摄像头和闪光灯' }}</text> </view> </view> <!-- 量进度 --> <progress :percent="progress" stroke-width="6" active-color="#FF5252" background-color="#f5f5f5" :active="true" :show-info="true" style="margin: 20px 0;" /> <!-- 控制按钮 --> <view class="button-group"> <button type="primary" @click="startMeasurement" :disabled="isMeasuring" style="background-color: #FF5252; border: none;"> 开始量 </button> <button type="warn" @click="stopMeasurement" :disabled="!isMeasuring"> 停止量 </button> <button type="default" @click="toggleFlash" :class="isFlashAutoControlled ? 'auto-control' : 'manual-control'"> <view class="flash-text">{{ flashStatus === 'torch' ? '关闭' : '打开' }}</view> <text class="control-mode">{{ isFlashAutoControlled ? '(自动)' : '(动)' }}</text> </button> </view> <!-- 使用说明 --> <view class="instructions"> <text>使用方法:</text> <text>1.手指轻轻覆盖在后置摄像头和闪光灯上</text> <text>2. 点击"开始量"按钮</text> <text>3. 保持手指稳定约30秒</text> </view> <!-- 免责声明 --> <view class="disclaimer"> <text class="disclaimer-title">免责声明</text> <text>1. 本小程序仅供娱乐和参考使用,不作为任何医疗诊断依据</text> <text>2. 量结果可能存在误差,不能替代专业医疗设备</text> <text>3. 如果您对自己的健康状况有疑虑,请及时就医</text> <text>4. 本小程序不会收集、存储任何用户信息</text> <text>5. 如将本程序的测试结果作为专业依据所造成的任何问题和损失,本程序不承担任何责任,包括法律责任</text> <text>6. 使用本小程序即表示您同意以上声明内容</text> </view> </view> </template> <script setup> import { ref, reactive, onMounted, onUnmounted } from 'vue' import { useCamera } from './composables/useCamera' import { useHeartRate } from './composables/useHeartRate' // 主逻辑状态 const { hasCameraPermission, isCameraReady, cameraContext, requestCameraPermission //onCameraReady } = useCamera() const { isMeasuring, heartRate, progress, statusText, flashStatus, ambientLight, signalQuality, isFlashAutoControlled, startMeasurement, stopMeasurement, toggleFlash } = useHeartRate(cameraContext) // 处理摄像头就绪事件 const handleCameraReady = () => { //onCameraReady(); isCameraReady.value = true } 我导入的useCamera是相机处理的相关逻辑,我已确认没有问题,我的心率量及处理逻辑useHeartRate.js的内容如下: import { ref, reactive, onUnmounted, toRefs } from 'vue' import * as SignalProcessor from './signalProcessor' import * as Validator from './validator' export function useHeartRate(cameraContext) { // 状态管理 const state = reactive({ isMeasuring: false, heartRate: 0, progress: 0, flashStatus: 'off', signalQuality: 0, statusText: '将手指轻轻覆盖摄像头和闪光灯', isFlashAutoControlled: false, flashStabilizationPeriod: false, ambientLight: 0, lastFlashToggleTime: 0, cameraSettings: { iso: 100, brightness: 0, exposure: 0 } }) // 内部状态 let frameListener = null let progressInterval = null let signalData = [] let consecutiveNoFingerFrames = 0 let consecutiveFingerFrames = 0 let lastFrameTime = 0 const fps = 30 // 清理资源 const cleanup = () => { if (frameListener) { frameListener.stop() frameListener = null } if (progressInterval) { clearInterval(progressInterval) progressInterval = null } signalData = [] consecutiveNoFingerFrames = 0 consecutiveFingerFrames = 0 } // 开始量 const startMeasurement = () => { if (state.isMeasuring) return // 确保摄像头已准备好 if (cameraContext.value===null) { console.error('摄像头上下文未初始化') state.statusText = '摄像头未准备好,请重试' //state.statusText = '请等待摄像头初始化完成后再开始量'; return } cleanup() Object.assign(state, { isMeasuring: true, heartRate: 0, progress: 0, signalQuality: 0, isFlashAutoControlled: true, lastFlashToggleTime: Date.now(), statusText: '正在检测环境光...', flashStabilizationPeriod: true }) signalData = [] try { // 创建帧监听器 frameListener = cameraContext.value.onCameraFrame((frame) => { processFrame(frame) }) frameListener.start() } catch (error) { console.error('启动帧监听失败:', error) state.statusText = '启动量失败,请重试' cleanup() return } // 模拟进度更新 progressInterval = setInterval(() => { if (state.progress < 100) { state.progress += 1 } else { clearInterval(progressInterval) calculateHeartRate() } }, 100) } // 停止量 const stopMeasurement = () => { if (!state.isMeasuring) return cleanup() Object.assign(state, { isMeasuring: false, flashStatus: 'off' }) } // 处理帧数据(核心逻辑) const processFrame = (frame) => { const now = Date.now() if (now - lastFrameTime < 1000 / fps) return lastFrameTime = now // 1. 检测环境光 const ambientLight = detectAmbientLight(frame) // 2. 调整相机设置 const { settings, needFlash } = adjustCameraSettings(ambientLight) state.ambientLight = ambientLight state.cameraSettings = settings // 3. 检测手指位置 const { isFingerPresent } = detectFinger(frame) if (!isFingerPresent) { state.statusText = '请将手指完全覆盖摄像头和闪光灯' consecutiveNoFingerFrames = consecutiveNoFingerFrames ? consecutiveNoFingerFrames + 1 : 1 if (consecutiveNoFingerFrames >= 15) { stopMeasurement() state.progress = 0 state.statusText = '未检测手指,请重新量' return } return } else { consecutiveFingerFrames = consecutiveFingerFrames ? consecutiveFingerFrames + 1 : 1 if (consecutiveFingerFrames >= 3) { consecutiveNoFingerFrames = 0 consecutiveFingerFrames = 0 } } // 4. 处理信号数据 const redValue = getFrameRedValue(frame) signalData.push({ time: now, value: redValue, ambientLight }) // 5. 保持数据量 if (signalData.length > 300) { signalData.shift() } // 6. 更新状态文本 updateStatusText() } // 检测环境光强度 const detectAmbientLight = (frame) => { const data = new Uint8Array(frame.data) let totalBrightness = 0 const samplingStep = 20 // 采样步,提高性能 let samplingCount = 0 // 采样图像中心区域的亮度 const centerX = Math.floor(frame.width / 2) const centerY = Math.floor(frame.height / 2) const sampleRadius = Math.min(frame.width, frame.height) * 0.3 for (let y = centerY - sampleRadius; y <= centerY + sampleRadius; y += samplingStep) { for (let x = centerX - sampleRadius; x <= centerX + sampleRadius; x += samplingStep) { if (x >= 0 && x < frame.width && y >= 0 && y < frame.height) { const index = (y * frame.width + x) * 4 // 计算YUV亮度: Y = 0.299R + 0.587G + 0.114B const brightness = (data[index] * 0.299 + data[index + 1] * 0.587 + data[index + 2] * 0.114) totalBrightness += brightness samplingCount++ } } } return samplingCount > 0 ? totalBrightness / samplingCount : 0 } // 动态调整相机参数 const adjustCameraSettings = (ambientLight) => { const settings = { ...state.cameraSettings } let needFlash = false // 调整环境光阈值,避免频繁切换 if (ambientLight < 40) { // 降低暗光阈值 // 低光环境 settings.iso = 400 settings.exposure = 1.5 // 降低曝光调整幅度 settings.brightness = 0.3 // 降低亮度调整幅度 needFlash = true } else if (ambientLight < 100) { // 正常光环境 settings.iso = 200 settings.exposure = 1 settings.brightness = 0 needFlash = false } else { // 强光环境 settings.iso = 100 settings.exposure = 0 settings.brightness = -0.3 // 降低亮度调整幅度 needFlash = false } // 如果不是自动控制模式,不建议开启闪光灯 if (!state.isFlashAutoControlled) { needFlash = false } return { settings, needFlash } } // 检测手指是否正确放置 const detectFinger = (frame) => { const data = new Uint8Array(frame.data) const width = frame.width const height = frame.height const centerRegionSize = 0.6 // 检测中心60%的区域 let totalRed = 0 let totalGreen = 0 let totalBlue = 0 let pixelCount = 0 // 计算中心区域的边界 const startX = Math.floor(width * (1 - centerRegionSize) / 2) const endX = Math.floor(width * (1 + centerRegionSize) / 2) const startY = Math.floor(height * (1 - centerRegionSize) / 2) const endY = Math.floor(height * (1 + centerRegionSize) / 2) // 分析中心区域的RGB通道值 for (let y = startY; y < endY; y += 2) { // 隔行采样提高性能 for (let x = startX; x < endX; x += 2) { // 隔列采样提高性能 const i = (y * width + x) * 4 totalRed += data[i] // 红色通道值 totalGreen += data[i + 1] // 绿色通道值 totalBlue += data[i + 2] // 蓝色通道值 pixelCount++ } } const avgRed = totalRed / pixelCount const avgGreen = totalGreen / pixelCount const avgBlue = totalBlue / pixelCount // 计算红色通道占比 - 手指覆盖时红色通道值应明显高于其他通道 const redRatio = avgRed / (avgGreen + avgBlue + 1) // 防止除零 // 计算整体亮度 const brightness = (avgRed + avgGreen + avgBlue) / 3 // 判断手指是否存在的多重条件 const isFingerPresent = (redRatio > 1.2) && (brightness > 30 && brightness < 220) && (avgRed > 60) return { isFingerPresent } } // 计算红色通道值(优化版) const getFrameRedValue = (frame) => { if (!frame.data || frame.width <= 0 || frame.height <= 0) return 0 const data = new Uint8Array(frame.data) let redSum = 0 let pixelCount = 0 const sampleArea = Math.min(frame.width, frame.height) * 0.3 // 采样区域大小 // 采样中心区域,避免边缘噪声 const centerX = Math.floor(frame.width / 2) const centerY = Math.floor(frame.height / 2) // 使用圆形采样区域,减少手指边缘影响 for (let y = centerY - sampleArea; y <= centerY + sampleArea; y++) { for (let x = centerX - sampleArea; x <= centerX + sampleArea; x++) { // 检查是否在圆形区域内 const distance = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2)) if (distance <= sampleArea && x >= 0 && x < frame.width && y >= 0 && y < frame.height) { const index = (y * frame.width + x) * 4 // 增加红色通道权重,减少其他通道干扰 redSum += data[index] * 0.8 - data[index+1] * 0.1 - data[index+2] * 0.1 pixelCount++ } } } return pixelCount > 0 ? redSum / pixelCount : 0 } // 更新状态文本 const updateStatusText = () => { if (signalData.length > 30) { const recentData = signalData.slice(-30) const variance = SignalProcessor.calculateSignalVariance(recentData.map(d => d.value)) // 更新状态文本 if (variance < 0.1) { state.statusText = '请稍微增加按压力度' } else if (variance > 2.0) { state.statusText = '请稍微减小按压力度' } else { state.statusText = '信号良好,请保持稳定' } } } // 计算心率 const calculateHeartRate = () => { if (signalData.length < fps * 5) { state.heartRate = 0 state.progress = 100 state.statusText = "量时间不足,请保持手指稳定" stopMeasurement() return } // 简化版心率计算 state.heartRate = Math.floor(Math.random() * 40) + 60 state.signalQuality = Math.floor(Math.random() * 40) + 60 state.statusText = "量完成" } // 闪光灯控制 const toggleFlash = () => { const newStatus = state.flashStatus === 'torch' ? 'off' : 'torch' const now = Date.now() if (now - state.lastFlashToggleTime > 1000) { state.flashStatus = newStatus state.isFlashAutoControlled = false state.lastFlashToggleTime = now if (newStatus === 'torch') { setTimeout(() => { state.isFlashAutoControlled = true }, 3000) } } } // 组件卸载时清理 onUnmounted(() => { cleanup() }) return { ...toRefs(state), startMeasurement, stopMeasurement, toggleFlash } } // 导工具函数 export * from './signalProcessor' export * from './validator' 我在真机上调试的时候,授权了相机权限,点击开始量按钮后就现了问题,控制台信息如下: WAServiceMainContext.js:1 MiniProgramError (void 0) is not a function TypeError: (void 0) is not a function at updateStatusText (https://usr/app-service.js:8946:15) at processFrame (https://usr/app-service.js:8842:5) at Function.<anonymous> (https://usr/app-service.js:8778:9) at <CameraContext.onCameraFrame callback function> at https://lib/WAServiceMainContext.js:1:432607 at Ht.o (https://lib/WAServiceMainContext.js:1:1749601) at Ht.emit (https://lib/WAServiceMainContext.js:1:986787) at Object.emit (https://lib/WAServiceMainContext.js:1:992138) at https://lib/WAServiceMainContext.js:1:993357 at https://lib/WAServiceMainContext.js:1:983327 在量过程中检测手指功能正常,根据环境光强度控制电筒功能不正常,我只能提供这些信息了,请你帮我找问题所在并解决
06-05
<think>我们使用Arduino UNO R3开发板,连接SH1106 OLED(I2C地址0x3C)和MAX30102血氧传感器(I2C地址0x57)。 注意:MAX30102需要3.3V供电,所以VIN接3.3V,GND接GND。SDA和SCL分别接开发板上的SDA和SCL(即A4和A5,但在UNO上右侧有专门的SDA/SCL引脚,实际与A4/A5是内部连通的)。 我们将使用以下库: - MAX3010x_Sensor_Library:用于MAX30102传感器(由SparkFun提供) - U8g2:用于OLED显示(支持SH1106) 由于Arduino UNO内存有限(2KB SRAM,32KB Flash),我们需要特别注意内存使用。避免使用大型缓冲区,尽量使用局部变量,并尽可能使用F()宏将字符串保存在Flash中。 设计功能: 1. 初始化MAX30102传感器,配置为血氧监模式。 2. 初始化SH1106 OLED显示屏。 3. 实时读取血氧饱和度(SpO2)和心率(HR),并在OLED上显示。 4. 为了减少内存占用,我们将使用U8g2的“内存较少”的模式(例如,使用U8g2的页面缓冲模式,而不是全屏缓冲)。 步骤: a. 安装库:在Arduino IDE中,通过库管理器安装“SparkFun MAX3010x Pulse and Proximity Sensor Library”和“U8g2”库。 b. 连接硬件: MAX30102: VIN -> 3.3V GND -> GND SDA -> SDA (A4) SCL -> SCL (A5) SH1106 OLED: VCC -> 5V (注意:有些SH1106模块是5V兼容的,如果模块支持3.3V-5V,则接5V;如果只支持3.3V,则接3.3V。注意模块说明,一般接5V没问题) GND -> GND SCL -> SCL (A5) SDA -> SDA (A4) c. 注意:两个设备都是I2C,所以可以并联到同一个I2C总线上。 代码结构: - 包含必要的库 - 定义MAX30102对象和U8g2对象 - 在setup()中初始化串口(用于调试)、MAX30102和OLED - 在loop()中: 读取传感器数据(血氧和心率) 在OLED上显示 添加适当的延迟 注意:MAX30102的读取需要一定时间,且血氧计算需要多个样本。我们使用库中的示例算法。 由于内存限制,我们使用U8g2的页面缓冲模式(U8G2_R0表示不旋转,使用I2C接口,具体根据你的OLED型号选择)。SH1106的驱动在U8g2中为:U8G2_SH1106_128X64_NONAME_F_HW_I2C(全屏缓冲)或U8G2_SH1106_128X64_NONAME_1_HW_I2C(页面缓冲)。为了节省内存,我们使用页面缓冲(1_HW_I2C),然后使用firstPage()和nextPage()来逐页绘制。 代码示例:</think># 智能血氧监仪设计 ## 系统设计与实现方案 根据您提供的硬件(UNO R3、SH1106 OLED、MAX30102)和库要求(MAX3010x_Sensor_Library、U8g2),我将设计一个完整的血氧监系统,包含数据采集、处理和显示功能。 ### 硬件连接示意图 ``` MAX30102传感器 ├── VIN → UNO 3.3V ├── GND → UNO GND ├── SDA → UNO SDA (A4) └── SCL → UNO SCL (A5) SH1106 OLED显示屏 ├── VCC → UNO 5V ├── GND → UNO GND ├── SDA → UNO SDA (A4) └── SCL → UNO SCL (A5) ``` ### 完整代码实现 ```cpp #include <Wire.h> #include <U8g2lib.h> #include "MAX30105.h" #include "spo2_algorithm.h" // 初始化OLED对象 (SH1106, 128x64) U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); // 初始化MAX30102传感器 MAX30105 particleSensor; // 血氧计算参数 #define MAX_BRIGHTNESS 255 const byte RATE_SIZE = 4; // 平均心率值的样本数 byte rates[RATE_SIZE]; // 心率数组 byte rateSpot = 0; long lastBeat = 0; // 最后检测到心跳的时间 // 血氧饱和度计算变量 float beatsPerMinute; int beatAvg; int spo2; // 传感器数据缓冲区 #define BUFFER_SIZE 100 uint32_t irBuffer[BUFFER_SIZE]; // 红外LED传感器数据 uint32_t redBuffer[BUFFER_SIZE]; // 红光LED传感器数据 // 系统状态变量 enum SystemState { INIT, DETECTING, MEASURING, READY }; SystemState currentState = INIT; unsigned long stateChangeTime = 0; const unsigned long FINGER_DETECT_TIMEOUT = 10000; // 10秒手指检测超时 void setup() { Serial.begin(9600); // 初始化OLED u8g2.begin(); u8g2.setFont(u8g2_font_ncenB08_tr); // 设置字体 // 显示启动画面 displayStartupScreen(); delay(2000); // 初始化MAX30102 if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) { displayError("Sensor not found"); while (1); // 卡死 } // 配置传感器 byte ledBrightness = 60; // 0-255 byte sampleAverage = 4; // 采样平均次数 byte ledMode = 2; // 红光+红外模式 int sampleRate = 100; // 采样率Hz int pulseWidth = 411; // ADC分辨率 int adcRange = 4096; // ADC范围 particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); // 启用自动增益控制 particleSensor.enableDIETEMPRDY(); // 初始化缓冲区 for (byte i = 0; i < BUFFER_SIZE; i++) { redBuffer[i] = 0; irBuffer[i] = 0; } currentState = DETECTING; stateChangeTime = millis(); } void loop() { switch (currentState) { case DETECTING: handleDetectingState(); break; case MEASURING: handleMeasuringState(); break; case READY: handleReadyState(); break; } // 每100ms刷新一次显示 static unsigned long lastDisplayUpdate = 0; if (millis() - lastDisplayUpdate > 100) { updateDisplay(); lastDisplayUpdate = millis(); } } // 状态处理函数 void handleDetectingState() { // 检查是否有手指放在传感器上 long irValue = particleSensor.getIR(); if (irValue > 50000) { // 检测手指 currentState = MEASURING; stateChangeTime = millis(); displayMessage("Measuring..."); } else if (millis() - stateChangeTime > FINGER_DETECT_TIMEOUT) { // 超时未检测手指 displayMessage("Place finger"); } } void handleMeasuringState() { // 采集传感器数据 for (byte i = 0; i < BUFFER_SIZE; i++) { while (!particleSensor.available()) particleSensor.check(); // 检查新数据 redBuffer[i] = particleSensor.getRed(); irBuffer[i] = particleSensor.getIR(); particleSensor.nextSample(); // 准备下一个样本 // 计算心率 long currentTime = millis(); if (checkForBeat(irBuffer[i]) == true) { long delta = currentTime - lastBeat; lastBeat = currentTime; beatsPerMinute = 60 / (delta / 1000.0); if (beatsPerMinute < 255 && beatsPerMinute > 20) { rates[rateSpot++] = (byte)beatsPerMinute; rateSpot %= RATE_SIZE; // 计算平均心率 beatAvg = 0; for (byte x = 0; x < RATE_SIZE; x++) beatAvg += rates[x]; beatAvg /= RATE_SIZE; } } // 计算血氧饱和度 maxim_heart_rate_and_oxygen_saturation(irBuffer, BUFFER_SIZE, redBuffer, &spo2); // 检查量是否完成 if (millis() - stateChangeTime > 5000) { // 5秒量时间 currentState = READY; stateChangeTime = millis(); } } } void handleReadyState() { // 显示量结果 if (millis() - stateChangeTime > 10000) { // 10秒后返回检测状态 currentState = DETECTING; stateChangeTime = millis(); } } // 显示函数 void displayStartupScreen() { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.drawStr(15, 30, "Pulse Oximeter"); u8g2.setFont(u8g2_font_ncenB08_tr); u8g2.drawStr(30, 50, "Initializing..."); u8g2.sendBuffer(); } void displayError(const char* errorMsg) { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB10_tr); u8g2.drawStr(10, 30, "ERROR:"); u8g2.drawStr(10, 50, errorMsg); u8g2.sendBuffer(); } void displayMessage(const char* msg) { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB10_tr); u8g2.drawStr(10, 30, msg); u8g2.sendBuffer(); } void updateDisplay() { u8g2.clearBuffer(); // 显示状态标题 u8g2.setFont(u8g2_font_ncenB10_tr); u8g2.drawStr(5, 15, "Oximeter Status"); // 绘制分隔线 u8g2.drawHLine(0, 20, 128); // 根据状态显示不同内容 switch (currentState) { case DETECTING: u8g2.setFont(u8g2_font_ncenB08_tr); u8g2.drawStr(10, 40, "Place finger on"); u8g2.drawStr(10, 55, "sensor"); break; case MEASURING: u8g2.setFont(u8g2_font_ncenB08_tr); u8g2.drawStr(10, 40, "Measuring..."); break; case READY: // 显示血氧和心率 u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.setCursor(10, 40); u8g2.print("SpO2: "); u8g2.print(spo2); u8g2.print("%"); u8g2.setCursor(10, 60); u8g2.print("BPM: "); u8g2.print(beatAvg); // 添加血氧水平指示 if (spo2 >= 95) { u8g2.drawBox(100, 25, 5, 15); // 绿色高血氧 u8g2.drawBox(100, 40, 5, 15); } else if (spo2 >= 90) { u8g2.drawBox(100, 40, 5, 15); // 黄色中等血氧 } else { u8g2.drawBox(100, 55, 5, 15); // 红色低血氧 } break; } u8g2.sendBuffer(); } ``` ## 操作步骤指南 ### 1. 硬件连接 1. 将MAX30102的VIN连接到UNO的3.3V 2. 将MAX30102的GND连接到UNO的GND 3. 将MAX30102的SDA连接到UNO的SDA引脚 4. 将MAX30102的SCL连接到UNO的SCL引脚 5. 将SH1106 OLED的VCC连接到UNO的5V 6. 将SH1106 OLED的GND连接到UNO的GND 7. 将SH1106 OLED的SDA连接到UNO的SDA引脚 8. 将SH1106 OLED的SCL连接到UNO的SCL引脚 ### 2. 软件安装 1. 安装Arduino IDE (1.8.x或更高版本) 2. 安装库: - 在Arduino IDE中:工具 → 管理库 → 搜索"U8g2" → 安装 - 在Arduino IDE中:工具 → 管理库 → 搜索"MAX3010x" → 安装SparkFun库 3. 复制上述代码到新的Arduino项目 4. 选择开发板:工具 → 开发板 → Arduino AVR Boards → Arduino Uno 5. 选择端口:工具 → 端口 → 选择正确的COM端口 6. 上传代码到开发板 ### 3. 使用说明 1. 系统启动后会显示初始化界面 2.手指轻轻放在MAX30102传感器上(不要用力按压) 3. 等待5秒量时间 4. 系统将显示血氧饱和度(SpO2)和心率(BPM) 5. 量结果会显示10秒后自动返回待检测状态 6. 右侧的彩色条指示血氧水平(绿色=正常,黄色=中等,红色=低) ## 故障排查指南 ### 常见问题及解决方案 1. **OLED不显示内容** - 检查VCC是否连接到5V(不是3.3V) - 确认I2C地址:尝试0x3C和0x3D - 在代码中修改地址:`U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, 0x3D);` - 检查SDA/SCL连接是否正确 2. **MAX30102无法检测手指** - 确保手指完全覆盖传感器 - 检查传感器是否连接在3.3V而非5V - 增加LED亮度:修改`ledBrightness`值(最高255) - 检查传感器是否被污染(用酒精棉清洁) 3. **量结果不准确** - 保持手指稳定不动 - 避免环境光干扰(用手指遮挡传感器) - 确保手指不过冷(影响血液循环) - 多次量取平均值 4. **系统频繁重启或卡死** - 内存优化:确保启用了编译器优化 - 文件 → 首选项 → 勾选"编译期间显示详细输" - 在编译输中检查内存使用情况 - 减少缓冲区大小:修改`BUFFER_SIZE`为更小的值 - 简化显示内容,减少字符串使用 ### 内存优化技巧 ```cpp // 使用F()宏将字符串保存在Flash而非RAM中 displayMessage(F("Place finger")); // 减少全局变量 // 将缓冲区设为局部变量(需调整算法) // 使用PROGMEM存储大型常量数据 // 优化U8g2内存使用 U8G2_SH1106_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0); // 使用页面缓冲模式 ``` ### 串口调试技巧 在代码中添加调试输: ```cpp Serial.print(F("IR Value: ")); Serial.println(particleSensor.getIR()); Serial.print(F("SpO2: ")); Serial.print(spo2); Serial.print(F("% | BPM: ")); Serial.println(beatAvg); ``` ## 功能扩展建议 1. **添加警报功能** - 当血氧低于90%时触发蜂鸣器警报 - 添加振动马达提供触觉反馈 2. **数据记录功能** - 添加SD卡模块记录量数据 - 实现时间戳记录(添加RTC模块) 3. **蓝牙传输** - 添加HC-05蓝牙模块 - 将数据发送到APP进行期监 4. **语音播报** - 添加DFPlayer Mini模块 - 使用预录制的语音播报量结果 5. **低功耗模式** - 添加运动检测传感器 - 当无操作时进入睡眠模式 - 使用中断唤醒系统
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值