ESP32+MAX30102+OLED:DIY心率血氧监测仪全攻略
2026/9/20 3:08:35 网站建设 项目流程

1. 从一堆散件到一台能用的健康监测仪:整体设计思路

1.1 这个项目到底在做什么

手头有一块 ESP32 开发板、一个 MAX30102 心率血氧模块、一块 0.96 寸 OLED 屏,外加几根杜邦线。目标很明确:把这三样东西拼在一起,做成一台能实时显示心率、血氧饱和度的小型健康监测仪。插上电,手指往传感器上一放,OLED 屏幕上就能跳出当前的心率和血氧数值,不需要连电脑,不需要额外的仪器。

听起来简单,但真正动手做的时候,你会发现事情没那么轻松。MAX30102 出来的原始数据是一堆红外和红光的 ADC 采样值,直接显示毫无意义;OLED 屏幕要正确点亮,I2C 地址、初始化序列、供电电压都得对;ESP32 的 I2C 引脚分配、上拉电阻、时钟频率也会影响整个系统的稳定性。这些问题一个接一个,每一个都可能让你卡上半天。

这个项目适合谁?如果你刚接触 ESP32,学过一点 MicroPython 或者 Arduino,想找一个既有实用性又能把 I2C 通信、传感器驱动、屏幕显示串起来的综合练习,那这个项目非常合适。它不像点个 LED 那么简单,但也不至于复杂到让人放弃。做完之后,你对 I2C 总线的理解、对传感器数据处理的思路、对嵌入式项目调试的方法,都会上一个台阶。

1.2 为什么选 ESP32 + MAX30102 + OLED 这个组合

市面上做心率监测的方案不少,为什么偏偏选这三个凑一起?这里面有讲究。

ESP32 的核心优势在于它同时具备 Wi-Fi 和蓝牙能力,双核 240MHz 的主频处理传感器数据绰绰有余,GPIO 资源丰富,I2C 接口可以灵活映射到几乎任意引脚。更重要的是,它的生态非常成熟,MicroPython 和 Arduino 两种开发方式都有大量现成库可以用。对于这个项目来说,ESP32 的性能是过剩的,但过剩意味着你后续想加功能——比如把数据传到手机、加个温度传感器、驱动更大的屏幕——都不用换主控。

MAX30102 是一颗集成度很高的心率血氧传感器,内部集成了红光 LED、红外 LED、光电探测器、光学元件和低噪声信号处理电路。它通过 I2C 接口输出原始数据,最高采样率可以到 3200Hz,内置 FIFO 可以缓存 32 组样本。相比自己搭光电电路,用 MAX30102 省去了大量模拟电路设计的麻烦。它的工作电压是 1.8V 核心电压加 3.3V 或 5V 的 LED 供电,模块化产品通常已经做好了电平转换,直接接 ESP32 的 3.3V 就行。

OLED 选 0.96 寸 I2C 版本,原因就两个字:省事。I2C 接口只需要两根信号线,和 MAX30102 可以挂在同一条总线上,只要地址不冲突就行。0.96 寸的尺寸刚好能显示心率和血氧两个核心数值,分辨率 128x64 足够显示清晰的数字和简单图标。SSD1306 驱动芯片的库非常成熟,MicroPython 和 Arduino 都有现成支持。

1.3 系统架构与数据流

整个系统的数据流是这样的:MAX30102 以一定频率采集红光和红外光的反射信号,通过 I2C 总线把原始数据传给 ESP32。ESP32 拿到数据后,先做滤波处理去掉高频噪声和基线漂移,然后用峰值检测算法找出心跳周期,计算心率;同时利用红光和红外光吸收比值的经验公式估算血氧饱和度。最后把计算好的数值格式化,通过另一路 I2C 或者同一路 I2C 发给 OLED 屏幕显示。

这里有个关键点:MAX30102 和 OLED 如果挂在同一条 I2C 总线上,必须确保它们的 I2C 地址不冲突。MAX30102 的固定地址是 0x57(7 位地址),OLED 的 SSD1306 常见地址是 0x3C 或 0x3D,两者不冲突,可以共用一条总线。ESP32 作为 I2C 主机,轮流和两个从机通信,这在 MicroPython 里用同一个 I2C 对象就能实现。

