使用MAX30102模块踩坑记录(硬件与算法)

        之前的一个项目需要用到一款心率血氧传感器,选型选择了MAX30102,在ArduinoIDE或Vscode的PlatformIO插件中可以搜索到使用Arduino框架的MAX3010X库"SparkFun MAX3010x Pulse and Proximity Sensor Library",库中包含了简单读取、心率测算、血氧测算、串口光强绘图等多个demo,用户可以快速完成驱动。我的项目使用了运行Arduino框架的ESP32来驱动MAX30102等传感器,并通过WiFi将处理后的数据发送到OneNET服务器。项目在Vscode+PlatformIO环境中完成开发。

        然而烧录测试时发现,手指不接触sensor时,程序运行正常,手指一接触传感器,上位机立即收不到数据,排错许久后最终发现是硬件上的疏忽,原因总结如下:

Arduino Heart Rate Monitor Using MAX30102 and Pulse Oximetry — Maker Portal

        如图1所示,实际测量时手指很容易按到排针焊点,由于人体相当于nF级别的对地电容,IIC通讯速率下,人体会对SDA或SCL线路产生电平干扰。如图2所示,这个干扰过程相当于IIC高电平经SDA或SCL上的上拉电阻对人体电容充电的过程,导致SDA或SCL线路上本该很陡的矩形波信号边沿变平滑,使得通讯失败。

         下面分析上位机收不到数据的原因,以下是SparkFun官方库中的一个example,可以看到,手指按下后,如果IIC通讯失败,while条件始终为false,对Serial的操作全部被跳过,导致上位机收不到数据。

// ..\examples\Example7_Basic_Readings_Interrupts\Example7_Basic_Readings_Interrupts.ino
void loop()
{
  particleSensor.check(); //Check the sensor, read up to 3 samples

  while (particleSensor.available()) //do we have new data?
  {
    samplesTaken++;

    Serial.print(" R[");
    Serial.print(particleSensor.getRed());
    Serial.print("] IR[");
    Serial.print(particleSensor.getIR());
    Serial.print("] G[");
    Serial.print(particleSensor.getGreen());
    Serial.print("] Hz[");
    Serial.print((float)samplesTaken / ((millis() - startTime) / 1000.0), 2);
    Serial.print("]");

    if (digitalRead(interruptPin) == LOW) //Hardware way of reading interrupts
    {
      Serial.print(" INT!");
    }

    byte flags = particleSensor.getINT1(); //Software way of reading interrupts
    if (flags)
    {
      Serial.print(" I[");
      Serial.print(flags, BIN);
      Serial.print("]");
    }

    Serial.println();

    particleSensor.nextSample(); //We're finished with this sample so move to next sample
  }
}

        以下是SparkFun官方库中,读取MAX30102片上FIFO中IR传感器数据的实现,可以看到,读取失败会返回0值。所以即使MAX30102的环形FIFO已经满了,若手指干扰了IIC通讯,也只能读到0。

// ..\SparkFun MAX3010x Pulse and Proximity Sensor Library\src\MAX30105.cpp
// Report the most recent IR value
uint32_t MAX30105::getIR(void)
{
  // Check the sensor for new data for 250ms
  if (safeCheck(250))
    return (sense.IR[sense.head]);
  else
    return (0); // Sensor failed to find new data
}

        解决办法如下:

        (1)降低IIC通讯速率

        以下是SparkFun官方库对IIC通讯速率的定义,分别为标准模式100Kbps,高速模式400Kbps。

// ..\SparkFun MAX3010x Pulse and Proximity Sensor Library\src\MAX30105.h
#define I2C_SPEED_STANDARD        100000
#define I2C_SPEED_FAST            400000

        通讯速率越高,波形畸变越严重,所以如果发现手指按下后读取数据失败,可以将IIC速率降低为100Kbps,如下所示。

// esp32_oneNET\src\main.cpp
// Initialize MAX30102
void max30102_setup(void)
{
  if (!particleSensor.begin(Wire, I2C_SPEED_STANDARD)) // Use default I2C port, 100kHz speed
    Serial.println("MAX30105 was not found. Please check wiring/power. ");
  delay(1000);
  for (int i = 1; (!particleSensor.begin(Wire, I2C_SPEED_STANDARD)); i++)
  {
    Serial.println(String("Retrying: the ") + String(i) + String(" times for ") + String("MAX30102"));
    delay(1000);
  }
  Serial.println("MAX30105 is activating!");
  // MAX30102初始化配置
  particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); // Configure sensor with these settings
}

        (2)绝缘

        如果IIC总线上还挂载了其他设备,要求IIC必须工作在高速模式,最好在传感器表面垫一层透光率高的绝缘材料,比如透明胶带或保鲜膜。

       另外,笔者认为SparkFun官方库计算心率使用的动态平均值软件滤波算法效果并不好,因此我在自己的项目中使用了FFT算法计算心率,最终计算心率的效果较为准确和稳定,文末附上开源链接:ESP32_ONENET

        期待和大家学习交流

  • 10
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值