实时心跳 LED 可视化:基于 uECG 与 Arduino Nano 的心率光环
项目缘起
一直想做一个能跟随真实心跳闪烁的 LED 装置——不仅限于静止状态,运动中也能准确捕捉。尝试多年未果,直到发现 uECG:一款开源可穿戴 ECG 设备,提供 Arduino 兼容的数字心跳输出引脚(每次心跳输出高/低电平翻转),极大简化了信号处理。
硬件架构
| 组件 | 数量 | 说明 |
|---|---|---|
| uECG 设备 | 1 | 胸部贴片式 ECG,DRV 引脚输出心跳数字信号 |
| Arduino Nano R3 | 1 | 主控,读取心跳信号驱动 LED 环 |
| NeoPixel LED 环 (WS2812, 16 颗) | 1 | DI 接 D11,彩色指示心率区间 |
| 1000mAh LiPo 电池 | 1 | 直连 5V/GND 供电(不走稳压器) |
电路连接
- uECG DRV → Arduino D3(需外接上拉,代码中开启内部上拉)
- NeoPixel DI → Arduino D11
- 电池 + → Arduino 5V、NeoPixel 5V、uECG VCC
- 电池 - → Arduino GND、NeoPixel GND、uECG GND
关键点:LiPo 直供 5V 引脚(绕过稳压器),避免压降导致 Arduino 复位。LiPo 满电 4.2V,放电至 3.4V 仍有 15% 电量,足够稳定工作。

算法核心:滑动窗口 BPM 计算
- 维护 20 个心跳时间戳的循环数组
- 新心跳到来时,移入数组,计算首尾时间差 Δt
- BPM = 20 × 60000 / Δt
- 窗口越大 → 稳定性越好、响应越慢;可在代码中调整
BEAT_HIST
视觉映射策略
| 心率区间 | 颜色 | 点亮 LED 数 |
|---|---|---|
| < 90 BPM | 蓝 | BPM/10 个(如 60→6 个) |
| 90-110 | 蓝→绿渐变 | 同上 |
| 110-130 | 绿→黄渐变 | 同上 |
| 130-150 | 黄→粉渐变 | 同上 |
| > 150 | 红 | 同上 |
- 心跳间期(非 QRS 波)亮度降为 25%,形成"脉动"效果
- 最大亮度 120/255,避免刺眼
完整代码
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 11
#define NUMPIXELS 16
int in_pin = 3;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
#define BEAT_HIST 20
long beats[BEAT_HIST];
void push_beat(long ms) {
for (int x = 0; x < BEAT_HIST-1; x++) beats[x] = beats[x+1];
beats[BEAT_HIST-1] = ms;
}
int get_bpm() {
long dt = beats[BEAT_HIST-1] - beats[0];
return BEAT_HIST * 60000 / dt;
}
long last_pix_upd = 0;
int prev_in_state = 0;
void setup() {
pixels.begin();
pinMode(in_pin, INPUT);
digitalWrite(in_pin, 1); // 关键:启用内部上拉,uECG 无内部上拉
}
void loop() {
long ms = millis();
int in_state = digitalRead(in_pin); // 1=无心跳, 0=心跳发生
if (in_state == 1 && prev_in_state == 0) push_beat(ms);
prev_in_state = in_state;
if (ms - last_pix_upd > 10) {
last_pix_upd = ms;
int bpm = get_bpm();
int max_bright = 120;
float dd = 20;
float t1 = 90, t2 = t1+dd, t3 = t2+dd, t4 = t3+dd;
int r, g, b;
if (bpm < t1) { r=0; g=0; b=max_bright; }
else if (bpm < t2) { r=0; g=max_bright*(bpm-t1)/dd; b=max_bright-g; }
else if (bpm < t3) { r=max_bright*(bpm-t2)/dd; g=max_bright-r; b=r/4; }
else if (bpm < t4) { r=max_bright; g=0; b=max_bright/2 - max_bright*(bpm-t3)/(2*dd); }
else { r=max_bright; g=0; b=0; }
if (in_state) { r*=0.25; g*=0.25; b*=0.25; } // 非心跳期暗化
int on_pixels = (bpm + 5) / 10;
for (int i=0; i
可穿戴组装建议
- Arduino Nano 尺寸刚好嵌入 LED 环内圈,电池贴附旁边
- uECG 贴胸部,通过带连接器的导线与主体连接(先贴传感器,再穿衣服,最后插拔连接器)
- 建议所有连接焊接固定,运动场景下杜邦线极易脱落

实测与改进方向
- 步行/静止:心率检测准确
- 跑步:电池跳动干扰 ECG 传感器,导线拉扯导致读数失真
- 胸前 LED 视觉反馈不便查看,计划改为手腕手环形式
- 第二版已规划:无线传输(nRF24/蓝牙)分离传感器与显示端
资源链接
- uECG 项目主页:Hackaday.io(含 PCB、固件、技术讨论)
- 本项目代码:文中完整列出
制造工艺