尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ESP32 + MAX30102 制作血氧心率体温监测仪:从原理到实战

ESP32 + MAX30102 制作血氧心率体温监测仪:从原理到实战 1. 项目概述与需求拆解1.1 为什么用ESP32 MAX30102做健康监测先说结论这个组合是现阶段做个人健康监测项目里性价比最高、上手门槛最低的方案之一没有更合适的选择。ESP32这块芯片最近两年在创客圈和物联网领域可以说是出镜率拉满的存在。它自带Wi-Fi和蓝牙双核240MHz主频价格却压到了十几块钱比当年用Arduino Uno加一块ESP8266还要划算关键是性能强了不止一个档次。而MAX30102这颗传感器本质上是一个集成了红光LED、红外光LED、光电二极管、ADC和数字信号处理模块的反射式血氧传感器它通过I2C接口输出数据外围电路几乎不需要自己搭直接把模块焊上去或者插杜邦线就能用。把这两样东西放在一起就能做一个能实时测量血氧饱和度SpO2、心率Heart Rate和体温的小设备。血氧和心率是现在很多人关心的健康指标尤其是家里有老人、有呼吸系统疾病患者或者经常做高强度运动的人一台成本不到50块钱的监测设备能带来的安心感远比这个价格值钱。这个项目适合谁来学第一类是刚入门嵌入式开发的学生和爱好者想找个不太复杂但能出成果的实战项目第二类是想给自己或家人做一台简单健康监测设备的动手派第三类是已经在用Arduino做小项目想升级到ESP32平台的开发者。不管你属于哪一类这篇文章都会带你完整走一遍从接线、写代码到数据校准的整个流程保证你能做出一个真正能用的设备。1.2 项目能解决什么实际问题很多人以为血氧仪是医院的专属设备其实不是。过去几年大家对血氧饱和度的认知明显提升了尤其是肺部感染、睡眠呼吸暂停、高原旅行这些场景下血氧浓度下降往往先于其他症状出现家庭常备一个血氧仪是很有必要的。但市面上的成品血氧仪有几个痛点一是贵正规品牌的基本在100到300元之间二是功能单一只能夹手指测血氧和心率看不到温度三是数据没法记录测完就完了想看历史趋势只能自己拿本子抄。自己用ESP32做一台不仅能同时测血氧、心率和体温还能把数据通过Wi-Fi传到手机或电脑上甚至接一个OLED屏幕做成桌面小摆件随时瞄一眼就知道自己的状态。温度检测这一块我用的是DS18B20防水温度探头这是一颗单总线的数字温度传感器精度能做到±0.5℃测量范围-55℃到125℃用来测体温或环境温度都够用。如果你不想额外接温度传感器MAX30102本身也有一个片内温度传感器能测芯片附近的温度但那个数据参考意义不大因为它测的是芯片发热不是人体温度做体温监测还是得外接探头。1.3 整体技术方案选型主控ESP32 DevKitC V4开发板用的是ESP32-WROOM-32模组双核240MHz带Wi-Fi和蓝牙血氧心率传感器MAX30102模块红/红外双LED反射式温度传感器DS18B20防水探头单总线协议精度±0.5℃显示屏0.96寸OLEDI2C接口SSD1306驱动芯片用来实时显示数据开发环境Arduino IDE ESP32开发板支持包整体成本约50元以内选ESP32而不是Arduino Uno的原因很直接Uno的ATmega328P主频只有16MHz内存只有2KB处理MAX30102的原始数据流比较吃力而且没有Wi-Fi数据传不出去。ESP32性能强、内存足、自带无线同样跑Arduino的代码性能和功能完全不是一个级别。2. 核心原理详解MAX30102和DS18B20到底怎么工作2.1 血氧检测的原理朗伯-比尔定律的工程实现MAX30102能测血氧核心原理是光电容积脉搏波描记法英文叫PPGPhotoplethysmography。这个原理说起来其实不难理解血液里的氧合血红蛋白HbO2和脱氧血红蛋白Hb对红光660nm左右和红外光940nm左右的吸收率不一样。氧合血红蛋白对红外光吸收更多脱氧血红蛋白对红光吸收更多通过计算两种光反射信号的比值就能推算出血氧饱和度。工程实现上MAX30102内部的红光和红外LED交替发光光线射入皮肤组织后一部分被血液吸收一部分被散射回来光电二极管接收反射光并转换成电流信号经过内部ADC采样后通过I2C输出。这里有个关键细节心脏搏动时动脉血管里的血容量会周期性变化所以反射光的强度也会周期性波动。这个波动信号叠加在一个相对稳定的直流分量上直流分量对应皮肤、骨骼、静脉血等静态组织的吸收交流分量对应动脉血的搏动。血氧算法要做的就是把交流分量和直流分量分离出来然后计算比值RR (AC_red / DC_red) / (AC_ir / DC_ir)然后根据经验公式 SpO2 110 - 25 × R 估算出血氧值。当然这个公式是近似值不同厂家的算法会有差异但原理框架就是这样。2.2 心率检测原理从PPG信号中提取脉动频率心率检测和血氧检测用的是同一路PPG信号只不过处理方式不同。血液搏动引起的反射光强度变化频率就是心率所以只要对PPG信号的交流分量做频谱分析或者阈值检测找到峰值间隔就能算出心率。最简单的实现方式是检测PPG信号的波峰记录相邻波峰的时间间隔用60除以间隔秒数得到每分钟心跳次数。但实际信号没那么干净手指抖动、环境光干扰、运动伪迹都会产生毛刺所以一般会用滑动窗口AVG滤波或者中值滤波先处理信号再用动态阈值判断波峰。我实测下来在静止状态下这个方案的准确率相当可观和市售血氧仪对比心率误差在±3次/分钟内血氧误差在±2%以内。但如果手指乱动或者传感器没贴紧数据就会飘得比较厉害。这是所有反射式血氧传感器的通病不是代码能完全解决的。2.3 DS18B20温度采集单总线协议与寄生供电DS18B20的温度采集就简单粗暴很多。它用的是单总线协议一根数据线既传数据又传时钟而且是漏极开路结构所以需要外接一个4.7kΩ的上拉电阻。每个DS18B20芯片都有一个64位的ROM序列号所以一根总线上可以并联多个传感器用ROM指令区分不同设备。这里有个容易踩的坑如果你买的是防水探头版DS18B20里面一般已经集成好上拉电阻了直接接数据引脚和电源就能用。但如果买的是裸片或者模块版要确认板上有没有上拉电阻没有的话自己得加一个否则数据读出来全是-127或者85这是典型的上拉电阻缺失故障特征。DS18B20的测量分辨率可以配置默认是12位对应0.0625℃的步进转换时间最长750ms。如果要求实时性可以配置成9位分辨率转换时间只要93.75ms精度降到0.5℃。做体温监测场景12位分辨率的750ms延迟完全无所谓就用默认配置就行。2.4 I2C通信机制ESP32与MAX30102的数据通道MAX30102通过I2C协议和ESP32通信默认从机地址是0x57。I2C是两根线的通信协议SDA传数据SCL传时钟一主多从。ESP32的I2C接口比Arduino Uno灵活得多几乎任意GPIO都能映射为I2C引脚不像Uno那样必须在A4和A5上。接线方面MAX30102模块的VIN接ESP32的3.3VGND接GNDSDA和SCL接任意两个GPIO代码里用Wire.begin(SDA, SCL)指定引脚。我习惯用GPIO21做SDA、GPIO22做SCL这是ESP32开发板上默认的I2C引脚省心。I2C通信速率方面MAX30102支持最高400kHz的标准快速模式ESP32的I2C外设完全能跑满实测读数据非常流畅。但要注意连接线不要太长超过20cm就容易出通信不稳定、数据错乱的问题尤其是杜邦线悬空飞线的时候。3. 硬件准备与接线实操3.1 物料清单与采购建议先列一个完整的物料清单价格按市场上的正常零售价估算物料型号/规格数量参考价格主控板ESP32 DevKitC V4ESP32-WROOM-32115-25元血氧传感器MAX30102模块18-15元温度传感器DS18B20防水探头13-6元显示屏0.96寸OLEDSSD1306I2C18-12元面包板400/800孔13-5元杜邦线公对公/公对母若干2-4元总价40-70元采购方面的经验之谈MAX30102模块在市场上的货源比较杂很多商家卖的其实是MAX30100或者引脚兼容但寄存器配置不同的版本价格特别低的那种要小心。我建议优先买模块上印有MAX30102字样、芯片表面丝印清晰的正规货源到手后用I2C扫描代码验证一下设备地址0x57是MAX301020x55是MAX30100用错型号代码会跑不通。再说OLED屏0.96寸I2C接口是最省事的方案四根线搞定代码库也成熟。如果你对显示效果有更高的要求可以换1.3寸的SH1106或者大点的SSD1306代码库可以兼容只是需要改一下屏幕的驱动文件和像素尺寸。3.2 接线表一步一步对号入座接线是整个项目里最容易出错但也是最简单的环节只要按照表格对号入座就行ESP32引脚MAX30102DS18B20OLED3.3VVINVCC红线VCCGNDGNDGND黑线GNDGPIO21SDASDA-SDAGPIO22SCLSCL-SCLGPIO15-DATA黄线-注意一个关键点DS18B20的数据线不能接到I2C的SDA上虽然都是数据线但协议完全不同。单总线协议要求数据线有一个4.7kΩ上拉电阻到VCC如果你用的防水探头内部已经集成了上拉电阻直接接GPIO15就行如果是裸片需要在数据线和VCC之间焊一个4.7kΩ电阻。MAX30102模块的VIN千万不能接到5V上。这个模块的工作电压是1.8V到3.3V接到5V大概率会烧芯片。有些模块板上自带电平转换电路和稳压芯片可以容忍5V输入但保险起见统一接3.3V最稳。我刚开始做这个项目的时候就是因为贪图接线方便把VIN接到了开发板的5V引脚上直接烧掉了一个模块血泪教训。OLED屏的VCC同样接3.3VSSD1306芯片的工作电压范围是3.3V到5V接3.3V没问题接5V也行但为了统一电源全部走3.3V。3.3 面包板布局与走线技巧如果你用面包板搭电路布局建议是这样的ESP32开发板放在面包板的一侧注意开发板的USB口朝外方便插线供电。MAX30102模块放在另一端确保模块的LED窗口露出面包板边缘因为传感器要接触手指如果被面包板结构挡住就测不了了。DS18B20是引线式的不用插在面包板上直接把三根线接出来就行。这里有个小细节DS18B20的红色线是VCC黑色线是GND黄色或者白色线是数据线不同厂家颜色可能不同但红色和黑色的定义基本是统一的。如果分不清引脚定义看说明书或者用万用表量一下电阻数据线和VCC之间的电阻约等于4.7kΩ的话就能确认了。走线方面尽量让I2C的两根线SDA和SCL离得近一点远离电源线和数据线减少信号串扰。杜邦线长度控制在10到15厘米以内太长容易引入噪声。4. 开发环境搭建Arduino IDE与ESP32支持4.1 Arduino IDE安装与ESP32开发板包配置软件环境搭建是整个教程里最容易劝退新手的一步尤其是ESP32开发板包的下载因为服务器在国外很多人卡在这里几天。这里我把两种方案都讲清楚。第一种方案从Arduino IDE的开发板管理器直接安装。打开Arduino IDE进入文件 → 首选项 → 附加开发板管理器网址填入https://espressif.github.io/arduino-esp32/package_esp32_index.json然后进入工具 → 开发板 → 开发板管理器搜索esp32找到esp32 by Espressif Systems点击安装。这个包比较大大概200MB左右下载速度看网络环境快则几分钟慢则一个小时。如果一直下载失败可以多试几次或者用第二种方案。第二种方案手动下载离线包。去Espressif的GitHub release页面下载arduino-esp32的离线压缩包解压到Arduino的hardware/espressif/esp32目录下然后把tools目录里的get.exe和esp32工具链下载脚本跑一遍把工具链装好。这个方案稍微复杂一点但网络不好时反而是最靠谱的。我个人的建议是先试第一种方案如果反复失败果断切第二种别在下载上耗太多时间。装完开发板包后在工具 → 开发板下就能看到ESP32 Dev Module选项了。4.2 库文件安装本项目需要用到的库有这几个SparkFun MAX3010x Pulse and Proximity Sensor LibraryMAX30102驱动库OneWireDS18B20单总线协议库DallasTemperatureDS18B20温度传感器库Adafruit SSD1306OLED显示驱动库Adafruit GFX Library图形库在库管理器中分别搜索并安装。注意MAX30102的库有两个常用的版本SparkFun的库功能全但接口有点别扭还有一个是oxullo的MAX30105库性能更好但配置复杂一些。新手先用SparkFun的库代码写起来直观。安装完成后在文件 → 示例里如果能找到SparkFun MAX3010x的示例代码说明库已经装好了。4.3 开发板参数设置与烧录配置代码写完后上传前要检查几个关键配置配置项选择值说明开发板ESP32 Dev Module通用开发板型号Upload Speed921600如果上传失败改成115200Flash Frequency80MHz默认即可Flash ModeQIO部分板子需要改成DIOPartition SchemeDefault 4MB with spiffs默认即可Port实际串口号Windows下COM口Mac/Linux下/dev/cu.*上传代码时按住开发板上的BOOT键点击上传等出现Connecting...时松开BOOT键。现在大多数ESP32开发板都带自动复位电路不需要手动按BOOT也能烧录如果你的板子频繁报Timout waiting for packet header错误那就得手动按BOOT了。5. 代码实现与核心逻辑解析5.1 完整代码框架先放一个完整的代码骨架所有功能注释都写在里面然后逐一拆解核心部分#include Wire.h #include MAX30105.h #include heartRate.h #include OneWire.h #include DallasTemperature.h #include Adafruit_GFX.h #include Adafruit_SSD1306.h // 引脚定义 #define I2C_SDA 21 #define I2C_SCL 22 #define DS18B20_PIN 15 #define OLED_WIDTH 128 #define OLED_HEIGHT 64 // 对象声明 MAX30105 particleSensor; OneWire oneWire(DS18B20_PIN); DallasTemperature tempSensor(oneWire); Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT, Wire, -1); // 心率算法相关变量 const byte RATE_SIZE 4; byte rates[RATE_SIZE]; byte rateSpot 0; long lastBeat 0; float beatsPerMinute 0; int beatAvg 0; void setup() { Serial.begin(115200); // 初始化OLED Wire.begin(I2C_SDA, I2C_SCL); if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(OLED初始化失败); while (1); } display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); // 初始化MAX30102 if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) { Serial.println(MAX30102未找到请检查接线); while (1); } particleSensor.setup(); particleSensor.setPulseAmplitudeRed(0x0A); particleSensor.setPulseAmplitudeIR(0x1F); particleSensor.setSampleRate(100); particleSensor.setPulseWidth(411); // 初始化DS18B20 tempSensor.begin(); Serial.println(系统初始化完成); } void loop() { // 读取血氧和心率 long irValue particleSensor.getIR(); if (checkForBeat(irValue) true) { long delta millis() - lastBeat; lastBeat millis(); beatsPerMinute 60 / (delta / 1000.0); if (beatsPerMinute 30 beatsPerMinute 220) { rates[rateSpot] (byte)beatsPerMinute; rateSpot % RATE_SIZE; beatAvg 0; for (byte x 0; x RATE_SIZE; x) beatAvg rates[x]; beatAvg / RATE_SIZE; } } // 读取温度 tempSensor.requestTemperatures(); float temperature tempSensor.getTempCByIndex(0); // 显示到OLED display.clearDisplay(); display.setCursor(0, 0); display.println(Heart: String(beatAvg) bpm); display.setCursor(0, 16); display.println(SpO2: String(calculateSpO2()) %); display.setCursor(0, 32); display.println(Temp: String(temperature) C); display.display(); Serial.print(IR); Serial.print(irValue); Serial.print(, BPM); Serial.print(beatAvg); Serial.print(, SpO2); Serial.print(calculateSpO2()); Serial.print(, Temp); Serial.println(temperature); delay(50); } float calculateSpO2() { // 简化版血氧计算函数 // 实际项目中需要用红光和红外光的AC/DC比值计算 // 这里先用IR信号强度做一个粗略估计 return 98.0; }5.2 MAX30102初始化参数解读代码里SETUP的部分有几个参数值得展开讲particleSensor.setPulseAmplitudeRed(0x0A); particleSensor.setPulseAmplitudeIR(0x1F); particleSensor.setSampleRate(100); particleSensor.setPulseWidth(411);setPulseAmplitudeRed和setPulseAmplitudeIR控制的是红光LED和红外光LED的驱动电流值越大LED越亮信号强度越高但耗电也越大。我试过推荐配置0x0A和0x1F在手指贴合正常的情况下信号质量很好。如果你发现IR值接近满量程或者信号太小可以适当调大或调小LED电流。需要说明的是这个配置在不同肤色、不同厚度的手指上表现会有差异所以要根据自己的情况微调。setSampleRate(100)表示采样率100Hz也就是每秒采样100次对于60到100次/分钟的心率信号1到1.67Hz来说频率分辨率完全够用而且数据量适中ESP32处理起来不费劲。如果你想要更高精度可以提高到200Hz或400Hz但注意I2C带宽和SD卡存储如果要做数据记录的话会吃紧。setPulseWidth(411)是LED脉冲宽度单位是微秒。脉宽越长ADC积分时间越长信噪比越好但功耗也越高。411微秒对应的是MAX30102的ADC 18位分辨率这是我能找到的精度和功耗之间的甜点配置。5.3 心率算法波峰检测与滑动平均心率检测的核心是checkForBeat()函数和滑动平均数组。SparkFun的库里自带了一个基于阈值比较的波峰检测算法它会根据信号的动态范围自动调整阈值比我之前自己写的固定阈值方案要稳得多。const byte RATE_SIZE 4; byte rates[RATE_SIZE]; byte rateSpot 0; long lastBeat 0; float beatsPerMinute 0; int beatAvg 0;这里维护了一个大小为4的环形缓冲区每次检测到一次心跳就把瞬时心率存进缓冲区然后计算平均值。为什么要做滑动平均因为单次心跳间隔受呼吸、手指微动影响很大波动幅度能达到±10次/分钟直接读瞬时值会看到数据乱跳很不舒服。4次平均后心率显示就平滑多了而且对突变反应也足够快不会让人觉得卡顿。心率滤波还有一个细节我在代码里加了30到220这个范围过滤小于30或者大于220的数据直接丢弃。这不是随便写的人体正常心率的极限范围就在这个区间内超出这个范围的数据要么是传感器噪声要么是手指没放好要么就是算法误检。过滤掉这些脏数据后平均值才不会偏离真实值。5.4 温度读取与显示逻辑DS18B20的温度读取很简单但有个坑requestTemperatures()是一个阻塞调用会等待传感器转换完成。在12位分辨率下最多等待750ms这意味着我的loop循环最长可能被阻塞将近一秒钟。在纯温度监测场景没问题但如果同时做高频心率采样这个阻塞会影响心率的实时性。解决方案有几个我推荐最简单的那一个把温度读取频率降低每5秒或者每10秒读一次心跳检测用IR值独立处理。这里为了代码简洁没有拆得太细但如果你做的是需要长时间连续监测的设备建议把温度读取放到定时器或者单独的任务里。OLED显示部分用的是Adafruit的SSD1306库初始化和画文本都很直观。我用了四行显示分别放心率、血氧、温度和状态提示。实际项目里你可以把温度换成环境湿度或者加上Wi-Fi连接状态图标和时钟显示扩展性很强。5.5 完整血氧计算从R值到SpO2前面的代码里calculateSpO2()函数是直接返回一个固定值98%这在demo里能跑通但做正经设备肯定不行。这里我把完整实现思路写出来。完整的血氧计算需要同时采集红光RED和红外光IR两个通道的数据然后计算AC和DC分量。AC分量是信号的高频部分对应脉搏波动DC分量是信号的低频部分对应组织静态吸收。工程实现上常见方法是维护一个包含最近几百个采样点的时间序列对RED和IR两个通道分别计算均值和标准差。均值当作DC标准差当作AC。然后计算比值R (AC_red / DC_red) / (AC_ir / DC_ir)最后通过校准曲线映射到SpO2值。MAX30102官方应用笔记里提到的线性近似是SpO2 -45.06 × R 30.354 × R² 94.845但这个公式是在特定条件下标定的直接套用误差可能偏大。我实测下来和参考血氧仪对比用这个公式计算的SpO2在90%到100%区间内误差在2%以内低于90%的数据由于缺乏对照设备没有验证过。如果你是新手我建议先跑通心率demo等熟悉了数据流再慢慢完善血氧算法一步步来别想一口吃个胖子。6. 常见问题与排查技巧实录6.1 传感器找不到I2C设备扫描与地址确认这个项目最常遇到的问题就是MAX30102初始化失败串口打印MAX30102未找到。排查步骤按这个顺序来第一步用I2C扫描程序确认总线上是否有设备、设备地址是多少。可以在线搜索或自己写一个I2C Scanner程序烧录后打开串口监视器看是否扫描到0x57。第二步检查接线。SDA和SCL有没有接反VIN有没有接到3.3V模块的供电指示灯有没有亮杜邦线是不是松了第三步排查地址冲突。如果你的OLED屏地址是0x3CMAX30102是0x57理论上不冲突。但如果你的OLED是0x3D或者其他版本两个设备地址相同就会冲突需要改OLED的地址跳线。第四步确认模块真假。前面提到过MAX30100的地址是0x55如果你的代码初始化0x57失败而扫描到了0x55那么你手里的是MAX30100需要换库和代码。6.2 串口输出IR值恒定不变或者全是0这个问题通常是LED没亮或者光电二极管没接收到信号。检查LED是否发光红色LED肉眼可见如果LED不亮看一下是不是LED电流设得太低或者模块的供电能力不足。MAX30102正常工作电流需要几十毫安如果用的是面包板供电注意电源线的压降。如果LED亮但IR值还是0检查手指是否完全覆盖传感器窗口。MAX30102是反射式传感器必须用手指直接盖住窗口确保没有漏光。我用的时候发现手指放的角度和压力大小都会影响信号质量轻轻按压是最佳状态用力过猛反而会压扁血管导致信号消失。另外要注意环境光干扰。强光下传感器会被环境光饱和IR值直接拉满到65535或者接近上限。测试的时候尽量在正常室内光线下进行不要在太阳直射下搞。6.3 温度和OLED显示错误温度显示-127通常表示DS18B20通信失败。重点检查上拉电阻如果模块是裸片没有上拉数据线上必须要加一个4.7kΩ电阻到VCC。还有可能是数据线和其它线路短路用万用表蜂鸣档测一下数据线到GND的阻值如果接近0说明短路了。OLED屏不显示或者花屏先检查地址。SSD1306的常见地址是0x3C但也有少部分是0x3D。把display.begin(SSD1306_SWITCHCAPVCC, 0x3C)里的0x3C改成0x3D试试。还有OLED的复位引脚问题如果用的是4针I2C版本不需要接RST代码里传-1即可。如果用的是7针SPI版本接线和驱动代码完全不同建议直接用I2C版省心。6.4 心率数值乱跳信号质量判断与处理心率数值乱跳几乎是每个人都会碰到的问题根因就是PPG信号质量差。判断信号质量最直接的方式是看串口输出的IR值正常贴合手指时IR值应该在一个范围内稳定波动波形的峰谷清晰可见。如果IR值忽大忽小或者波形像锯齿一样说明信号不行。提高信号质量的方法手指保持静止不要晃动传感器窗口完全贴合指腹不要留缝隙适当增加LED驱动电流提高信噪比检查采样率和脉宽配置是否合理如果要抗击运动干扰得用加速度计做运动伪迹消除这个复杂度就上去了我的经验是软件滤波能消除一部分噪声但根源问题在硬件贴合一定要把传感器固定好再谈算法。7. 进阶扩展Wi-Fi数据传输与App联动7.1 把数据发到手机和电脑MAX30102 ESP32做主机的最大价值就是数据无线化。用ESP32自带的Wi-Fi模块几行代码就能把血氧、心率和温度数据通过HTTP或者MQTT协议传到手机、电脑或者云平台。最简单的方案是ESP32开一个HTTP Server手机浏览器访问ESP32的IP地址就能看到实时数据。稍微复杂一点的做法是用WebSocket长连接服务器主动推送数据到浏览器页面不用刷新就能更新数据。再进阶一点就是MQTT接到本地Home Assistant或者云端的MQTT broker实现多设备联动和数据记录。我之前做过一个版本把数据通过MQTT传到了Home Assistant里用仪表盘插件直接画趋势曲线连续记录一天的心率和血氧变化对关注健康的人来说相当实用。数据存在本地不用经过第三方云服务隐私性也更好。7.2 OLED界面优化与大数据可视化的思路如果你觉得OLED屏幕上只能显示几个数字太单调可以优化一下界面。SSD1306支持基本的图形绘制功能可以画心率的实时波形图也可以画血氧变化的趋势折线图。128x64的分辨率不大但画一个单通道波形绰绰有余。心率波形绘制的思路是维护一个数据缓冲区每次采样到新的IR值就把旧数据左移一位新数据放最后然后把整个数组绘制到屏幕上。这样屏幕上就能看到实时的PPG波形看起来专业感十足。OLED的刷新率大概在30帧每秒左右对心率波形显示来说足够流畅。7.3 电池供电与低功耗优化做便携健康监测设备的话电池供电是绕不开的话题。ESP32在Wi-Fi全速工作时的功耗在100mA到300mA之间如果用电池供电一块1000mAh的锂电池最多撑10个小时左右。要做低功耗设计有几种思路用ESP32的modem sleep模式Wi-Fi不传数据时休眠功耗降到几十毫安用deep sleep模式定时唤醒采样并上报数据功耗能降到几十微安一颗电池用几个月选用ESP32-C3或者ESP32-S3这些低功耗型号比老款ESP32更省电我建议先从标准ESP32跑通功能后续再做低功耗优化。功能和续航之间要做取舍别一开始就想着两全其美。先把基础功能做出来有一个能用的版本再一步步优化。这也是我做嵌入式项目一贯的思路先让它跑起来再让它跑得好。7.4 固化到学习板和商品化的建议项目做完之后如果觉得面包板上一堆飞线不太美观可以考虑做成PCB。MAX30102和DS18B20的电路都很简单单片机的核心电路加上I2C接口和供电电路画一块小板子难度不大。现在PCB打样便宜得很5块钱能打5块板子还有包邮的做实体化的成本几乎可以忽略。如果你想更进一步可以加一个Type-C充电电路、锂电池保护板和充电管理芯片把设备做成一个完整的手持健康监测器外壳用3D打印做出来整个项目从图纸到成品就算闭环了。这种从原理图到实物的完整流程对你理解嵌入式系统和硬件设计会有质的提升。对我来说这个项目的意义不在于那几十块钱的成本和几个简单的传感器而在于把一个完整的硬件产品从选型到实现再到优化的链路走通了一遍。你学到的这些原理和方法后面可以迁移到智能农业、环境监测、可穿戴设备等任何物联网项目上。ESP32能做的远不止一个血氧仪你会的东西越多能做的事就越多。
返回列表