STONE串口屏通风器项目:人机界面设计, 控制系统开发、硬件连接及命令测试

简介: 该睡眠呼吸机屏幕界面采用5.6英寸STONE串口屏幕,内置背光,色彩丰富,配备串口接口便于与主控制板通信,触摸屏输入方式省去了键盘,使整个呼吸机外观更加简洁大方。可调节的压力值和延迟时间采用滑动调节方式,操作更加便捷。

记住“遇见沃弗斯”?如图(1)所示,在这篇长篇故事中,作者Roku Mu Xue讲述了孤独的老人李肯辰、他的女儿大宝和狗狗杜宝的故事。李肯辰最初并不喜欢狗狗杜宝, 他的女儿离家到北方工作,只有狗杜包一直陪伴着他;但狗总是半夜把他叫醒,后来得知老人李肯辰患有阵发性睡眠呼吸暂停综合征,狗杜包叫醒他是为了救他的命!

Meet the Woofers
认识“汪星人

如果你没有这么聪明的狗狗,可以使用天使睡眠呼吸机(图2)。它可以通过在睡眠中持续施加正压(CPAP模式)打开阻塞的气道,帮助预防睡眠呼吸暂停——不同体重可调节压力值,详见图(3)。你还可以设置15-60分钟的延迟压力提升(演示时可使用秒代替分钟以直观观察效果),确保在入睡后再逐步提升气压,避免高气压突然启动影响睡眠。

Figure (2) Main interface of sleep ventilator
图(2)睡眠呼吸机的界面
Figure (3) CPAP mode air pressure adjustment reference values
图(3)CPAP模式气压调节参考值

该睡眠呼吸机的显示界面采用5.6英寸STONE串口触摸屏,带背光,色彩丰富,并配备串口便于与主控制板通信,触摸输入消除了键盘,使整个呼吸机外观更加简洁大方。可调节的压力值和延迟时间为滑动选项——图(4),操作更便捷。此外,延迟时间倒计时和压力表显示值在专用界面中显示——图(5),直观清晰。开发过程记录如下。

Figure (4) Parameter adjustment interface
图(4)参数调整界面
Figure (5) Pressure and countdown ramp-up display screen
图(5)压力与倒计时渐变显示屏

描述

  • 人机界面(HMI)和控制创建说明
  • 界面下载
  • 硬件连接和命令测试
  • 调试代码

1、呼吸机屏幕设计和控制创建说明

如图 (2) 所示,通过“确定”按钮进入呼吸机参数调整界面,上下滑动调整左侧的空气压力值,同时上下滑动调整右侧的延迟增压时间。点击“启动”按钮开始增压,或延迟增压,红色“停止”按钮可随时退出或停止增压。按下 “压力表”按钮进入压力显示和时间延迟倒计时显示界面。

可在按钮属性页面调整参数:round_rsdius: 50,以实现圆形和弧形按钮。图(6)展示了“停止”按钮的属性截图。

Figure (6) Parameter design of the circular button
 图 (6) 圆形按钮的参数设计

字体在 STONE 显示屏上会以小写形式显示,而在 PC 上会显示得更大,因此请在 PC 上将字体大小设计得尽可能大,以免下载到屏幕后显得过小,导致需要多次调整字体大小。

文本选择器设计时,中间高亮部分的字体比两侧更大,颜色为红色以突出显示,例如ts11属性参数如图(7)所示。实际效果如图(4)所示。

Figure (7) Air pressure selector parameter design

图(7) 气压选择器参数设计

Figure (8) Pressure gauge pointer properties
图 (8) 压力表指针属性

指针仪表 gauge_pointer 控件的图标样式在图 (8) 中选择, 指针的锚点由 anchor_x、anchor_y 设置,范围为 0.0 – 1.0。角度设置,当前在 PC -140 指向空气压力 0,在屏幕上,原始位置在中间 0 度,需要程序命令重新指定。

图 (2) 和图 (3) 界面中,涉及控件名称、范围及编程所需内容以统一方式展示,便于编程时快速查阅。

编程控件名称

图 (2) 参数调整界面:

启动按钮名称 = b11

停止按钮名称 = b12

压力表按钮名称 = b13

延迟压力提升选择按钮名称 = cb1

压力选择名称 = ts11 范围 1-20

延迟时间选择名称 = ts12 范围 1-60

图 (3) 压力和倒计时显示屏:

压力表指针名称 = gp1 压力值 0: -140, 20: -110

分钟倒计时名称 = l21

秒倒计时名称 = l22

