You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Itsy Bitsy nRF52840实现BLE低功耗蓝牙下125Hz速率的PPG传感器实时数据传输?

如何基于Itsy Bitsy nRF52840实现BLE低功耗蓝牙下125Hz速率的PPG传感器实时数据传输?

首先得揪出问题的核心:你现在用bleuart.println()把每个样本转成带换行的字符串发送,这简直是带宽杀手!一个16位的整数转成字符串再加上换行,至少要5-6个字节,还得单独发一个BLE包——每个BLE包本身还有几十字节的协议开销,1250个样本就要发1250个小包,这效率能不低吗?4分钟的传输时间就是这么来的。另外,之前实时传输出现信号间隙,大概率是因为你每次只读一个样本就立刻发BLE,I2C读取和BLE发送的时序冲突,导致传感器的下一个样本没及时读取而丢失了。

下面给你一套完整的优化方案,既能实现实时125Hz传输,又能保证信号无间隙,还能把传输速度拉满:


核心优化思路

  1. 抛弃字符串,用二进制传输:直接发送16位整数的原始字节,每个样本只占2字节,数据量直接砍到原来的1/3甚至更少。
  2. 利用传感器FIFO批量读样本:MAX30105自带32级FIFO缓冲区,传感器会自动把采样数据存在FIFO里,哪怕MCU暂时在处理BLE,也不会丢样本,我们可以一次读取多个样本再批量发送,彻底解决信号间隙问题。
  3. 优化BLE连接参数:调小连接间隔,让BLE链路的响应更及时,适配实时传输的需求。
  4. 用批量发送代替单样本发送:一次发多个样本的二进制数据,减少BLE包的数量,大幅提升传输效率。

修改后的完整代码

#include <bluefruit.h>
#include <Wire.h>
#include "MAX30105.h"

MAX30105 particleSensor;
BLEUart bleuart;

// 定义二进制传输的临时缓冲区(适配BLE MTU,默认23字节,这里设32字节存16个样本)
#define TX_BUF_SIZE 32
uint8_t tx_buf[TX_BUF_SIZE] = {0};
uint8_t tx_buf_idx = 0;

void setup() 
{
  Serial.begin(230400);
  Serial.println("Initializing...");

  Wire.begin();
  
  // 初始化传感器
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) 
  {
    Serial.println("ERROR: MAX30102 not found.");
    while (1);
  }
  Serial.println("MAX30105 initialized.");

  // 优化传感器配置:开启FIFO,设置采样率125Hz,配置FIFO阈值
  particleSensor.setup(0x1F, 8, 2, 3200, 69, 4096); // 维持你的采样参数
  particleSensor.enableFIFO(); // 开启FIFO功能
  particleSensor.setFIFOAlmostFull(28); // FIFO存到28个样本时可以触发读取(可选)
  particleSensor.setFIFOOverflowEnabled(true); // 溢出时标记状态
  particleSensor.clearFIFO(); // 初始化清空FIFO

  // 优化BLE参数
  Bluefruit.begin();
  Bluefruit.setTxPower(4);
  // 缩小连接间隔:7.5ms - 15ms(单位1.25ms), slave latency设0,保证实时响应
  Bluefruit.Periph.setConnInterval(6, 12);
  Bluefruit.Periph.setConnSlaveLatency(0);
  Bluefruit.Periph.setConnSupervisionTimeout(400); // 超时时间400ms
  Bluefruit.setTxMTU(256); // 协商更大的MTU,提升批量发送效率

  bleuart.begin();
  startAdv();
  Serial.println("Setup complete, ready for real-time transmission!");
}

void startAdv(void)
{
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.Advertising.addService(bleuart);
  Bluefruit.Advertising.addName();
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);
  Bluefruit.Advertising.setFastTimeout(30);
  Bluefruit.Advertising.start(0);
}

void loop() {
  // 读取FIFO中所有可用的样本
  int available_samples = particleSensor.getFIFOAvailableSamples();
  if (available_samples > 0) {
    for (int i = 0; i < available_samples; i++) {
      // 读取传感器IR值
      uint32_t irValue = particleSensor.getIR();
      // 你的压缩逻辑不变
      uint16_t compressedValue = (irValue % 100000) / 10;

      // 把16位整数转成大端字节序(和iOS端约定好即可,小端也可以)
      tx_buf[tx_buf_idx++] = (compressedValue >> 8) & 0xFF; // 高字节
      tx_buf[tx_buf_idx++] = compressedValue & 0xFF; // 低字节

      // 如果缓冲区满了,立刻发送
      if (tx_buf_idx >= TX_BUF_SIZE) {
        bleuart.write(tx_buf, tx_buf_idx);
        tx_buf_idx = 0; // 重置缓冲区索引
      }
    }

    // 发送缓冲区中剩余的未发数据
    if (tx_buf_idx > 0) {
      bleuart.write(tx_buf, tx_buf_idx);
      tx_buf_idx = 0;
    }
  }

  // 可选:处理BLE连接状态,比如调试用
  if (Bluefruit.connected()) {
    // 可以在这里加一些连接状态的调试输出,比如每1秒打印一次传输统计
    static unsigned long last_stats = 0;
    if (millis() - last_stats > 1000) {
      Serial.println("Real-time transmission active, connected.");
      last_stats = millis();
    }
  }
}

关键优化点详解

1. 二进制传输的优势

原来每个样本用println()发送,比如12345\n要6字节,现在直接发2字节的原始数据,数据量减少了70%以上。而且BLE可以一次打包多个样本,比如用256字节的MTU,一次能发128个样本,传输效率直接拉满。

2. 传感器FIFO的作用

MAX30105的FIFO会自动把采样到的样本存起来,哪怕MCU在处理BLE发送的瞬间,传感器也在持续采样存FIFO,这样就不会因为时序冲突导致样本丢失,彻底解决了实时传输的信号间隙问题。我们只需要定时读取FIFO里的所有可用样本即可。

3. BLE连接参数优化

  • 连接间隔调小到7.5ms-15ms:让BLE从设备(你的nRF52840)能更频繁地和主机(iOS设备)通信,保证数据能及时发出去。
  • Slave latency设为0:禁止从设备跳过连接事件,确保每一次连接事件都能传输数据。
  • 更大的MTU:协商256字节的MTU,一次能发更多样本,减少包数量。

4. iOS端适配

你的iOS app需要改成解析二进制数据:每次读取2字节,拼接成一个16位整数(注意字节序要和代码里的一致,这里用的是大端),然后按原来的逻辑分10秒块喂给神经网络就行,比解析字符串还简单高效。


额外注意事项

  • 如果还是担心传输延迟,可以开启BLE的“增强型连接”或者用BLE Notify的批量发送(不过BLE UART已经足够用了)。
  • 可以在代码里加一些错误处理,比如检查FIFO溢出状态,如果溢出了就清空FIFO并打印警告,排查是否采样率和读取速度不匹配。
  • 测试时可以用Serial打印每秒读取的样本数,确认是不是稳定在125个左右。

备注:内容来源于stack exchange,提问作者AceKijani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:24:34