串口调试助手开发(04):数据收发开发
视频地址:哔哩哔哩
数据收发功能开发
上一节我们讲解了串口调试助手App 的需求导入,本节我们将讲解 数据收发的开发流程。
在工作台的项目列表中找到 串口调试助手 App,点击【项目菜单】->【开发文档】进入到需求文档页面。
编辑页面信息
- 点击 切换到页面菜单,选中 DataTransfer。

- 点击 编辑 按钮,对页面信息进行修改

功能详情
功能详情填写:
- 串口选择(仅在桌面端存在,手机端默认连接一个串口设备)
- 监测当前平台是否为android,如果是 则隐藏串口选择功能并默认连接第一个串口,否则 显示串口选择功能
- 串口通信设置
- 基本设置,波特率、停止位、数据位、校验位
- 通信协议设置,支持拼接帧头、帧尾、校验位、帧长、控制字,用户只需发送数据位
- 追加符设置,支持追加换行符 0x0D(回车)、 0x0A(换行)
- 日志管理
- 自动滚屏,可以进行设置日志是否自动滚屏
- 字符过滤,在日志中高亮显示内容
- 时间戳拼接,可以进行设置日志是否拼接时间戳
- 日志一键清理
- 日志存储限制,当前窗口显示的数据 不超过 1000条
- 用户每次打开软件,清空日志文件,一旦有数据接收并将日志追加写入到此文件(该文件保留的是全部日志内容)
- 日志保存,实际上就是将 本地的日志文件 复制到 用户指定的位置
- 数据收发
-
支持Hex和文本两种数据格式发送与显示
-
参考页面
放入下图:

变量列表
进入变量列表,点击 添加变量:
| 变量名称 | 变量类型 | 是否为数组 | 默认值 | 变量描述 |
|---|---|---|---|---|
| cycle | Bool | 否 | false | 循环发送 |
| logs | Map | 是 | 日志数据列表 | |
| fileLength | Int | 否 | 0 | 选中的文件大小 |
| fileChunkSizeStr | String | 否 | 56 | 文件分包大小 |
| fileChunkSize | Int | 否 | 56 | 文件分包大小 |
| chunkIndex | Int | 否 | 0 | 当前分块索引 |
| delayed | String | 否 | 20 | 延时发送值,毫秒值 |
| list | Map | 是 | 日志保存的变量记录 | |
| sendData | String | 否 | 发送的数据内容 | |
| hexDisplay | Bool | 否 | true | 是否16进制显示 |
| buChong | String | 否 | 0 | 文件拆包补包 |
| timestamp | Bool | 否 | true | 是否拼接时间戳 |
| filename | String | 否 | 选中的文件名称 | |
| autoscroll | Bool | 否 | true | 是否自动滚动 |
| protocolData | Map | 否 | 过滤协议数据 | |
| hexSend | Bool | 否 | true | 是否16进制发送 |
| newLine | Bool | 否 | true | 是否添加新行 |
| baudRate | String | 否 | 115200 | 波特率 |
| dataBits | String | 否 | 8 | 数据位 |
| stopBits | String | 否 | 1 | 停止位 |
| serialData | Map | 否 | 串口数据 | |
| serialPort | Var | 否 | 桌面端串口连接对象 | |
| _dropDownValue | String | 否 | 选择的串口端口值 | |
| availablePorts | String | 是 | 可用的串口端口列表 |
配置流程
在流程设计中,填写以下流程:
-
页面初始化:开始串口扫描,默认连接索引为0的设备,配置串口数据监听
-
发送按钮:将输入框中的内容通过串口发送给硬件
点击保存按钮保存数据
页面搭建
1. 点击右上角 跳转到开发页面,选中“DataTransfer”页面,开始页面设计。

2. 点击小部件面板图标进入小部件面板

3.在页面预览区域点击顶部导航栏图标,打开添加顶部导航