2、接口下载

点击主菜单下的“调试”—“下载”,选择呼吸机文件夹,系统将生成一个与项目同名的子文件夹(每次下载前需将最后生成的文件夹重命名,否则会返回错误);

检查 STONE 串口屏幕背面的拨动开关至 “设备”位置,使用USB通信电缆直接连接电脑与显示屏的USB接口,电脑将弹出新盘符,将刚生成的项目同名子文件夹/default/复制到 显示屏存储目录下的“raw”文件夹。

3、硬件连接与命令测试

编程前需验证待用命令。按图(9)连接,检查STONE反馈,通过串口助手验证命令有效性。

本项目涉及五种控制类型:

  • 按钮
  • 标签
  • 文本选择器
  • 指针仪表
  • 复选框

我们将逐一测试并验证它们。

Figure (9) Serial Assistant test adapter board jumper diagram

图 (9) 串口助手测试适配器板跳线图

按钮相关:

0x1001 按钮键值主动发送

键值(数据段的最后一个字节):

0x01 按下按钮

0x02 松开按钮(按钮触发点击事件完成)

示例:

参数屏幕的“开始”按钮(名称 = b11)被按下以主动发送命令。

ST<0x10 0x01 0x00 0x04 b11 0x01>ET

串行助手返回十六进制数据: 验证通过

53 54 3C 10 01 00 04 62 31 31 01 3E 45 54 5C 0B

按钮释放(按钮点击动作完成) 激活命令 —- 作为一种方式 62 31 31 02 解码可行!

ST<0x10 0x01 0x00 0x04 b11 0x02>ET

串口助手返回十六进制数据:验证通过

53 54 3C 10 01 00 04 62 31 31 02 3E 45 54 18 0B

注:按钮 b11(ASCII 码 0x62 0x31 0x31)。

标签相关:

set_text 设置标签显示的文本

示例:

设置文本: (最小倒计时名称 = l21)

ST<{“cmd_code”: “set_text”, “type”: “label”, “widget”: “l21”, ‘text’: “29”}>ET 验证顶部屏幕是否正常

文本选择器相关:

0x1081 文本选择器值已发送 (整数类型 主动发布:选择器调整后立即发布)

数据格式:

值:数据段的最后四个字节

示例:(延迟提升时间最小选择器控制名称 = ts12,选项:1-60,当前值 16,当值为 20 时重新选择)

串行助手接收的十六进制数据为。

53 54 3C 10 81 00 08 74 73 31 32 00 00 00 14 3E 45 54 F8 4E OK

屏幕主动给出指令如下:

ST<0x10 0x81 0x00 0x08 ts12 0x00 0x00 0x00 0x00 0x14 >ET ts11 当前值:20 = 0x14

与指针相关的参数:

set_angle 设置指针的旋转角度(整数类型)

示例:(气压计指针名称 = gp1)

设置指针角度参数:以下操作已验证通过

ST<{“cmd_code”: “set_angle”, “type”: “gauge_pointer”, “widget”: ‘gp1’, “angle”:-140}>ET

ST<{“cmd_code”: “set_angle”, “type”: “gauge_pointer”, “widget”: “gp1”, “angle”:30}>ET

ST<{“cmd_code”: “set_angle”, “type”: “gauge_pointer”, “widget”: ‘gp1’, “angle”:30}>ET

ST<{“cmd_code”: “set_angle”, “type”: “指针仪表”, “widget”: “gp1”, “angle”:130}>ET 指针“, ”widget“: ‘gp1’, ”angle”:130}>ET

ST<{“cmd_code”: “set_angle”, “type”: “gauge_pointer”, “widget”: “gp1”, “angle”:-110}>ET

ST<{“cmd_code”: “set_angle”, “type”: “gauge_pointer”, “widget”: ‘gp1’, “angle”:0}>ET

ST<{“cmd_code”: “set_angle”, “type”: “gauge_pointer”, “widget”: ‘gp1’, “angle”:270}>ET

复选框相关:

当状态发生变化时,多选按钮会主动发送。

0x1020 复选按钮值变化后主动发送

示例:(延迟增益选择旋钮名称 = cb1)

值更改时接收的“未选中”串行助手。

53 54 3C 10 20 00 04 63 62 31 00 3E 45 54 AC 46

等同于指令:

ST<0x10 0x20 0x00 0x0D cb1 0x00 >ET 未选中

当值发生变化时,“已选中”串行助手接收。

53 54 3C 10 20 00 04 63 62 31 01 3E 45 54 50 47