注意:虽然可以共用总线,但 MAX30102 的数据读取频率较高,如果 OLED 刷新太频繁,可能会影响传感器数据的实时性。建议 OLED 刷新率控制在 5-10Hz,传感器采样率保持在 50-100Hz。

2. 硬件连接与 I2C 总线那些坑

2.1 引脚分配与接线方案

先把手上的模块认清楚。ESP32 开发板常见的有 ESP32-DevKitC、NodeMCU-32S、ESP32-WROOM-32 等,引脚编号在不同板子上可能标注不一样,但 GPIO 编号是统一的。我一般选 GPIO21 作为 SDA,GPIO22 作为 SCL,这是 ESP32 默认的 I2C 引脚,大部分库都认这个默认值。

接线的时候注意几点:MAX30102 模块通常有 VIN、GND、SCL、SDA、INT 五个引脚。VIN 接 ESP32 的 3.3V,GND 共地,SCL 接 GPIO22,SDA 接 GPIO21,INT 引脚如果不用中断可以悬空。OLED 模块一般有 VCC、GND、SCL、SDA 四个引脚,VCC 接 3.3V,GND 共地,SCL 和 SDA 分别接到和 MAX30102 相同的总线上。

模块引脚ESP32 引脚说明
MAX30102VIN3.3V供电,不要接 5V
MAX30102GNDGND共地
MAX30102SCLGPIO22I2C 时钟线
MAX30102SDAGPIO21I2C 数据线
MAX30102INT悬空不用中断可不接
OLEDVCC3.3V供电
OLEDGNDGND共地
OLEDSCLGPIO22与传感器共用
OLEDSDAGPIO21与传感器共用

2.2 I2C 上拉电阻:最容易忽略的细节

I2C 总线是开漏输出结构,这意味着总线上的设备只能把线拉低,不能主动拉高。所以必须要有上拉电阻把线拉到高电平,否则总线永远处于低电平状态,通信直接失败。很多模块板子上已经自带了 4.7k 或 10k 的上拉电阻,但有些便宜的模块为了省成本没焊,这时候你就得自己加。

怎么判断需不需要加上拉电阻?用万用表测一下 SDA 和 SCL 对 VCC 的电阻,如果读到 4.7k 或 10k 左右,说明板载已经有了。如果读到无穷大,那就得自己在 SDA 和 SCL 上各接一个 4.7k 电阻到 3.3V。上拉电阻太小会导致功耗增加和信号上升沿过陡,太大则上升沿太慢导致通信失败。4.7k 在 3.3V 系统、100kHz 到 400kHz 的速率下是比较稳妥的选择。

实操心得:我曾经遇到过一块 OLED 模块,单独用的时候正常,和 MAX30102 挂一起就点不亮。查了半天发现是 OLED 模块板载上拉电阻是 10k,MAX30102 模块板载也是 10k,两个并联变成 5k,理论上没问题,但实际因为走线电容和模块质量,波形已经烂到无法识别。后来把 OLED 的上拉电阻拆掉,只保留 MAX30102 的,问题解决。所以多个模块共用总线时,上拉电阻不是越多越好,并联后总阻值不要低于 2.2k。

2.3 I2C 地址扫描与设备确认

接线完成后,第一件事不是急着写驱动,而是先扫描 I2C 总线,确认两个设备都能被识别到。MicroPython 下用下面这段代码:

from machine import Pin, I2C i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000) devices = i2c.scan() print("I2C devices found:", [hex(d) for d in devices])

正常应该输出类似['0x3c', '0x57']的结果。如果只看到一个或者一个都没有,先检查接线是否松动、供电是否正常、上拉电阻是否到位。如果看到一堆乱七八糟的地址,通常是总线干扰或者上拉电阻不合适导致的。

Arduino 环境下可以用 Wire 库的扫描示例:

#include <Wire.h> void setup() { Wire.begin(21, 22); Serial.begin(115200); Serial.println("I2C Scanner"); } void loop() { byte error, address; int nDevices = 0; for (address = 1; address < 127; address++) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("Found device at 0x"); Serial.println(address, HEX); nDevices++; } } if (nDevices == 0) Serial.println("No I2C devices found"); delay(5000); }