4. 拖动文本到导航栏中间,点击选中刚拖动的文本小部件,右侧出现小部件的配置页面,并配置如下内容
-
文本内容:
串口调试助手 -
字号:
18 -
字体颜色:
#FFFFFFFF
5. 拖动按钮到导航栏右侧,然后在右侧的属性编辑器中配置属性,属性信息如下:
-
宽:
100
-
高:
40
-
背景颜色:
#00FCFCFC
-
阴影大小:
0
6.拖动文本到按钮中,然后在右侧的属性编辑器中配置属性,属性信息如下:
-
文本内容:
串口设置
-
文本大小:
14
-
字体颜色:
#FFFFFFFF
7.拖动容器到上下布局中,在右侧的属性编辑器中配置属性,属性信息如下:
-
宽:
无限
-
高:
460
-
背景颜色:
#00C8C8C8
-
内边距:
10
8.拖动上下布局到容器中,在右侧的属性编辑器中配置属性,属性信息如下:
-
主轴对齐:
两端对齐
-
次轴对齐:
向左对齐
9.拖动左右布局到上下布局中,在右侧的属性编辑器中配置属性,属性信息如下:
-
主轴对齐:
两端对齐
-
次轴对齐:
居中对齐
10.拖动 复选框 + 文本 到左右布局中,在右侧的属性编辑器中配置属性,属性信息如下:
-
复选框绑定变量:
hexDisplay
-
文本内容:
16进制显示
11.拖动 复选框 + 文本 到左右布局中,在右侧的属性编辑器中配置属性,属性信息如下:
-
复选框绑定变量:
timestamp
-
文本内容:
时间戳
12.拖动 复选框 + 文本 到左右布局中,在右侧的属性编辑器中配置属性,属性信息如下:
-
复选框绑定变量:
autoscroll
-
文本内容:
自动滚动
13.拖动左右布局到上下布局中,在右侧的属性编辑器中配置属性,属性信息如下:
-
主轴对齐:
左侧对齐
-
次轴对齐:
居中对齐
14.拖动 按钮 + 文本 到左右布局中,在右侧的属性编辑器中配置属性,属性信息如下:
-
宽:
100
-
高:
40
-
背景颜色:
#FF2B66F4
-
阴影大小:
0
-
外边距:
右 20
-
文本内容:
清空日志
-
文本大小:
14
-
字体颜色:
#FFFFFFFF
15.拖动 按钮 + 文本 到左右布局中,在右侧的属性编辑器中配置属性,属性信息如下:
-
宽:
100
-
高:
40
-
背景颜色:
#FF2B66F4
-
阴影大小:
0 -
文本内容:
数据波形
-
文本大小:
14
-
字体颜色:
#FFFFFFFF
16.拖动 自动扩展 + 容器 到上下布局中,在右侧的属性编辑器中配置属性,属性信息如下:
-
宽:
无限
-
高:
空
-
圆角:
10
-
边框:
1 #FF000000 -
内边距:
4
-
背景颜色:
#00C8C8C8
17.拖动 滚动条 到容器中,然后拖动列表布局到到滚动条中,在右侧的属性编辑器中配置属性,属性信息如下:
-
数据绑定:
logs
-
宽:
无限
-
高:
无限
18.拖动 上下布局 到列表布局中,拖动 条件生成器到上下布局中, 然后在右侧的属性编辑器中配置属性,属性信息如下:
-
条件内容:
timestamp = true
19.拖动 文本 到条件内容1中,然后在右侧的属性编辑器中配置属性,属性信息如下:
-
文本内容:
${logs[index]['time']}>
-
文本大小:
12
-
字体颜色:
#FF9C9C9C
20.拖动 条件生成器到上下布局中, 然后在右侧的属性编辑器中配置属性,属性信息如下:
-
条件内容1:
logs[index]['type'] == 'common'
-
条件内容2:
logs[index]['type'] == 'send'
21.拖动 文本 到条件内容1中,然后在右侧的属性编辑器中配置属性,属性信息如下:
-
文本内容:
${logs[index]['content']}
-
文本大小:
12
22.拖动 文本 到条件内容2中,然后在右侧的属性编辑器中配置属性,属性信息如下:
-
文本内容:
[${logs[index]['uuid']}] 写入: ${convertData(logs[index]['value'])}
-
文本大小:
12
-
字体颜色:
#FF00CD00
23.拖动 文本 到条件内容3中,然后在右侧的属性编辑器中配置属性,属性信息如下:
-
文本内容:
[${logs[index]['uuid']}] 接收: ${convertData(logs[index]['value'])}
-
文本大小:
12
-
字体颜色:
#FF0070E6
-
宽:无限
-
高:
320
-
背景颜色:
#00FFFFFF
-
内边距:
10
25.拖动 上下布局 到容器中,然后在右侧的属性编辑器中配置属性,属性信息如下:
-
主轴对齐:
两端对齐
-
次轴对齐:
左侧对齐
26.拖动 文本 到上下布局中,然后在右侧的属性编辑器中配置属性,属性信息如下:
-
文本内容:
单项发送
-
文本大小:
14
-
字体粗细:
700
27.拖动 文本输入框 到上下布局中,然后在右侧的属性编辑器中配置属性,属性信息如下:
-
数据绑定:
sendData
-
宽:
无限
-
高:50
-
提示文本内容:
数据格式:${hexSend? 'aa00bb11cc22': '任意字符' }
-
提示字体颜色:
#FF9E9E9E
28.拖动 左右布局 到上下布局中,然后在右侧的属性编辑器中配置属性,属性信息如下:
-
主轴对齐:
两端对齐
-
次轴对齐:
居中对齐
29.拖动 上下布局 到左右布局中,然后在右侧的属性编辑器中配置属性,属性信息如下:
-
主轴对齐:
两端对齐
-
次轴对齐:
居中对齐
30.拖动 复选框 + 文本 + 文本输入框 至 上下布局中,
-
复选框绑定变量:
cycle
-
文本内容:
循环发送 延时(ms):
-
文本输入框宽:
80
-
文本输入框高:
40
-
边框:
1 #FF000000
-
文本输入框绑定变量:
delayed
31.拖动 复选框 + 文本 + 复选框 + 文本 至 上下布局中,
-
复选框1绑定变量:
hexSend
-
文本内容1:
16进制发送
-
复选框2绑定变量:
newLine
-
文本内容2:
添加换行
32.拖动 按钮 + 文本 至 上下布局中,
-
宽:
100
-
高:
40
-
背景颜色:
#FF2B66F4
-
阴影大小:
4
-
圆角:
10
-
文本内容:
设置协议
-
文本大小:
14
-
字体颜色:
#FFFFFFFF
33.拖动 上下布局 至 左右布局中
-
主轴对齐:
向上对齐
-
次轴对齐:
居中对齐
34.拖动 按钮 + 文本 至 上下布局中,
-
宽:
100
-
高:
40
-
背景颜色:
#FF2B66F4
-
阴影大小:
4
-
圆角:
10
-
文本内容:
发送
-
文本大小:
14
-
字体颜色:
#FFFFFFFF
35.拖动 按钮 + 文本 至 上下布局中,
-
宽:
100
-
高:
40
-
背景颜色:
#FF2B66F4
-
阴影大小:
4
-
圆角:
10
-
文本内容:
快捷发送
-
文本大小:
14
-
字体颜色:
#FFFFFFFF
配置逻辑(动作流程图)
进入页面动作流程图
1.点击右上角 页面设置 图标进入到页面设置中
2.打开动作 → 点击 编辑流程图

