采用STONE串口屏便携式监护仪,硬件部门决定使用NXP的32位微处理器芯片作为整机监控、心电图分析及控制中心,通过心电图生物电信号采集与放大、 右腿驱动,导入MIT心电图数据库进行算法处理,同时监测血氧饱和度传感器电信号、血压、呼吸电信号的放大与滤波处理,通过高速波特率通信驱动STONE串口屏实时显示波形及参数变化,并与参考值进行比较以做出判断,监测人体参数变化并报警。若出现范围偏差,系统将自动发出语音提示。
STONE串口屏的新版本完美支持曲线绘制和显示,通信协议简单易记,微控制器工程师阅读手册后即可使用,命令集中有大量驱动程序示例,直接复制修改名称和参数即可使用。串行连接简单,可使用官方适配器板,无论是直接串行连接还是USB转串行,都非常方便易用。 监视器可同时显示多组关键参数,尤其是3条曲线可动态绘制并同时流畅显示,应可支持同时绘制更多组曲线图形,可根据需求尝试。当然,命令集曲线功能已包含同时推送多组数据的示例,但当前硬件实际使用时未响应,希望官方能尽快优化。这可能通过更新固件解决。对于此新版本的曲线绘制功能,我最初尝试了各种调试方法均未成功,最终发现串口屏幕有新固件,刷入新固件后才看到“真容”。看到刷入固件后串口屏幕上流畅显示的ECG曲线,心情大好,哈哈……
该项目的设计图如图(1)所示。界面基于STWI101WT-01串口屏,分辨率为1024*600,左侧显示波形曲线的2/3,右侧显示1/3的数值。
本文将重点介绍曲线图视图的创建与绘制。该设计完全遵循项目演示的实际生产流程,未进行分类整理,可能显得杂乱,但这是真实开发过程的真实体现。
首先,我们确定了心电图波形曲线的绘制方式。

该演示模拟75bpm心率,相当于800ms心搏周期,即每40ms一个点,每20个点构成一个周期。基于整个图表视图控件的X轴被划分为100等分,每屏显示4秒波形,屏幕刷新率为25Hz,看起来非常平滑。该图表视图控件的线系列元素已设置为smooth = false (心电图波形较为尖锐),下包络线未显示,且点标记未显示。区域。参见图(4),Y轴的最小值为0,最大值为140,程序中给定的最大值为130,范围相对较广。请参见图(2)至图(7)以查看其他参数设置,这些参数适用于图表视图1及其元素X轴1、Y轴2、柱形系列1和线形系列1。我们为图表视图控件的背景颜色选择透明的RGBA(0,0,0,0),这将显示底色(黑色),其他控件(如视图)也具有相同特性。






根据上述设置模拟心电图波形(75bpm)的编程代码如下。
首先,定义两个变量如下。
Int num19_1 = 0;
Int num19_2 = 0;
Then generally in the main loop main.c, the ECG curve is depicted by the following code.
delay(10);
num19_1 += 1;
if(num19_1 >= 4){ // Draw one point every 40ms.
num19_1 = 0;
num19_2 += 1;
if(num19_2 == 3){
Serial.println("ST<{\"cmd_code\":\"set_value\",\"type\":\"line_series\",\"widget\":\"line_series1\",\"mode\":\"push\",\"value\" :10}>ET");
}else if(num19_2 == 4){
Serial.println("ST<{\"cmd_code\":\"set_value\",\"type\":\"line_series\",\"widget\":\"line_series1\",\"mode\":\"push\",\"value\" :130}>ET");
}else{
Serial.println("ST<{\"cmd_code\":\"set_value\",\"type\":\"line_series\",\"widget\":\"line_series1\",\"mode\":\"push\",\"value\" :40}>ET");
}
if(num19_2 >= 20){ //every 20 data is a cycle
num19_2 = 0;
}
}
然后需要处理二氧化碳曲线,重点是扫描同步。
该项目中的3条曲线,图表视图2用于血氧传感器(SpO2),图表视图3用于二氧化碳呼吸。
图表视图2和图表视图3的X轴设置为最小值0,最大值100,ECG图表视图1与之相同。为保持同步,曲线扫描范围需一致。图表视图2的Y轴最大值设置为100,因此程序中Y轴的最大值预设为95。算法代码如下:
First, define 3 variables as follows.
Int num19_3 = 0;
Int num19_4 = 0;
Int num19_5 = 0;
The CO2 curve is then generally depicted in the main loop, main.c, by the following code.
num19_3 += 1;
if(num19_3 >= 4){ //one point every 40ms
num19_3 = 0;
num19_4 += 1;
if(num19_4 <= 10){
num19_5 = num19_4*9; //the first 10 points increase linearly
Serial.print("ST<{\"cmd_code\":\"set_value\",\"type\":\"line_series\",\"widget\":\"line_series3\",\"mode\":\"push\",\"value\":" );
Serial.print(num19_5);
Serial.println("}>ET");
}else if(num19_4 <= 40){ // the last 30 points decrease linearly
num19_5 = 95 - (num19_4 - 10)*3;
Serial.print("ST<{\"cmd_code\":\"set_value\",\"type\":\"line_series\",\"widget\":\"line_series3\",\"mode\":\"push\",\"value\":" );
Serial.print(num19_5);
Serial.println("}>ET");
}else{
num19_4 = 0;
Serial.println("ST<{\"cmd_code\":\"set_value\",\"type\":\"line_series\",\"widget\":\"line_series3\",\"mode\":\"push\",\"value\" :5}>ET");
} }