扫描到设备只是第一步,接下来要确认设备能正常读写。MAX30102 的 Part ID 寄存器地址是 0xFF,读出来应该是 0x15。如果读出来是 0x00 或者 0xFF,说明通信有问题,可能是地址错了或者芯片没正常工作。

3. MAX30102 驱动与数据处理核心细节

3.1 MAX30102 寄存器配置与初始化

MAX30102 内部有一堆寄存器需要配置,包括模式选择、采样率、LED 电流、FIFO 控制等。初始化流程大致是:复位、设置 FIFO 配置、设置模式为 SpO2 模式、设置 LED 电流、设置采样率和脉宽、清除中断状态。

关键寄存器说明:

寄存器地址名称功能推荐值
0x09Mode Config工作模式0x03 (SpO2)
0x0ASpO2 Config采样率、脉宽0x27 (100Hz, 411us)
0x0CLED1 PA红光 LED 电流0x24 (约 7mA)
0x0DLED2 PA红外 LED 电流0x24 (约 7mA)
0x04FIFO ConfigFIFO 平均、回卷0x50
0x08FIFO Write Ptr写指针0x00
0x02Int Enable中断使能0xC0

LED 电流的设置很关键。电流太小,信号弱,信噪比差;电流太大,功耗高,而且可能让光电探测器饱和。7mA 左右对大多数人的手指厚度来说是比较合适的起点。如果发现信号幅度太小,可以逐步增加到 10mA 或 12mA;如果信号削顶,就往下调。

采样率设 100Hz 意味着每秒采 100 组红光和红外数据。脉宽 411us 对应 18 位 ADC 分辨率,这是精度和速度的平衡点。FIFO 平均设为 4,相当于硬件做了 4 次平均,能有效降低随机噪声。

3.2 原始数据读取与 FIFO 操作

MAX30102 的 FIFO 是 32 组样本的循环缓冲区,每组样本包含红光和红外两个 18 位数据,实际存储时每个数据占 3 个字节,所以一组样本是 6 个字节。读 FIFO 的时候要先读写指针和读指针,计算可读样本数,然后连续读取。

def read_fifo(self): wr_ptr = self.i2c.readfrom_mem(self.addr, 0x04, 1)[0] rd_ptr = self.i2c.readfrom_mem(self.addr, 0x06, 1)[0] num_samples = (wr_ptr - rd_ptr) & 0x1F if num_samples == 0: return [] data = self.i2c.readfrom_mem(self.addr, 0x07, num_samples * 6) samples = [] for i in range(num_samples): red = (data[i*6] << 16 | data[i*6+1] << 8 | data[i*6+2]) & 0x3FFFF ir = (data[i*6+3] << 16 | data[i*6+4] << 8 | data[i*6+5]) & 0x3FFFF samples.append((red, ir)) return samples

这里有个细节:18 位数据在 3 个字节里存储时,高 6 位是无效的,所以要和 0x3FFFF 做与运算把无效位清掉。不清的话数据会偏大很多,导致后续计算全错。

注意:读 FIFO 之前一定要先读写指针和读指针,不能直接读数据寄存器。直接读数据寄存器不会自动推进读指针,会导致重复读取同一组数据。另外,读指针在读取数据后会自动更新,不需要手动写回。

3.3 心率计算:从原始信号到 BPM

拿到原始红外数据后,不能直接拿来做峰值检测,因为信号里包含直流分量(DC)和交流分量(AC)。直流分量来自组织、骨骼、静脉血等非搏动成分,交流分量才反映动脉血的搏动。所以第一步是去直流,通常用滑动平均或者一阶高通滤波。

我常用的是一个简单的滑动窗口平均法:维护一个长度为 N 的窗口,每次新数据进来就替换最老的数据,然后计算窗口内平均值作为直流估计,原始值减去这个平均值就得到交流信号。N 取采样率的 0.5 到 1 倍比较合适,100Hz 采样的话 N 取 50 到 100。

去直流之后,信号会围绕零点波动。接下来做峰值检测:当信号从负变正且超过某个阈值时,认为检测到一个峰值。两个峰值之间的时间间隔就是心跳周期,60 除以周期(秒)就是心率。