等效于指令:

ST<0x10 0x20 0x00 0x0D cb1 0x01 >ET selected

经过上述命令测试,下一步可以放心地编程实现该功能。当然,首先仍需确保硬件连接正确。我们使用 ESP32 开发板实现与 STONE 串口屏幕的通信。

如图 (10) 所示,NodeMCU-32S 开发板的 TX0、RX0 和 GND 需与 STONE 屏幕适配器板对应的引脚连接,以完成 HMI 信息交互。

STONE屏幕接口为232信号,通过适配板进入MCU接口需使用TX和RX信号,以实现电平匹配。

图(10)左侧为NodeMCU-32S开发板,蓝色(TX0)、红色(RX0)引脚连接至适配板跳线位置(蓝色跳线移除后),黑色为GND。

Figure (10) NodeMCU-32S development board and adapter board communication connection diagram
图(10)NodeMCU-32S开发板与适配器板通信连接示意图

4、调试代码

本项目重点在于按钮解码、多选按钮及文本选择器的处理,以及气压计指针驱动算法、倒计时计时器和延迟升压功能的实现。

启动气压延迟升压过程, ESP32板蓝色LED将闪烁提示,气压显示接口的气压指针将模拟均匀升压至目标气压位置(表中16cmH2O对应160),延迟倒计时将以每秒递减的方式动态显示 (为直观设置1分钟倒计时,实际使用时建议设置15分钟至60分钟)。

以下录制代码已验证,请参考视频查看运行效果。

/*

Frank 2022年5月3日为睡眠呼吸机开发

使用NodeMCU-32s ESP32在Arduino 1.8.13环境下

使用深圳STONE HMI

整数类型:int(-32768,32767)无符号整数:unsigned int(0,65535)

按钮:

启动—“b11”—0x62 0x31 0x31

停止—“b12”—0x

文本选择器:

压力—‘ts11’—0x74 0x73 0x31 0x31

延迟时间–“ts12”—

复选框:

延迟增强—“cb1”—0x63 0x62 0x31

标签:

分钟—“l21”—

秒 – ‘l22’ –

指针:—“gp1”–

*/

//——睡眠通气机使用———-2022.05.03——

int delayTimeMin = 1; //延迟压力增压的最小值

int delayTimeSec = 0;

int inPressure = 20; // cmH2O 设置

int nowPressure = 0; // 当前 cmH2O

int start = 0; // 1 = 开始,0 = 停止

int dealyedPressure = 1; // 1 = 选择器,0 = 不延迟

int minShow,secShow,pointerShow;

float m1,m2,m3;

int i1,i2,i3;

//——时间计数器使用——

int timeON = 0; // 0 = 关闭,1 = 打开

int ii = 0;

unsigned int time_1s=0;

unsigned int time_1m=0;

unsigned int time_1h=0;

int iii = 0;

int LED_blue = 2; // IO2,控制LED点亮(高电平为点亮)

void setup() {

Serial.begin(115200);

pinMode(LED_blue, OUTPUT);

}