页面初始化
1. 点击页面初始化下面的加号添加事件
2.点击开始下方的加号,添加动作

3.鼠标选中动作,右侧出现动作配置并配置如下内容
-
动作类型:
硬件动作 -
硬件类型:
串口
-
操作类型:
扫描
4.添加新的动作,右侧出现动作配置并配置如下内容
-
动作类型:
硬件动作 -
硬件类型:
串口
-
操作类型:
连接
-
连接方式:
数组索引
-
设备索引:
0

5.添加新的动作,右侧出现动作配置并配置如下内容
-
动作类型:
硬件动作 -
硬件类型:
串口
-
操作类型:
接收数据
-
点击
新增自定义代码块-
代码块名称:
数据处理 -
代码块编辑区
processData(strList) { addReceiveLog(strList.map((e) => int.parse(e, radix: 16)).toList()); }
-
-
选中这个代码块
-
点击
新增自定义代码块-
添加以下代码块,用来方便其它函数调用处理
-
// Custom Code: 日志记录 addLog(type, {content, value}) async { if (logs.length > 1000) { logs.removeRange(0, logs.length - 1000); } var map = { 'time': DateTime.now().toString().substring(0, 23), 'type': type, 'content': content, 'value': value }; logs.add(map); // list.add(map); if (currentFile == null) { Directory directory = await getTemporaryDirectory(); currentFile = File('${directory.path}${Platform.pathSeparator}serialtool.log'); currentFile?.deleteSync(); } try { IOSink sink = currentFile!.openWrite(mode: FileMode.append); sink.writeln( "${map['time']}, ${map['type'] == 'common' ? map['content'] : map['value']} "); await sink.flush(); await sink.close(); } catch (e) { print('写入异常----> $e'); } setState(() {}); } // Custom Code: 普通消息记录 addCommonLog(content) { addLog('common', content: content); } // Custom Code: 发送消息记录 void addSendLog(value) { addLog('send', value: value); } // Custom Code: 接收消息记录 void addReceiveLog(value) { addLog('receive', value: value); } // Custom Code: 数据格式转换 convertData(data) { if (hexDisplay) { return (data.cast<int>()) .map((e) => e.toRadixString(16).padLeft(2, '0')) .join(' '); } return String.fromCharCodes(data.cast<int>()); }
-
清空日志按钮动作流程图
1.点击按钮小部件进入到小部件配置中
2.打开动作 → 点击 编辑流程图
单击事件
1. 点击页面初始化下面的加号添加事件

2.点击开始下方的加号,添加动作