class HeartRateDetector: def __init__(self, sample_rate=100): self.sample_rate = sample_rate self.buffer = [] self.buffer_size = sample_rate self.last_peak_time = 0 self.threshold = 1000 self.bpm = 0 def update(self, ir_value, timestamp): self.buffer.append(ir_value) if len(self.buffer) > self.buffer_size: self.buffer.pop(0) if len(self.buffer) < self.buffer_size: return dc = sum(self.buffer) / len(self.buffer) ac = ir_value - dc if ac > self.threshold and timestamp - self.last_peak_time > 300: if self.last_peak_time > 0: interval = timestamp - self.last_peak_time self.bpm = 60000 / interval self.last_peak_time = timestamp

阈值设 1000 是个经验值,实际要根据信号幅度调整。如果发现心率跳动很大,通常是阈值太低导致噪声被误判为峰值,或者太高导致漏检。可以加一个自适应阈值:根据最近几秒的信号幅度动态调整。

3.4 血氧饱和度估算原理与实现

血氧饱和度的计算基于红光和红外光吸收比值的经验公式。氧合血红蛋白和还原血红蛋白对红光和红外光的吸收特性不同:氧合血红蛋白对红外光吸收少、对红光吸收多;还原血红蛋白相反。通过计算 R = (AC_red / DC_red) / (AC_ir / DC_ir),再代入经验公式 SpO2 = 110 - 25 * R,就能估算出血氧值。

这个公式是经验性的,不同厂家的传感器、不同个体的手指厚度、不同环境光条件都会影响结果。所以血氧值只能作为参考,不能用于医疗诊断。实际使用中,R 值通常在 0.4 到 1.0 之间,对应 SpO2 从 100% 到 85% 左右。

def calculate_spo2(red_ac, red_dc, ir_ac, ir_dc): if red_dc == 0 or ir_dc == 0: return 0 r = (red_ac / red_dc) / (ir_ac / ir_dc) spo2 = 110 - 25 * r return max(0, min(100, spo2))

实操心得:血氧值对信号质量非常敏感。手指按得太轻,信号弱,R 值波动大;按得太重,血流受阻,读数也会偏低。最好的状态是手指自然覆盖传感器窗口,不施加明显压力。另外,环境光太强的时候,光电探测器会饱和,读数完全不可信。可以在传感器上方加一个遮光罩,或者用手掌稍微遮挡一下。

4. OLED 显示驱动与界面设计

4.1 SSD1306 初始化与 I2C 通信

0.96 寸 OLED 模块的核心是 SSD1306 驱动芯片,它支持 I2C 和 SPI 两种接口。I2C 版本的地址通常是 0x3C 或 0x3D,具体看模块上的电阻配置。初始化序列比较长,包括关闭显示、设置时钟分频、设置多路复用比、设置显示偏移、设置起始行、设置电荷泵、设置内存寻址模式、设置段重映射、设置 COM 扫描方向、设置对比度、设置预充电周期、设置 COM 引脚配置、设置对比度、设置显示模式等。

MicroPython 下直接用ssd1306库最省事:

from machine import Pin, I2C import ssd1306 i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000) oled = ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3C) oled.fill(0) oled.text("Hello", 0, 0) oled.show()

Arduino 下用Adafruit_SSD1306Adafruit_GFX库:

#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> Adafruit_SSD1306 display(128, 64, &Wire, -1); void setup() { Wire.begin(21, 22); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println("Hello"); display.display(); }

4.2 显示布局与刷新策略

128x64 的屏幕空间有限,要合理规划布局。我一般把屏幕分成三行:第一行显示心率,用大号字体;第二行显示血氧,同样大号字体;第三行显示状态信息,比如信号质量或者提示文字。

刷新策略很重要。如果每次循环都全屏刷新,I2C 通信量会很大,影响传感器数据读取。更好的做法是只在数值变化时才刷新,或者固定 5-10Hz 的刷新率。另外,oled.show()操作比较耗时,不要在中断里调用。

last_display_time = 0 display_interval = 200 # ms def update_display(oled, bpm, spo2, timestamp): global last_display_time if timestamp - last_display_time < display_interval: return last_display_time = timestamp oled.fill(0) oled.text("HR:", 0, 0) oled.text(str(int(bpm)), 40, 0) oled.text("SpO2:", 0, 24) oled.text(str(int(spo2)), 60, 24) oled.text("%", 100, 24) oled.show()

