×

在OLED屏幕上获取实时心电图

消耗积分:0 | 格式:zip | 大小:0.07 MB | 2022-12-23

康桃花

分享资料个

描述

前段时间,我发布了几个项目,演示了如何从 uECG 设备获取数据 - 但他们有很多混乱的代码,并且仍然只使用其中的基本数据。所以最后我写了一个 Arduino 库,使这种方式更简单,更可靠,这里是:https ://github.com/ultimaterobotics/uECG_library (请注意,您还需要从库管理器安装 RF24 库,如果您想要在这个项目中显示 OLED 上的数据 - 也是 Adafruit 的 SSD1306 库)。

1. 原理图

原理图与使用 nRF24 模块和 OLED 的任何其他项目相同:nRF24 连接到 Arduino 的 SPI 总线(D13、D12、D11)和模块 CS 和 CE 线的两个任意引脚 - 为了方便起见,我选择了 D10 和 D9。唯一重要的一点:nRF24模块必须接3.3V线,不能接5V!在 3.3V 和 GND 之间添加 1uF 或 10uF 电容器也有很大帮助——那些 nRF24 模块需要稳定的电压,而 Arduino 不能总是在其 3.3V 线路上提供,电容器有助于实现这一点。

OLED 通过 I2C - SDA 连接到 A4,SCL 连接到 A5,并由 5V 线路供电。就我而言,OLED 模块具有用于 I2C 协议的内置电阻器。如果您的模块没有它们 - 您需要在 SDA 到 3.3V 和从 SCL 到 3.3V 之间添加 4.7k 电阻,尽管我最近看到的大多数模块已经有了它们。

您可以在下面看到示意图,这是组装项目的照片:

pYYBAGOlH1CASEVbAAosSycB6Kg559.jpg
 

2.代码

uECG 库需要几行代码才能正常运行,即:

在 setup() 中,您需要调用 uECG.begin(pin_cs, pin_ce) - 您需要告诉它哪些引脚号用于 nRF24 CS 和 CE 线路,它将打开模块并在内部将其置于正确的模式。

在 loop() 中,您需要尽可能频繁地调用 uECG.run():uECG 设备会发送大量数据 - 每几毫秒一个数据包 - 如果您在下一次之前不调用 uECG.run()数据包到达,其数据将丢失。这意味着永远不要在循环内调用 delay() 函数,并为需要计时的任务使用 millis() (我在库示例中添加了一个示例)。

该项目代码在库中作为示例提供,也附在下面(如果它看起来太复杂 - 请记住,这里 95% 的代码专用于优化显示绘图,用于简单地将值打印到串行监视器中只需要几行):

#include 
#include 
#include 

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

int rf_cen = 9; //nRF24 chip enable pin
int rf_cs = 10; //nRF24 CS pin

void setup() {
  Serial.begin(115200); //serial output - very useful for debugging
  while(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
  }
  display.display();
  delay(100);
  uECG.begin(rf_cs, rf_cen);
  delay(100);

  // Clear the buffer
  display.clearDisplay();
  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(WHITE); // Draw white text
  display.cp437(true);         // Use full 256 char 'Code Page 437' font
  display.display();
  delay(100);
  Serial.println("after display");
}

uint32_t prev_data_count = 0;
uint32_t prev_displ = 0;

uint8_t ecg_screen[128];
int ecg_screen_len = 128;
float ecg_avg = 0;
float ecg_max = 1;
float ecg_min = -1;
int ecg_size = 40;

int displ_phase = 0;

void loop() 
{
  uECG.run();
  uint32_t data_count = uECG.getDataCount();
  int new_data = data_count - prev_data_count;
  prev_data_count = data_count;
  if(new_data > 0)
  {
    uint32_t ms = millis();
    int16_t ecg_data[8];
    uECG.getECG(ecg_data, new_data);
    for(int x = 0; x < new_data; x++)
      Serial.println(ecg_data[x]);
      
    for(int x = new_data; x < ecg_screen_len; x++)
      ecg_screen[x-new_data] = ecg_screen[x];
    for(int x = 0; x < new_data; x++)
    {
      ecg_avg *= 0.99;
      ecg_avg += 0.01*ecg_data[x];
      ecg_max = ecg_max*0.995 + ecg_avg*0.005;
      ecg_min = ecg_min*0.995 + ecg_avg*0.005;
      if(ecg_data[x] > ecg_max) ecg_max = ecg_data[x];
      if(ecg_data[x] < ecg_min) ecg_min = ecg_data[x];
      int ecg_y = 63-ecg_size*(ecg_data[x] - ecg_min) / (ecg_max - ecg_min + 1);
      ecg_screen[ecg_screen_len-1-new_data+x] = ecg_y;
    }

    if(ms - prev_displ > 30)
    {
      prev_displ = ms;
      if(displ_phase == 0)
      {
        display.clearDisplay();
        display.setCursor(0, 0);
        display.print("BPM: ");
        display.println(uECG.getBPM());
        display.print(" RR: ");
        display.println(uECG.getLastRR());
        display.print("steps: ");
        display.print(uECG.getSteps());
        int batt_mv = uECG.getBattery();
        int batt_perc = (batt_mv - 3300)/8;
        if(batt_perc < 0) batt_perc = 0;
        if(batt_perc > 100) batt_perc = 100;
        display.drawLine(110, 0, 127, 0, WHITE);
        display.drawLine(110, 10, 127, 10, WHITE);
        display.drawLine(110, 0, 110, 10, WHITE);
        display.drawLine(127, 0, 127, 10, WHITE);
        int bat_len = batt_perc / 6;
        for(int x = 1; x < 10; x++)
          display.drawLine(110, x, 110+bat_len, x, WHITE);
      }
      if(displ_phase == 1) 
      {
        for(int x = 1; x < ecg_screen_len/2; x++)
          display.drawLine(x-1, ecg_screen[x-1], x, ecg_screen[x], WHITE);
      }      
      if(displ_phase == 2) 
      {
        for(int x = ecg_screen_len/2; x < ecg_screen_len-1; x++)
          display.drawLine(x-1, ecg_screen[x-1], x, ecg_screen[x], WHITE);
      }
      if(displ_phase == 3) 
        display.display();
      displ_phase++;
      if(displ_phase > 3) displ_phase = 0;
    }
  }
}

3.处理数据

板载执行大量处理,您可以获得设备计算的各种统计数据:BPM、GSR、最后 RR 间隔、HRV 参数和 16 个 HRV 箱(第一个箱表示变化 <1% 的节拍量,第二个箱 - 1% 到 2% 之间的变化等),步行的步数,加速度计读数(虽然刷新率很低,所以它只适用于姿势估计)。

但是您也可以获得原始 ECG 读数 - 数据流并不完美,有时会丢失一些数据包,但您仍然可以获得可用的东西:

poYBAGOlH2iARbhHAAJC6xvKoWY979.png
 

好吧,就是这样 - 如果你让这个设备在角落里收集灰尘,现在它实际上可以正常工作了:)


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !