大家好,我是杂烩君。
嵌入式开发,终端工具就像螺丝刀:不需要花里胡哨,但要顺手、稳定、关键功能齐全。
这次我们来推荐一个Github上开源的、跨平台、热门的终端工具——COMTool。
https://github.com/Neutree/COMTool
1. COMTool 功能
串口调试:ASCII/HEX、定时发送、发送历史、常用指令一键发送、收发统计、时间戳、保存日志、发送文件、转义字符等,都有。
协议插件:内置“协议插件”,支持自己写 Python 编解码脚本,还能做快捷键发送(例如方向键控制设备)。
图表插件:内置“图表插件”,按协议帧实时画折线,做传感器/姿态曲线很方便。
网络/远程:同一个工具里还能切到 TCP/UDP、SSH(配合终端插件)。
2. 串口
COMTool 的“调试插件”(一般叫 Send Receive / dbg)就是最传统的串口收发界面,但它把很多小功能做得很顺手。
支持:
- ASCII / HEX 双模式定时发送(压测/心跳/自动化)常用指令、发送历史时间戳、保存日志、自动换行终端颜色(ANSI Color)发送文件收发统计与清屏/清缓存(跑长测必用)编码与“乱码”处理
3. 图表
如果你经常调 IMU、温度、ADC、电机速度,串口刷数字看久了会很痛苦。COMTool 的图表插件思路很直接:
你给它发一种“曲线协议帧”它按帧解析出 name/x/y,就能实时画折线图表控件还能在设置里
双击添加(想画几条就加几条)
4. TCP/UDP
除了串口,COMTool 还支持 TCP/UDP(客户端/服务端)。
- 串口 + 网口双通道设备UDP 广播发现、日志上报TCP 长连接协议
5. SSH
6. 协议
很多时候我们不是单纯收发字符串,而是有一套自己的协议:帧头、长度、校验、字段解析、再把结果友好地打印出来。
COMTool 的 协议插件就是干这个的,而且门槛不高:
你可以在插件里写一段 Python 代码,定义 encode() / decode(),就能实现“发送前打包、接收后解包”。它还支持
自定义快捷键发送(Key mode):比如方向键控制云台/小车,不用鼠标点点点。
例如,STM32 端做一套二进制协议(带帧头/长度/校验),COMTool 的 decode() 负责把上报帧解析成可读内容;COMTool 的 encode() 负责把“点灯/灭灯”命令打包发给 STM32。
协议例子
帧格式:
AA 55:帧头
LEN:后面
CMD+PAYLOAD 的字节数
CMD:命令字(1 字节)
PAYLOAD:数据(可选)
SUM:校验(把 AA 55 LEN CMD PAYLOAD... 全部字节求和,取低 8 位)
主要行为是:
周期性上报:每 200ms 发一帧 REPORT,payload 里带 LED 状态 + 计数器接收命令并回包
:收到 LED_SET(0/1) 就控制 PC13 的 LED,并回一帧 ACK
staticvoidcomtool_protocol_task(void *param)
{
(void)param;
uint32_t counter = 0;
uint8_t led_state = 0;
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
// parser state
enum { S_HDR0, S_HDR1, S_LEN, S_BODY, S_SUM } st = S_HDR0;
uint8_t len = 0;
uint8_t body[1 + COMTOOL_P1_MAX_PAYLOAD]; // CMD + payload
uint8_t body_idx = 0;
uint8_t chk = 0;
TickType_t last_report = xTaskGetTickCount();
while (1)
{
// ---- parse RX bytes ----
uint8_t b = 0;
while (comtool_rb_pop(&b))
{
switch (st)
{
case S_HDR0:
st = (b == COMTOOL_P1_HDR0) ? S_HDR1 : S_HDR0;
break;
case S_HDR1:
st = (b == COMTOOL_P1_HDR1) ? S_LEN : S_HDR0;
break;
case S_LEN:
len = b;
if (len < 1 || len > (1 + COMTOOL_P1_MAX_PAYLOAD))
{
st = S_HDR0;
break;
}
body_idx = 0;
// init checksum = sum(AA,55,LEN)
chk = (uint8_t)((COMTOOL_P1_HDR0 + COMTOOL_P1_HDR1 + len) & 0xFF);
st = S_BODY;
break;
case S_BODY:
body[body_idx++] = b;
chk = (uint8_t)((chk + b) & 0xFF);
if (body_idx >= len)
st = S_SUM;
break;
case S_SUM:
if (chk == b)
{
// valid frame
uint8_t cmd = body[0];
constuint8_t *pl = &body[1];
uint8_t pl_len = (uint8_t)(len - 1);
if (cmd == COMTOOL_CMD_LED_SET && pl_len >= 1)
{
led_state = (pl[0] != 0) ? 1 : 0;
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, led_state ? GPIO_PIN_SET : GPIO_PIN_RESET);
uint8_t ack_pl[3] = { cmd, 0x00, led_state }; // result=0 ok
comtool_send_frame(COMTOOL_CMD_ACK, ack_pl, sizeof(ack_pl));
}
}
st = S_HDR0;
break;
default:
st = S_HDR0;
break;
}
}
// ---- periodic report ----
TickType_t now = xTaskGetTickCount();
if ((now - last_report) >= pdMS_TO_TICKS(200))
{
last_report = now;
counter++;
uint8_t rep_pl[1 + 4];
rep_pl[0] = led_state;
rep_pl[1] = (uint8_t)(counter & 0xFF);
rep_pl[2] = (uint8_t)((counter >> 8) & 0xFF);
rep_pl[3] = (uint8_t)((counter >> 16) & 0xFF);
rep_pl[4] = (uint8_t)((counter >> 24) & 0xFF);
comtool_send_frame(COMTOOL_CMD_REPORT, rep_pl, sizeof(rep_pl));
}
vTaskDelay(pdMS_TO_TICKS(5));
}
}
总结
COMTool 给我的感觉是:它不是“功能堆满”的那种大而全,而是把嵌入式最常用的调试链路——收发、协议、终端、曲线——做成了一个相对统一、能长期用的工具。如果你也在找一款“用着顺手、不卡、还能扩展”的串口工具,这是个不错的选择。
如果觉得文章有帮助,麻烦帮忙转发,谢谢!
947