void loop() {

int inChar;

// 读取串口发送的信息并简单解码:

if (Serial.available() > 0) {inChar = Serial.read();}

if (inChar == 0x62) // 2 个按钮

{

if (Serial.available() > 0){inChar = Serial.read();}

if (inChar == 0x31)

{

if (Serial.available() > 0){ inChar = Serial.read();}

如果 (inChar == 0x31)

{

如果 (Serial.available() > 0){ inChar = Serial.read();}

如果 (inChar == 0x02)

{

如果(dealyedPressure == 1 && start == 0)

{

time_1s = 0;

time_1m = 0;

timeON = 1;

}

start = 1; //62 31 31 02 表示启动成功!

}

} else if (inChar == 0x32)

{

if (Serial.available() > 0){ inChar = Serial.read();}

if (inChar == 0x02)

{

start = 0; //62 31 32 02 表示停止成功!

时间开启 = 0;

}

}

}

} else if (inChar == 0x63) // 1 个复选框

{

if (Serial.available() > 0){inChar = Serial.read();}

if (inChar == 0x62)

{

if (Serial.available() > 0){ inChar = Serial.read();}

如果 (inChar == 0x31)

{

如果 (Serial.available() > 0){ inChar = Serial.read();}

如果 (inChar == 0x00)

{

dealyedPressure = 0; // 0 = 不延迟

timeON = 0;

} else if(inChar == 0x01)

{

dealyedPressure = 1; // 1 = 选择器,

if(start == 1)

{

time_1s = 0;

time_1m = 0;

timeON = 1;

}

}

}

}

}else if(inChar == 0x74) //2 个文本选择器

{

if (Serial.available() > 0){inChar = Serial.read();}

如果 (inChar == 0x73)

{

如果 (Serial.available() > 0){ inChar = Serial.read();}

如果 (inChar == 0x31)

{

如果 (Serial.available() > 0){ inChar = Serial.read();}

如果 (inChar == 0x31) // 74 73 31 31 是过程

{

如果 (Serial.available() > 0){ inChar = Serial.read();}

如果 (inChar == 0x00)

{

如果 (Serial.available() > 0) { inChar = Serial.read(); }

如果 (inChar == 0x00)

{

如果 (Serial.available() > 0) { inChar = Serial.read(); }

如果 (inChar == 0x00)

{

如果 (Serial.available() > 0) { inChar = Serial.read(); }

inPressure = inChar;

}

}

}

}else if (inChar == 0x32) // 74 73 31 32 是延迟最小值

{

if (Serial.available() > 0){ inChar = Serial.read();}

if (inChar == 0x00)

{

如果 (Serial.available() > 0) { inChar = Serial.read(); }

如果 (inChar == 0x00)

{

如果 (Serial.available() > 0) { inChar = Serial.read(); }

如果 (inChar == 0x00)

{

如果 (Serial.available() > 0) { inChar = Serial.read(); }

延迟时间最小值 = inChar;

}

}

}

}

}

}

}

//———显示——- 分钟——– 秒———

如果 (time_1m < delayTimeMin)

{

minShow = delayTimeMin – time_1m – 1;

secShow = 59 – time_1s;

} else

{

timeON = 0;

}

if(start == 1 && dealyedPressure == 1)

{

Serial.print(“ST<{\”cmd_code\“:\”set_text\“,\”type\“:\”label\“,\”widget\“:\”l21\“,\”text\“:\””);

Serial.print(minShow);

Serial.println(“\”}>ET“);

Serial.print(”ST<{\“cmd_code\”:\“set_text\”,\“type\”:\“label\”,\“widget\”:\“l22\”,\“text\”:\“”);

Serial.print(secShow);

Serial.println(“\”}>ET“);

}else //stop or dealyedPressure == 0

{

Serial.print(”ST<{\“cmd_code\”:\“set_text\”,\“type\”:\“label\”,\“widget\”:\“l21\”,\“text\”:\“”);

Serial.print(delayTimeMin);

Serial.println(“\”}>ET“);

Serial.print(”ST<{\“cmd_code\”:\“set_text\”,\“type\”:\“label\”,\“widget\”:\“l22\”,\“text\”:\“”);

串口打印(delayTimeSec);

串口打印(“”}>ET”);

}

//———–显示——压力——–

如果(delayedPressure == 0 || timeON == 0)

{

pointerShow = inPressure*17 -145;

}else

{

i1 = time_1m*60 + time_1s;

m1 = (float)delayTimeMin * 60.0;

m2 = (float)i1 / m1;

m3 = (float)inPressure * 17.0 * m2;

pointerShow = int(m3) – 145;

}

if(start == 1)

{

Serial.print(“ST<{\”cmd_code\“:\”set_angle\“,\”type\“:\”gauge_pointer\“,\”widget\“:\”gp1\“,\”angle\“:”);

Serial.print(pointerShow);

Serial.println(“}>ET”);

}else //stop

{

Serial.println(“ST<{\”cmd_code\“:\”set_angle\“,\”type\“:\”gauge_pointer\“,\”widget\“:\”gp1\“,\”angle\“:-145}>ET”);

}

//——————时间++ ——–开始———

if(时间ON == 1)

{

延迟(10);

ii += 1;

if(ii > 29) //秒++

{

ii = 0;

时间_1s += 1;

if(时间_1s >= 60) //分钟++

{

time_1s = 0;

time_1m += 1;

if(time_1m >= 60) //hour++

{

time_1m = 0;

time_1h += 1;

}

}

if(iii == 0){

iii = 1;

digitalWrite(LED_blue, HIGH); // 打开 LED(HIGH 表示电压为高电平)

}else

{

iii = 0;

digitalWrite(LED_blue, LOW); // 通过将电压设为低电平关闭 LED

}

}

}

//——————时间递增 ——–结束———

}

解码说明。

气压计指针驱动算法涉及整数与浮点数之间的运算,需严格遵循以下运算格式规范,否则将无法获得正确结果。

    滚动至顶部