图(8)中的二氧化碳波形是上述程序在属性符号圆角半径为4时对线系列3的影响。我们尝试将符号圆角半径修改为30,希望曲线过渡更加圆滑,但测试结果与图(8)无异,见图(9),这表明当点密度较高时,圆角效果不明显。这只能通过更改点坐标来实现。

让我们通过图 (10) 分析图 (3) 中 X 轴的属性。在图 (3) 中,当 split line 的显示设置为 true 时,会显示长垂直条(分隔条);当 line 的显示设置为 true 时,会显示 X 轴的水平线 (例如顶部图表视图底部的水平线);当 show = true 且 tick = true 时,X 轴水平线下方刻度线的细线将显示;当 show = true 且 label = true 时,X 轴水平线下方(数据填充的值)的数字将显示。当 show = true 时,将显示 X 轴水平线下方(数据填充的值)的数字。这就是全部内容。
现在只剩下 SpO2 曲线部分,决定使用 AD 转换。
使用 ESP32 ADC 进行曲线模拟,该 ADC 为 12 位,满量程为 4096。图表视图 2 的 Y 轴最大值为 255,将 ADC 读取值除以 20 后可满足曲线显示需求。全量程SPO2显示为100%,因此ADC读取值除以20再除以2.55即可在标签2中显示。由于程序涉及整数运算,算法已修正,请参考实际测试通过的程序代码。使用Arduino中的analogRead(32)函数直接读取ESP32的GPIO32(即ADC-CH4)的AD转换值。测试可通过电位器实现,也可直接将 ADC-CH4 引脚连接至 GND、+3.3V 或 +5V,或悬空观察干扰波形(见视频效果:接地时显示低电平,连接 +3.3V 或 +5V 时均为高电平且幅度相同,悬空时出现异常曲线),确保标签2能及时显示 ADC 电压变化。代码和算法代码如下。
//--------ADC-------
int adcPin = 32; // GPIO32, also ADC-CH4
int adcValue = 0;
int num19_6 = 0;
delay(10);
adcValue = analogRead(adcPin); // Read in the value of the AD conversion
adcValue = adcValue/20;
//-----SPO2 curve plotting ------
num19_6 += 1;
if(num19_6 >= 4){ // one point every 40ms
num19_6 = 0;
Serial.print("ST<{\"cmd_code\":\"set_value\",\"type\":\"line_series\",\"widget\":\"line_series2\",\"mode\":\"push\",\"value\":" );
Serial.print(adcValue);
Serial.println("}>ET");
adcValue = (adcValue*10)/21;
Serial.print("ST<{\"cmd_code\":\"set_value\",\"type\":\"label\",\"widget\":\"label2\",\"value\":");
Serial.print(adcValue);
Serial.println("}>ET");
}

参见图 (11) 中的实际图片部分截图,以及图 (12) 中的计算机设计界面。与图 (3) 类似,当图表视图 2 的 X 轴 2 分割线显示为 false 时,图 (12) 中黄色 SPO2 区域中间的分隔条将关闭显示 (与实际画面一致);视频显示的平滑波形可完全实现实时示波器效果。

参考点
STONE 设计师平台图表视图控制的结构如图 (13) 所示。官方用户手册 8.1 中有详细说明,包括每个属性和参数的解释;指令集 4.24 提供了数据推送的方法,见图 (14),程序中使用的指令示例在此处。


- Arduino 的 analogRead() 函数用于模拟 AD 转换。在 Arduino 版本 1.8.13 的菜单中,依次选择“帮助”—“参考”—“学习”—“模拟 I/O”,即可看到 analogRead() 函数的描述,如下所示。
analogRead()
描述
Arduino 板载有一个 6 通道(Mini 和 Nano 型号为 8 通道,Mega 型号为 16 通道)的 10 位模拟-数字转换器。这意味着它将输入电压在 0 到 5 伏特之间映射为 0 到 1023 之间的整数值。这使得两次读取之间的分辨率为 5 伏特 / 1024 个单位,即每单位 0.0049 伏特(4.9 毫伏)。analogReference()。
读取一个模拟输入大约需要 100 微秒(0.0001 秒),因此最大读取速率约为每秒 10,000 次。
语法
analogRead(pin)
参数
pin:要读取的模拟输入引脚编号(大多数板子为0至5,Mini和Nano为0至7,Mega为0至15)
返回
整数(0至1023)
注意
如果模拟输入引脚未连接任何设备,analogRead() 返回的值会因多种因素而波动(例如其他模拟输入的值、手部距离电路板的远近等)。
然而,请注意,ESP32的ADC-CH4为12位,返回值范围为0至4096。如需更多详细信息,请参阅原文中的相关链接及ESP32手册。