3.点击添加按钮,选择添加动作,并配置动作属性
-
动作类型:
变量更新
-
选择变量:
logs
- 数组操作类型:
清空数组

数据波形按钮动作流程图
1.点击按钮小部件进入到小部件配置中
2.打开动作 → 点击 编辑流程图
单击事件
1. 点击页面初始化下面的加号添加事件

2.点击开始下方的加号,添加动作

3.点击添加按钮,选择添加动作,并配置动作属性
-
动作类型:
导航
-
导航到:
DataWaveform
发送按钮动作流程图
1.点击按钮小部件进入到小部件配置中
2.打开动作 → 点击 编辑流程图
单击事件
1. 点击页面初始化下面的加号添加事件

2.点击开始下方的加号,添加动作

3.点击添加按钮,选择添加动作,并配置动作属性
-
动作类型:
自定义代码块
-
点击
新增自定义代码块-
代码块名称:
串口数据发送 -
设置入参:
String -
代码块编辑区
writeData(String data) async { while (true) { print("object==== 发送数据 $data"); // 相关参数 List<int> paramBytesList = []; if (hexSend || canSend) { for (int i = 0; i < data.length; i += 2) { int v = int.parse("${data[i]}${data[i + 1]}", radix: 16); paramBytesList.add(v); } } else { paramBytesList = List.from(data.codeUnits); } if (!canSend) { String checkBitType = protocolData['checkBitType'] ?? '累加'; bool isFixedBegin = protocolData['isFixedBegin'] ?? false; bool isLength = protocolData['isLength'] ?? false; bool isControlWord = protocolData['isControlWord'] ?? false; bool isCheckBit = protocolData['isCheckBit'] ?? false; bool isFixedEnd = protocolData['isFixedEnd'] ?? false; String fixedBegin = protocolData['fixedBegin'] ?? 'AA'; String length = protocolData['length'] ?? '0'; String controlWord = protocolData['controlWord'] ?? '00'; String checkBitRangeStart = protocolData['checkBitRangeStart'] ?? '1'; String checkBitRangeEnd = protocolData['checkBitRangeEnd'] ?? '2'; String fixedEnd = protocolData['fixedEnd'] ?? '55'; List<int> resolved(String source) { List<int> res = []; for (int i = 0; i < source.length; i = i + 2) { res.add(int.parse(source[i] + source[i + 1], radix: 16)); } return res; } final dataLength = paramBytesList.length; int frontLength = 0; int totalLength = paramBytesList.length; if (isFixedBegin) { List<int> res = resolved(fixedBegin); paramBytesList.insertAll(0, res); totalLength += res.length; frontLength += res.length; } if (isLength) { paramBytesList.insert( frontLength, dataLength + int.parse(length, radix: 16)); totalLength += 1; frontLength += 1; } if (isControlWord) { List<int> res = resolved(controlWord); paramBytesList.insertAll(frontLength, res); totalLength += res.length; frontLength += res.length; } if (isCheckBit) { List<int> res = []; totalLength += 1; if (isFixedEnd) { res = resolved(fixedEnd); totalLength += res.length; } int checksum = 0; for (int i = int.parse(checkBitRangeStart); i < totalLength - int.parse(checkBitRangeEnd); i++) { if (checkBitType == '累加') { checksum = (checksum + paramBytesList[i]) & 0xFF; } if (checkBitType == '异或') { checksum = (checksum ^ paramBytesList[i]) & 0xFF; } } paramBytesList.add(checksum); if (res.isNotEmpty) { paramBytesList.addAll(res); } } else { if (isFixedEnd) { List<int> res = resolved(fixedEnd); paramBytesList.addAll(res); } } } if (!canSend && newLine) { paramBytesList.addAll([0x0D, 0x0A]); } await Future.delayed(Duration(milliseconds: int.parse(delayed)), () {}); addSendLog(paramBytesList); sendDataSerialByIntArr(paramBytesList); if (!cycle) { break; } } }
-
-
选中这个代码块,并填写入参 sendData
快捷发送按钮动作流程图
1.点击按钮小部件进入到小部件配置中
2.打开动作 → 点击 编辑流程图
单击事件
1. 点击页面初始化下面的加号添加事件

2.点击开始下方的加号,添加动作

3.点击添加按钮,选择添加动作,并配置动作属性
-
导航到
CustomData -
动作类型:
导航
4.添加一个新的动作,并配置动作属性
-
-
操作类型:
数据读取 -
接收数据的变量:
sendData -
读取本地数据的键名:
data -
动作类型:
本地存储
-

5.添加一个新的动作,并配置动作属性
-
-
选中
串口数据发送-
输入参数 sendData
-
-
动作类型:
自定义代码块
-
至此,页面及逻辑全部设置完成。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐







所有评论(0)