【DigiKey创意大赛】便携生命探测仪05+心电数据解析并在PC显示
[复制链接]
上一帖介绍了驱动BME680并将检测结果显示在屏幕上,然后设计一个操作交互界面,实现用户操作功能。本帖介绍如何获取心电数据并解析协议,最终显示在电脑上。
一、方案和开发板的选择
本次作品为了实现心电采集,我选用了领慧立芯推出的医疗级模拟前端心电采集方案。具体实现采用的是领慧立芯的心电采集评估板,该评估板基于LH001-91设计。
LH001-91是专门针对心电信号采集而开发的医疗级模拟前端,集成了24位Σ-Δ ADC,可编程增益放大器,150Hz带内噪声3μVpp,包括右腿驱动,导电脱落监测等功能的AFE。产品的性能指标优越,其中输入参考噪声2.9 μVpp ,共模抑制比 117dB,内部参考温漂16ppm/℃;静态功耗0.1μW,各项核心指标均达到国际品牌的水平,甚至整体性能优于国际厂商同类型产品,并得到国内医疗客户的专业评测,是目前国内极少能够通过医疗测试的国产芯片。使国内高精度信号链产品有望进入医疗领域,结束被欧美品牌垄断的局面。
更详细资料见如下链接:
下图是我申请到的LH001-91的评估板,主要由AFE(LH001-91),通用MCU和两侧的三个电极组成。
领慧立芯同时提供全套的软件演示方案,下图是基于领慧立芯的评估板获得的心电图实时波形图,心电特征明显,满足医疗级应用。
为了降低主控端的软件复杂度,我还选用了一块ESP32-C6开发板做心电数据解析,该开发板由DFRobot设计研发,型号是FireBeetle 2 ESP32-C6。如下图所示。
FireBeetle 2 ESP32-C6是一款基于ESP32-C6芯片设计的低功耗物联网主控板,适用于智能家居项目。ESP32-C6支持Wi-Fi 6、Bluetooth 5、Zigbee 3.0、Thread 1.3通讯协议,可接入多种通讯协议的物联网网络。FireBeetle 2 ESP32-C6支持Type-C、5V DC、太阳能供电,部署时有更多的供电方式选择。
在本次作品中主要是用到了ESP32-C6的两个串口做协议转换。原理框图如下图。
我在PC端用了一个能显示串口数据波形的软件,ESP32-C6负责从LH001-91评估板接收字符串数据,然后转换成上位机软件能识别的协议,再从另外一个串口发出去。
二、硬件电路连接
LH001-91评估板只有一个type-c接口,采集心电用的板子上的焊盘做电极。为了能跟ESP32-C6板连接,需要将电源和串口飞线出来。为了方便采集心电,也需要引出导联线接口。
电源和串口飞线:电源部分直接从USB的5V网络焊盘上引出电源和接地。串口是从板子上的CH340E芯片TX和RX直接飞线出来,为了防止TX线输出信号线与导致烧IO口,我把PCB走线割断串联了一个100欧姆的电阻。最终将飞线出来的接头汇总到一个PH端子上,并用热熔胶固定好。
心电电极飞线:板子上有留有三个电极的焊盘测试点,我找到一个耳机插头形式的导联线,只需要在板上焊接一个耳机插座即可,这个比较容易,焊好后用热熔胶固定。最终的成品如下图。
- 软件编写
首先是分析LH001-91评估板的程序可知,最终输出的每个采样点数据是一个字符串,如下图所示。
这个字符串中包含心电原始数据,滤波后数据,心率,导联状态等信息,最后以回车换行结尾。数据格式有整型和浮点两种,解析的时候可以都转换成浮点数。实际转换代码如下。
代码1:
#include <Arduino.h>
// 定义一个足够大的数组来存储浮点数
float dataArray[10]; // 假设我们知道数组的最大长度
int dataArrayIndex = 0;
String data = "";
void setup() {
// 初始化串口通信
Serial0.begin(1500000); //pin 16/17 to ECG
Serial1.begin(1500000); //pin 4/5 to PC or Main board
// reserve 200 bytes for the inputString:
data.reserve(200);
}
void loop() {
// 检查是否有数据可读
data = "";
if (Serial0.available() > 0) {
// 读取串口数据
data = Serial0.readStringUntil('\n');
// 移除字符串末尾的换行符
data.trim();
// 使用 split 方法按照逗号拆分字符串
int lastIndex = 0;
for (int i = 0; i < data.length(); i++) {
if (data[i] == ',') {
// 将子字符串转换为浮点数并存储在数组中
String numberString = data.substring(lastIndex, i);
dataArray[dataArrayIndex++] = numberString.toFloat();
lastIndex = i + 1;
}
}
// 处理最后一个元素
String lastNumberString = data.substring(lastIndex);
dataArray[dataArrayIndex++] = lastNumberString.toFloat();
// 重置索引,准备发送数据
dataArrayIndex = 0;
if (0 != dataArray[6]) {
// 将浮点数数组转换回字符串,以逗号分隔,并添加回车符
for (int i = 0; i < 10; i++) {
Serial1.print(dataArray[9 - i]);
if (i < 9) {
Serial1.print(",");
} else {
Serial1.println();
}
}
}
}
}
设计此代码我取了个巧,直接把需求写清楚发给Kimi,然后Kimi会给出一份参考代码,我在参考代码上修改成我需要的形式,省去不少脑细胞。下图是我截取的转换后数据,程序转换效果不错。
然后按照上位机软件的协议要求修改发送代码,上位机显示只需要滤波后的波形数据,心率,导联信息,其他的可以都舍弃掉。改完的代码如下。
代码2:
#include <Arduino.h>
// 定义一个足够大的数组来存储浮点数
float dataArray[10]; // 假设我们知道数组的最大长度
int dataArrayIndex = 0;
String data = "";
unsigned char DataScope_OutPut_Buffer[15] = {0}; //串口发送缓冲区
//函数说明:将单精度浮点数据转成4字节数据并存入指定地址
//附加说明:用户无需直接操作此函数
//target:目标单精度数据
//buf:待写入数组
//beg:指定从数组第几个元素开始写入
//函数无返回
void Float2Byte(float *target,unsigned char *buf,unsigned char beg)
{
unsigned char *point;
point = (unsigned char*)target; //得到float的地址
buf[beg] = point[0];
buf[beg+1] = point[1];
buf[beg+2] = point[2];
buf[beg+3] = point[3];
}
void setup() {
// 初始化串口通信
Serial0.begin(1500000); //pin 16/17 to ECG
Serial1.begin(256000); //pin 4/5 to PC or Main board
// reserve 200 bytes for the inputString:
data.reserve(200);
}
int fps_num = 0;//丢点计数
void loop() {
// 检查是否有数据可读
data = "";
if (Serial0.available() > 0) {
// 读取串口数据
data = Serial0.readStringUntil('\n');
// 移除字符串末尾的换行符
data.trim();
// 使用 split 方法按照逗号拆分字符串
int lastIndex = 0;
for (int i = 0; i < data.length(); i++) {
if (data[i] == ',') {
// 将子字符串转换为浮点数并存储在数组中
String numberString = data.substring(lastIndex, i);
dataArray[dataArrayIndex++] = numberString.toFloat();
lastIndex = i + 1;
}
}
// 处理最后一个元素
String lastNumberString = data.substring(lastIndex);
dataArray[dataArrayIndex++] = lastNumberString.toFloat();
// 重置索引,准备发送数据
dataArrayIndex = 0;
if (fps_num > 8)
{
//丢点处理,防止屏幕刷新过快
DataScope_OutPut_Buffer[0] = '$'; //帧头
Float2Byte(&dataArray[5],DataScope_OutPut_Buffer,1);
Float2Byte(&dataArray[6],DataScope_OutPut_Buffer,5);
Float2Byte(&dataArray[7],DataScope_OutPut_Buffer,9);
DataScope_OutPut_Buffer[13] = 13;
// for (int i = 0; i < 14; i++) {
Serial1.write(DataScope_OutPut_Buffer,14);
// }
fps_num = 0;
}
fps_num ++;
// if (0 != dataArray[6]) {
// // 将浮点数数组转换回字符串,以逗号分隔,并添加回车符
// for (int i = 0; i < 10; i++) {
// Serial1.print(dataArray[9 - i]);
// if (i < 9) {
// Serial1.print(",");
// } else {
// Serial1.println();
// }
// }
// }
}
}
手头没有心电信号模拟仪,只好亲自上阵,电极片贴在胸口,如下图。
期间经过多次调试修改错误,最终实现在PC端显示心电波形。具体效果见开头视频。
数据解析没问题后,下一步就是将这部分整合到主控端,就基本完成本次作品设计了。
|