4.3 显示汉字与自定义图形

SSD1306 本身不支持汉字,要显示汉字需要自己取模生成点阵数据。常用的方法是使用 PCtoLCD2002 或者在线取模工具,生成 16x16 或 24x24 的字节数组,然后用oled.blit()或者逐像素绘制。

# 16x16 汉字点阵示例("心"字) heart_icon = bytearray([ 0x00,0x00,0x1F,0xF0,0x10,0x10,0x10,0x10, 0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0, 0x00,0x00,0x04,0x40,0x08,0x20,0x10,0x10, 0x20,0x08,0x40,0x04,0x00,0x00,0x00,0x00 ]) def draw_heart(oled, x, y): for row in range(16): for col in range(16): byte_index = row * 2 + col // 8 bit_index = 7 - (col % 8) if heart_icon[byte_index] & (1 << bit_index): oled.pixel(x + col, y + row, 1)

注意:取模的时候要注意字节排列顺序,不同工具生成的格式可能不一样。有的按列优先,有的按行优先;有的高位在前,有的低位在前。用之前先用一个小图案测试一下,确认方向正确再批量生成。

5. 系统联调与常见问题排查

5.1 传感器数据不稳定的排查思路

MAX30102 数据不稳定是最常见的问题,表现包括心率跳动大、血氧值乱跳、信号时有时无。排查的时候按以下顺序来:

先看原始数据。把红光和红外的原始值打印出来,正常应该看到红外值在几万到十几万之间波动,红光值略低一些。如果两个值都是 0 或者都是 0x3FFFF,说明通信有问题或者传感器没正常工作。如果数值很小(几百),可能是 LED 电流太小或者手指没放好。

再看波形。把去直流后的交流信号用串口绘图工具画出来,正常应该看到规律的搏动波形。如果波形杂乱无章,可能是环境光干扰或者电源噪声。可以尝试在传感器和手指之间加一层遮光材料,或者在电源引脚加 100uF 和 0.1uF 的退耦电容。

最后看算法参数。阈值、窗口大小、峰值间隔这些参数都需要根据实际信号调整。没有一套参数能适应所有情况,必须根据实际波形来调。

现象可能原因解决方法
原始值全 0I2C 通信失败检查接线、地址、上拉电阻
原始值全 0x3FFFF传感器饱和降低 LED 电流、遮光
心率跳动大阈值不合适调整阈值或加自适应
血氧值偏低手指压力大放松手指、调整位置
数据时有时无接触不良检查杜邦线、焊接点
OLED 不亮地址错误扫描 I2C 确认地址
OLED 花屏初始化序列不对检查库版本和初始化代码

5.2 I2C 总线冲突与性能优化

当 MAX30102 和 OLED 挂在同一条总线上时,如果 OLED 刷新太频繁,会占用总线时间,导致传感器 FIFO 溢出。FIFO 溢出后数据会丢失,心率计算就会出现断层。

优化方法有几个:降低 OLED 刷新率到 5Hz 以下;提高 I2C 时钟频率到 400kHz 甚至 1MHz(如果模块支持);把 OLED 和传感器分到两条不同的 I2C 总线上。ESP32 有两个 I2C 控制器,可以一个接传感器,一个接屏幕,彻底避免冲突。

# 双 I2C 总线配置 i2c_sensor = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000) i2c_oled = I2C(1, scl=Pin(19), sda=Pin(18), freq=400000)

实操心得:我一开始把两个设备挂一条总线,OLED 刷新率设了 20Hz,结果心率数据经常断。后来把刷新率降到 5Hz,情况好转但偶尔还是丢数据。最后改成双总线,问题彻底解决。如果你的项目对实时性要求高,强烈建议用双总线方案。

5.3 电源噪声与信号质量提升

ESP32 的 Wi-Fi 和蓝牙工作时会产生较大的电源噪声,如果传感器和主控共用一路电源,噪声会耦合到模拟信号里。表现就是心率波形上叠加了高频毛刺,峰值检测容易误判。

解决办法:在 MAX30102 的 VIN 和 GND 之间就近加一个 100uF 电解电容和一个 0.1uF 陶瓷电容;如果条件允许,用独立的 LDO 给传感器供电;在软件上增加数字滤波,比如 5 点滑动平均或者 IIR 低通滤波。

class LowPassFilter: def __init__(self, alpha=0.1): self.alpha = alpha self.value = 0 def update(self, new_value): self.value = self.alpha * new_value + (1 - self.alpha) * self.value return self.value

alpha 越小,滤波效果越强,但响应越慢。对于心率信号,alpha 取 0.1 到 0.3 比较合适。

5.4 常见问题速查表

问题排查步骤解决方案
扫描不到 I2C 设备测电压、测上拉、换线检查供电和接线
MAX30102 读数为 0读 Part ID (0xFF)确认地址和通信
心率始终为 0打印原始数据检查信号幅度和阈值
血氧值超过 100检查 R 值计算限制输出范围
OLED 显示乱码检查初始化序列换库或手动初始化
系统运行一段时间死机检查内存和看门狗加喂狗、优化内存
数据更新太慢检查循环延时减少阻塞操作
手指移动时数据跳变正常现象加运动补偿或提示

6. 从能用到好用:功能扩展与优化方向

6.1 数据滤波与信号质量评估

基础版本能跑起来之后,下一步是提升数据质量。除了前面提到的滑动平均和低通滤波,还可以用中值滤波去掉脉冲噪声,用带通滤波(0.5Hz 到 5Hz)提取心跳频段。信号质量评估可以通过计算信噪比或者信号幅度来判断,质量差的时候在屏幕上显示提示,而不是显示不可信的数值。

def signal_quality(ac_values): if len(ac_values) < 10: return 0 signal_power = sum(v*v for v in ac_values) / len(ac_values) noise_estimate = sum(abs(ac_values[i] - ac_values[i-1]) for i in range(1, len(ac_values))) / (len(ac_values) - 1) if noise_estimate == 0: return 100 snr = 10 * math.log10(signal_power / (noise_estimate * noise_estimate + 1e-6)) return max(0, min(100, snr * 5))

6.2 加入温度补偿与运动检测

MAX30102 内部有一个温度传感器,可以读取芯片温度。虽然它测的是芯片温度不是体温,但可以用来做温度漂移补偿。另外,如果加一个 MPU6050 之类的加速度计,可以检测手指是否在移动,移动时暂停测量或者降低数据可信度权重。

6.3 数据记录与导出

ESP32 的 Flash 可以存一些历史数据,或者通过 Wi-Fi 把数据传到电脑上做进一步分析。MicroPython 下可以用open()写文件,Arduino 下可以用 SPIFFS 或 LittleFS。数据格式建议用 CSV,方便用 Excel 或 Python 分析。

def log_data(bpm, spo2, timestamp): with open('/data.csv', 'a') as f: f.write('{},{},{}\n'.format(timestamp, bpm, spo2))

6.4 低功耗设计与电池供电

如果想让设备便携化,低功耗是必须考虑的。ESP32 支持 light sleep 和 deep sleep,MAX30102 也有低功耗模式。可以在没有手指接触的时候让系统进入休眠,检测到手指再唤醒。OLED 也可以关闭显示或者降低亮度来省电。

实操心得:我在做电池供电版本的时候,发现 Wi-Fi 开启时电流直接飙到 100mA 以上,电池根本撑不了多久。后来把 Wi-Fi 关掉,只用蓝牙按需传输,平均电流降到 20mA 左右,续航从几小时提升到一天多。所以如果你的项目不需要一直联网,果断关掉 Wi-Fi。

6.5 外壳设计与佩戴方式

裸板用起来不方便,可以设计一个简单的外壳。3D 打印是最方便的,没有打印机的话用亚克力板或者塑料盒改造也行。关键是要让手指能稳定地覆盖传感器窗口,同时屏幕朝向用户。我试过用橡皮筋把传感器固定在手指上,虽然丑但效果不错,信号稳定性比手按好很多。

这个项目从零开始做,快的话一个周末能跑通基础功能,但要调到数据稳定可靠,可能需要一到两周的反复调试。最大的坑往往不在代码,而在硬件连接和信号处理。耐心一点,把原始数据打印出来多看波形,比盲目改代码有效得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询