欢迎加入旋武社区:https://xuanwu.openatom.org
一、为什么要重写一个串口助手
串口调试工具属于开发现场里使用频率很高、但又很容易被低估的一类软件。调单片机、模组、网关、开发板,或者跑产线测试脚本时,开发者要反复完成同一套动作:枚举串口、配参数、文本/HEX 收发、切编码、控 DTR/RTS、存日志。传统桌面串口工具在 Windows、Linux、macOS 上已经非常成熟,但真正落到日常工作时,还是有三件事绕不开。
第一件是平台锁死。大量经典串口工具是 Windows 单平台,工程主力机一旦换成 macOS 或 Linux,就只能开虚拟机凑合。
第二件是中文乱码 ,这也是最经典的一个坑。上游 Qt 版接收侧用 QString::fromLatin1、发送侧用 toLatin1(),会把 UTF-8 / GBK 字节一律按 Latin1 解释。设备明明回的是 温度=26.5℃,界面上给你的是一串乱码。这不是"编码选项没选对",而是工具从设计上就没打算正确处理多字节编码。
第三件是鸿蒙生态缺位。鸿蒙 PC 进入开发桌面场景之后,想在本地窗口、权限、文件访问和设备模型下原生调串口,基本没有顺手的工具。
SSCom 就是冲着这三件事做的。它是开源串口调试助手 kangear/sscom 的 Rust 重构版 :在相近的使用场景下,用 Rust + egui 重新实现串口收发、编码转换与常见调试能力,不拷贝上游 Qt 源码;同时把同一套 Rust 核心库复用到鸿蒙端,让四端行为保持一致。
所以它不是在"再造一个轮子",而是把轮子换了个更结实的材质重做了一遍。
二、先把边界定清楚
串口调试助手看起来只是几个下拉框和按钮,但真正决定它好不好用的,是通信链路和系统边界怎么处理。重写之前先把"哪些能力照搬、哪些必须重做"列清楚:
| 能力 | 传统 Qt 桌面版 | SSCom 的做法 |
|---|---|---|
| 串口枚举 | 桌面系统接口直接枚举 | 桌面端走 serialport crate;鸿蒙端 Rust FFI 扫设备节点,经 NAPI 回给 ArkTS |
| 串口打开 | 原生二进制访问 COM / /dev/tty* |
鸿蒙端受 HAP 权限、设备节点、Native 句柄共同约束 |
| 数据收发 | Rust 线程读写串口 | 桌面端独立 IO 线程;鸿蒙端 ArkTS 轮询调用 NAPI,底层 Rust 负责读写 |
| 编码转换 | UTF-8 / GBK / Latin1 | 两端复用同一套 Rust 核心逻辑(encoding_rs) |
| HEX 支持 | UI 状态与解析同进程完成 | ArkTS 只管模式状态,Rust 负责字节转换与格式化 |
| 控制线 | 串口库封装 DTR/RTS | 鸿蒙端通过 ioctl 暴露给 ArkTS 开关 |
| 文件能力 | 系统文件对话框 | 桌面端 macOS 用系统脚本、Windows / Linux 用原生对话框;鸿蒙端走 @kit.CoreFileKit 的 DocumentViewPicker |
同时也要说清不做什么:SSCom 没有把串口助手做成一个"命令行外壳",而是把串口参数、收发面板、HEX / 编码状态、文件操作和状态栏放进同一个原生应用窗口里。这样更接近 PC 上日常调试的习惯------先确认连接,再决定收发格式,最后看日志和统计。
三、上手:把 SSCom 跑起来
1. 路线一:直接下载安装包(推荐新手)
不需要装任何编译环境。打开 Release 页面:https://atomgit.com/xiaohong-ai/sscom/releases
| 你的系统 | 下载文件 |
|---|---|
| macOS(Apple Silicon / M 系列) | SSCom-macOS-arm64.dmg |
| Windows x64 | SSCom-windows-x64.zip |
| Linux x86_64 | SSCom-linux-x86_64.tar.gz |
| 校验(推荐) | SHA256SUMS |
macOS :双击挂载 DMG,把 sscom.app 拖进「应用程序」,然后在终端剥掉隔离属性:
bash
xattr -dr com.apple.quarantine /Applications/sscom.app
这一步看着多余,但多半必须做------原因在第七节里讲。
Windows :解压 ZIP,双击 sscom.exe。界面中文依赖 C:\Windows\Fonts\ 下的微软雅黑等系统字体,一般开箱可用。
Linux:解压后直接跑,缺库就补上运行依赖:
bash
tar -xzf SSCom-linux-x86_64.tar.gz
cd SSCom-linux-x86_64
./sscom
# 若报缺库
sudo apt install -y libxcb-render0 libxcb-shape0 libxcb-xfixes0 \
libxkbcommon0 libudev1 libfontconfig1
2. 路线二:从源码构建
需要 Rust 1.76 以上(eframe 0.29 的要求),含 cargo。
bash
git clone https://atomgit.com/xiaohong-ai/sscom.git
cd sscom
cargo run --release
Windows 上产出 target/release/sscom.exe;Linux 桌面除上述库外还需要开发头文件:
bash
sudo apt install -y build-essential pkg-config libxcb-render0-dev libxcb-shape0-dev \
libxcb-xfixes0-dev libxkbcommon-dev libudev-dev libfontconfig1-dev
cargo build --release
要在 macOS 上一次性把四个平台的包都打出来:
bash
bash build-all.sh # 增量构建(编译 + 打包)
bash build-all.sh --force # 强制重新编译
bash build-all.sh --pack # 只打包,复用已有二进制
产物统一落在 dist/。跑测试用 cargo test,目前覆盖接收流的 GBK 流式解码等核心逻辑。
3. 路线三:鸿蒙端(HarmonyOS NEXT)
环境需要 DevEco Studio 5.0+、HarmonyOS SDK,Rust 侧要装 aarch64-unknown-linux-ohos target。源码在 ohos/ 目录,推荐用仓库自带脚本分段构建:
bash
cd ohos
# 完整构建:Rust staticlib → 复制 .a → 清理缓存 → 打包 HAP
bash build.sh
# 分步来也可以
bash build.sh --rust # 只编译 Rust 核心库
bash build.sh --hap # 只构建 HAP
bash build.sh --clean # 清理构建产物
产物路径:
text
ohos/entry/build/default/outputs/default/entry-default-signed.hap
装到真机并启动:
bash
hdc install ohos/entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.sscom.serial
需要注意的是,串口访问必须在真机上验证。模拟器提供不了真实的 USB 串口链路,最多只能验证 UI、文件选择和基础状态流转。鸿蒙侧的适配细节和坑,第五节、第七节还会展开。
4. 五分钟跑通一次收发
接好 USB 转串口模块之后,按这个顺序走一遍就上手了:
- 刷新串口列表 ------点左上角「刷新」,选中你的设备。macOS 常见路径是
/dev/cu.wchusbserial*,Windows 是COM1这类,Linux / 鸿蒙是/dev/ttyUSB*。看不到设备,先怀疑驱动和数据线。 - 配参数 ------波特率(默认 115200)、数据位、校验、停止位、流控。串口打开状态下也能改,SSCom 支持在线热重配,不用关了再开。
- 打开串口 ------指示灯变绿、状态栏出现
设备已打开 115200 8-None-1就说明连上了。 - 看接收、发数据 ------接收区持续输出设备日志;在发送区输入内容点「发送」。多数 AT 指令和 shell 需要行尾,勾上发送新行 CRLF 会自动补
\r\n。 - 要调寄存器就切 HEX ------勾上「HEX 显示 / HEX 发送」,发送框按空格分隔十六进制填,例如
48 65 6C 6C 6F;接收区同步换成等宽字体,方便按字节对齐排查。 - 中文乱码就换编码------默认收发都是 UTF-8;设备是国标串口协议就把「接收解码」改成 GBK。
- 周期性发心跳用定时发送------勾选并填间隔(ms,默认 1000)。要整包下发长报文或固件指令,用「打开文件」把文件内容送进发送区。
- 换主题------左下角「界面主题」里有浅色、黑色、黑紫、梯度渐变、公主芭比粉五套预设,点一下立即生效,并且会持久化,下次打开还是你选的那套。
配置(端口、参数、编码、主题、定时发送设置)都会自动存成 JSON,下次启动直接还原上一次的工作状态。
四、桌面端:界面与关键交互的取舍
macOS:中文日志能看,是最基本的体面

左侧是参数与数据格式,右侧是接收日志和发送区,底部固定状态栏。这张截图里跑的是星闪(SLE)光照联动 Hub 的调试日志------SSAP Client 注册、星闪地址设置、SLE 协议栈使能、扫描相位(scan, phase=0,LUX/SOCK/LED/ready)。中文完整、无乱码,状态栏实时显示 R:1770 S:0 收发字节数与当前连接状态。
这里有两个细节是刻意设计的:
- 编码选项放在最外层,不塞进二级菜单。 串口现场最常遇到的故障就是编码不一致,把「接收解码 / 发送编码」摆在主面板上,出问题的第一反应就能排查到它。
- 接收区可以直接选中复制。 窗口里那行提示「在接收区内选中文字后使用 Cmd+C 复制(与常见串口助手一致)」不是废话------工具顺手与否,往往就体现在能不能把一段日志直接抠出来粘到别处。
Windows:同一套界面逻辑

Windows 端与 macOS 端共用同一套 egui 界面逻辑,操作习惯完全一致。截图左下角展开的是「界面主题」菜单。
五、鸿蒙 PC 适配:把串口助手放进原生桌面工作流
这一节是重点。串口调试助手看起来只是几个输入框和按钮,但真正适配到鸿蒙 PC 时,重点不在界面复刻,而在通信链路和系统边界的重建。
1. 适配边界:桌面端的做法,鸿蒙端为什么要换
| 模块 | 桌面端常见处理 | 鸿蒙 PC 侧处理 |
|---|---|---|
| 串口枚举 | serialport crate 直接枚举 |
Rust FFI 扫描设备节点,通过 NAPI 返回 ArkTS |
| 串口打开 | 原生二进制直接访问 COM 或 /dev/tty* |
HAP 权限、设备节点、Native 句柄共同约束 |
| 数据收发 | Rust 线程读写串口 | ArkTS 轮询调用 NAPI,底层 Rust 负责读写 |
| HEX 支持 | UI 状态与 Rust 解析在同进程内完成 | ArkTS 管理模式与截断保护,Rust 负责字节转换与校验 |
| 编码转换 | UTF-8 / GBK / Latin1 由桌面端处理 | 通过 Rust 核心库复用 encoding_rs 能力 |
| 文件能力 | 系统文件对话框 | 使用鸿蒙文件选择器读取发送内容、保存接收内容 |
| 控制线 | DTR/RTS 由串口库封装 | 通过 ioctl 暴露给 ArkTS 开关 |
一句话总结差异:桌面端可以靠库把平台差异吃掉,鸿蒙端必须面对 HAP 生命周期、USB 串口权限、Native 库装载、ArkTS 与 Rust 之间的数据交换,以及窗口化 PC 体验。
2. 真机验证环境
| 项目 | 值 |
|---|---|
| 应用 BundleName | com.sscom.serial |
| 应用版本 | 1.0.0(versionCode 1000000) |
| 工程面向 SDK | compatibleSdkVersion / targetSdkVersion = HarmonyOS 6.1.0(23) |
| 运行时 | runtimeOS: HarmonyOS,apiType: stageMode |
| 目标 ABI | arm64-v8a |
| Native 编译 | nativeCompiler: BiSheng(毕昇编译器,经 CMake 链接 Rust staticlib) |
| 窗口模式 | supportWindowMode: ["fullscreen", "floating"],最小 800×600,最大高度 1600 |
| 连接方式 | hdc |
| 权限声明 | ohos.permission.ACCESS_DDK_USB、ohos.permission.ACCESS_DDK_USB_SERIAL |
| Rust 交叉目标 | aarch64-unknown-linux-ohos |
以下图片均为鸿蒙 PC 真机运行截图。
3. 主界面与关键交互

应用启动后,左侧是连接参数与数据格式,右侧是接收日志和发送区,顶部保留当前连接状态和打开串口入口。这个布局适合串口调试的高频操作:波特率、数据位、校验位、停止位、流控、HEX 显示、HEX 发送、编码选择都能在同一屏完成确认。
① 空态要先做稳。 截图中当前未接入 USB 串口设备,状态栏停留在「串口已关闭」。这类空态对串口工具很重要,因为真实现场经常先遇到的不是收发成功,而是数据线未连接、设备未授权、串口被占用或设备节点尚未生成。应用没有在空设备状态下崩溃,而是把状态稳定地留在界面和底部状态栏上------这比"能用"更重要。
② 刷新后自动选中第一个端口。 扫描到设备后,如果当前还没选端口,应用会自动把第一个可用端口填进去。看似一行代码的事,但省掉的是现场最常见的一次多余点击。
③ 打开与关闭是同一个按钮。 按钮文案随连接状态在「打开串口」和「关闭串口」之间切换,避免两个并列按钮带来的误操作空间------串口调试现场最不想干的事,就是在设备正在跑的时候手滑点错。
④ 状态栏把实际生效参数摊开。 打开成功后状态栏会拼出完整一行:
text
/dev/ttyUSB0 已打开 115200 8-None-1
端口、波特率、数据位-校验-停止位一次给全。串口调不通时,第一步永远是确认"我以为的参数"和"实际生效的参数"是不是一回事,这行字就是为了把这个确认成本压到最低。
⑤ 波特率用固定档位下拉,不自由输入。 常用档位从 300 到 2000000 直接选,默认保持 115200。这里没有让用户手动输入任意数字,是为了减少现场调试时的低级错误:多数开发板、模组和调试固件都使用固定波特率档位,下拉列表比自由输入更适合桌面工具的默认路径;确有特殊波特率时,再在核心层补充支持会更可控。
⑥ HEX 收发:字节视图和发送格式同时联动。 切换「HEX 显示 / HEX 发送」后,接收日志标题旁会出现 HEX 标记,发送区同步切换为按空格分隔字节的输入提示。这个联动避免了"接收按文本看、发送按 HEX 发"时状态不清的问题。调试二进制协议、bootloader 握手、寄存器读写或带校验帧的数据包时,文本窗口不足以判断真实字节,必须一眼能确认当前窗口正在以字节方式工作。界面底部也留了一行常驻提示:在接收区内拖选文字后复制 | HEX 时用空格分隔字节,例如 48 65 6C 6C 6F。
⑦ 编码选择:兼顾 UTF-8、GBK 和 Latin1。 串口设备并不总是按 UTF-8 输出日志,很多存量设备、工控模块和中文固件仍然使用 GBK,部分协议还会混合 Latin1 或直接输出原始字节。鸿蒙 PC 版本保留了接收编码和发送编码选择,其中接收侧支持 UTF-8、GBK、Latin1。编码能力放在串口工具里,比在日志输出后再做外部转换更实用。
⑧ 发送框回车即发送。 输入焦点的行为也按 PC 习惯做了适配------焦点在发送框时按回车直接发送,日志和输入之间不需要鼠标来回切换。
4. 鸿蒙端三层架构
text
ArkUI 页面
entry/src/main/ets/pages/Index.ets
负责界面、状态、参数选择、文件选择和按钮交互
ArkTS 模型层
entry/src/main/ets/model/SerialPort.ets
负责封装 NAPI 调用、轮询接收、统计字节数和编码状态
Native 核心层
entry/src/main/cpp/bridge.cpp
ohos/rust-core/src/lib.rs
负责 NAPI 注册、Rust FFI、串口读写、HEX 工具和 GBK/UTF-8 转换
完整调用链路:
text
Index.ets → SerialPort.ets → libsscom_napi.so (NAPI) → bridge.cpp → libsscom_core.a (Rust FFI)
主要 NAPI 能力:
| 接口 | 说明 |
|---|---|
listPorts |
枚举可用串口并返回字符串数组 |
openSerial / closeSerial |
打开和关闭串口句柄 |
readSerial |
从串口读取数据并返回 HEX 字符串 |
writeSerial / writeSerialHex |
按文本或 HEX 写入串口 |
setDtr / setRts |
控制 DTR、RTS 线路 |
reconfigure |
在线更新波特率、数据位、校验位、停止位和流控 |
gbkToUtf8 / utf8ToGbk |
在中文串口日志和发送内容之间做编码转换 |
接收侧在鸿蒙端是轮询模型 :ArkTS 用定时器周期性调用 readSerial 拿回 HEX 字符串,再按当前编码转成可读文本。这个设计不是最优性能,但对串口调试这种低速率、低频场景足够了,而且避免了把线程模型硬塞进 ArkTS 的复杂度。
这里有个鸿蒙端独有的细节:多字节字符的截断保护是两层做的 。Rust 侧负责字节层面的解码状态;ArkTS 侧还保留了一段"上次未完成的尾部 HEX 片段",在把 HEX 转回文本之前先补上这段残缺字节,避免在 setInterval 的轮询边界上把半个汉字当成完整字符渲染出来。
5. 构建与安装
鸿蒙端源码位于 ohos/ 目录,推荐使用仓库内脚本完成 Rust 静态库和 HAP 的分段构建:
bash
cd ohos
# 完整构建:Rust staticlib → 复制 .a → 清理缓存 → 打包 HAP
bash build.sh
# 只编译 Rust 核心库
bash build.sh --rust
# 只构建 HAP
bash build.sh --hap
构建成功后,产物路径为:
text
ohos/entry/build/default/outputs/default/entry-default-signed.hap
安装到真机并启动:
bash
hdc install ohos/entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.sscom.serial
签名别漏 :
build-profile.json5里的signingConfigs需要在本机 DevEco Studio 的Project Structure > Signing Configs里生成(证书、profile、密钥库都是本机路径),工程里不自带。Rust 侧的交叉编译 linker 配置由ohos/build-rust.sh生成。另外要记住:串口访问必须在真机上验证。模拟器通常无法提供真实 USB 串口链路,最多只能验证 UI、文件选择和基础状态流转。
六、一套 Rust 核心,两种前端
回到整体。SSCom 最值得说的设计是核心逻辑只写一遍:桌面端和鸿蒙端共享同一个 Rust 核心库,串口 I/O、编码转换、HEX 解析都在 Rust 里,两端只是换了不同的 UI 外壳。
┌─────────────────────────────────┐ ┌──────────────────────────────────┐
│ 桌面端 (egui) │ │ 鸿蒙端 (ArkUI) │
│ │ │ │
│ main.rs ─→ egui UI │ │ Index.ets ─→ ArkUI │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ io_thread.rs │ │ SerialPort.ets ─→ libsscom_napi │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ serialport crate │ │ bridge.cpp (NAPI) │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ src/ (encoding, HEX, ...) │ │ libsscom_core.a (Rust FFI) │
└─────────────────────────────────┘ └──────────────────────────────────┘
| 端 | 语言 | UI 框架 | 串口层 | 产物 |
|---|---|---|---|---|
| 桌面(macOS / Windows / Linux) | Rust | egui + glow | serialport crate |
原生二进制(.app / .exe / ELF) |
| 鸿蒙(HarmonyOS NEXT) | Rust + ArkTS | ArkUI | Rust FFI(libc / termios) | HAP |
为什么这么拆?
- 串口 I/O 是脏活。 非阻塞读、
EAGAIN、超时、断线处理、字节流里的多字节字符截断......这些逻辑放在 Rust 里写一次、测一次,两端都受益。 - UI 该跟着平台走。 桌面端用 egui 追求轻量和跨平台一致;鸿蒙端用 ArkUI 才能真正融进鸿蒙的系统体验(窗口模式、原生文件选择器、应用市场分发)。
- 中间靠 NAPI 桥接。 C++ 桥接层只做类型转换与 NAPI 注册,不含业务逻辑,边界清晰。
桌面端源码结构:
sscom/
├── src/ # 桌面端 Rust 源码 (egui)
│ ├── main.rs # 入口 & egui UI(含 5 套主题预设)
│ ├── io_thread.rs # 串口 IO 线程 (serialport)
│ ├── recv_log.rs # 接收日志组件
│ ├── rx_stream.rs # 接收流处理(跨包增量解码 + 单测)
│ ├── settings.rs # 配置持久化(JSON)
│ └── fonts.rs # 系统 CJK 字体加载
├── ohos/ # 鸿蒙端工程
│ ├── rust-core/ # Rust 核心库 (staticlib)
│ ├── entry/src/main/ # ArkTS 前端 + NAPI C++ 桥接层
│ ├── build.sh # 一键构建脚本
│ └── docs/BUILD.md # 构建详细文档
├── scripts/ # 各平台打包脚本
├── build-all.sh # 全平台打包脚本
└── .gitcode/workflows/ # 全平台 CI 构建发布
七、重写过程中真正难的地方
功能列表看着不长,但真正花时间的都在下面这些地方。前四条都是鸿蒙侧绕不过去的。
难点一:serialport 在鸿蒙 target 上根本编译不过
桌面端用 serialport crate 统一处理各平台差异很舒服,但这条路在鸿蒙上走不通------serialport 依赖 nix / libudev,无法在 OHOS target 上编译。
所以鸿蒙核心库改成了 staticlib,串口操作直接走 POSIX termios:open 用 O_RDWR | O_NOCTTY | O_NONBLOCK,参数配置自己写 configure_serial,波特率自己做映射表(baud_to_constant),再用 cfsetispeed / cfsetospeed 下发;DTR/RTS 走 ioctl。
好处是串口访问更贴近系统能力、依赖更少;代价是波特率映射、数据位、校验位、停止位、流控和错误返回全都要自己兜住------比如遇到不支持的波特率时怎么报错而不是静默降级成 115200,这类边界都得一个个补。
难点二:ArkTS 与 Rust 之间必须控制好数据边界
鸿蒙端的调用链路是 Index.ets → SerialPort.ets → libsscom_napi.so → bridge.cpp → libsscom_core.a。ArkTS 适合管理界面状态和用户交互,Rust 适合处理字节、编码和串口读写;中间的 C++ NAPI 层负责类型转换和句柄传递。这里最容易出问题的是指针生命周期、字符串释放、空返回值和异常兜底。项目里做了三件事:
- 字符串所有权统一收口 :所有 Rust 侧返回的 C 字符串都由
sscom_string_free释放,并且明确规定用libc::free而不是CString::from_raw------因为 Rust 的全局分配器未必和 libc 的malloc是同一个,混用会踩内存。 - 错误信息回传 :用一个 thread-local 的
LAST_ERROR记录最后一条错误,通过sscom_get_last_error取出来,比自己拼返回码清楚得多。 - panic 兜底 :所有
#[no_mangle] extern "C"函数都包在catch_unwind里。Rust 侧 panic 一旦跨过 FFI 边界就是未定义行为,鸿蒙原生层会直接崩,必须挡在边界上。
难点三:串口权限和设备节点比桌面端敏感得多
应用已经声明 ohos.permission.ACCESS_DDK_USB 和 ohos.permission.ACCESS_DDK_USB_SERIAL,但声明权限并不等于所有设备都能直接访问 。真机上还要考虑 USB 授权、设备热插拔、系统签名、普通应用权限级别和 /dev/tty* 节点可见性。
这也解释了为什么鸿蒙 PC 版的截图保留的是无设备状态:这不是"没来得及接设备",而是这个路径在现场非常常见,也最能看出应用是否处理得稳定。等接入真实 USB 转串口后,用户只需要刷新列表、选设备、确认波特率,再打开连接即可。
难点四:PC 窗口体验需要重新组织
手机应用常见的单列页面并不适合串口调试。PC 用户更习惯一边看接收日志,一边调整参数和发送数据。鸿蒙 PC 版本采用左侧参数、右侧日志和发送区的布局,并把状态栏固定在底部,减少来回切页。
工程侧对应的是窗口配置:supportWindowMode 开放 fullscreen 和 floating 两种模式,最小窗口 800×600、最大高度 1600。适配时还要注意系统避让区、桌面任务栏和浮窗形态,保证在真实 PC 桌面上不遮挡核心控件。
难点五:构建环境要和工程配置严格匹配
鸿蒙项目对 SDK、签名材料、Hvigor、CMake、NDK linker 和 Rust target 的匹配要求比较高。本工程面向 HarmonyOS SDK 6.1.0(23) 配置(compatibleSdkVersion 与 targetSdkVersion 一致)、目标 ABI arm64-v8a、Native 侧走毕昇编译器 + CMake,Rust 侧要额外装 aarch64-unknown-linux-ohos target。
只要本机 SDK 缺少对应的 compatibleSdkVersion,构建阶段就会被直接拦下。 所以工程化适配时要把 SDK 版本、签名配置和 Rust 交叉编译环境提前固定,别等临近打包才暴露问题。
难点六:中文乱码的根因不是"编码选错",是字节边界
换掉 fromLatin1 只是第一步。真正麻烦的是串口是按字节流来的 ------一个汉字在 UTF-8 下占 3 字节、GBK 下占 2 字节,而这两三个字节完全可能被拆到两次 read 里。这时候无论选 UTF-8 还是 GBK,直接解码都会在分包边界冒出乱码或替换字符。
SSCom 的解法是 RxStreamDecoder 做跨包增量解码 :UTF-8 侧用一个 utf8_pending 缓冲暂存不完整的字节序列,等下一批数据到齐再一起解码;GBK 侧直接用 encoding_rs 的有状态 Decoder,让解码器自己记住跨包状态。鸿蒙端则在 ArkTS 侧再加一层尾部 HEX 片段缓存(见第五节),两端一起把半包问题堵死。
顺带记一个真实的坑:encoding_rs 的 decode_to_string 不会扩容 输出 String,而 String::new() 的 capacity 是 0,直接传进去会导致整段解码写不进去、界面一直空白。必须先把目标字符串 reserve 到位。这种问题在单元测试里抓不到,只有真机跑起来才会暴露。
难点七:字体------egui 自带字体不含汉字
egui 自带的 Ubuntu-Light 不含完整汉字,所以如果什么都不做,接收区的中文会显示成一排 □。SSCom 启动时会按候选列表依次尝试加载系统 CJK 字体(冬青 / PingFang / 宋体 / 黑体 / Arial Unicode 等),插到 Proportional / Monospace 字体栈的最前面。
接收日志默认用比例体绘制走系统 CJK;一旦勾选 HEX 显示就切成等宽字体,方便十六进制按字节对齐。Linux 上如果系统里一个候选字体都没有,那就得手工补 Noto CJK 或文泉驿。
难点八:macOS 上"已损坏"和 Dock 图标直角白底
这两个都是 macOS 特有的坑,且都跟签名/图标打包有关。
DMG 首次启动常常提示"sscom 已损坏"。文件其实没问题------构建用了 ad-hoc 签名并启用了硬化运行时,但没有走 Apple 公证,Gatekeeper 会给下载来的应用打上隔离属性。xattr -dr com.apple.quarantine 剥掉即可,这也是为什么安装步骤里那行命令不能省。
图标则更微妙:从 .app 启动时要使用 bundle 内的 icon.icns,系统会自动加圆角;如果运行时用 with_icon 传了一张方形 PNG 进去,Dock 上就会显示成直角白底的方块。这类问题不影响功能,但很影响"像个正经应用"的观感。
难点九:跨平台打包与 CI
四类产物的构建方式差异不小,最后收敛成一套脚本 + 一条流水线:
- macOS 包要生成
.icns、打包.app、做 ad-hoc 签名并启用硬化运行时; - Windows 用 PowerShell 脚本打 ZIP,并且在 release 构建下隐藏控制台黑框;
- Linux 出
tar.gz; - 鸿蒙端要 Rust 交叉编译 → 复制
.a→ 清缓存 → 打 HAP → 校验,串成一条链。
CI 里还专门做了两件"国内环境必需"的事:Rust 工具链经 rsproxy 镜像 安装(否则拉工具链经常超时)、crates.io 源切换 sparse index;同时跑 cargo fmt / clippy 检查和依赖漏洞扫描。Release 会随附 SBOM(软件物料清单) 与 SHA256 校验和 ,另有 SECURITY.md(漏洞私密报告渠道)、CONTRIBUTING.md、CHANGELOG.md、COMMITTERS.md。
八、当前能做什么、还差什么
现在版本(v2.0.8)已经覆盖的能力:
- 串口枚举、刷新、打开 / 关闭,以及开着串口热更新波特率、数据位、校验位、停止位、流控;
- 文本 / HEX 收发、定时发送、从文件整包发送;
- 接收编码 UTF-8 / GBK / Latin1,发送编码 UTF-8 / GBK,附带跨包增量解码;
- DTR / RTS 线路控制;
- 复制全部、清除窗口、保存接收到文件、打开文件到发送区;
- HEX 显示、自动滚动、发送新行 CRLF;
- 状态栏收发字节计数与连接状态;
- 五套界面主题与配置持久化;
- 鸿蒙 PC 上的 HAP 安装、启动和窗口展示,四类产物齐备。
鸿蒙 PC 端真机验证已覆盖的能力:
- HAP 安装、启动、窗口展示,
fullscreen/floating两种窗口模式; - 串口扫描、刷新、自动选中首个可用端口与无设备空态展示;
- 波特率、数据位、校验位、停止位、流控等连接参数选择;
- DTR、RTS 控制入口;
- HEX 显示、HEX 发送、CRLF 自动追加与尾部截断保护;
- UTF-8、GBK、Latin1 接收编码选择;
- 文本 / HEX 发送区、接收日志区、复制、清空、保存和打开文件入口(走鸿蒙文件选择器);
- 底部状态栏同步收发字节数、端口与串口参数。
同时边界也很明确,不吹:
- 鸿蒙模拟器不支持串口,串口相关能力必须真机验证;
- 鸿蒙端虽然声明了 USB 串口权限,但声明不等于可用 ,还涉及 USB 授权、热插拔、系统签名和
/dev/tty*节点可见性; - 桌面端目前没有断线自动重连,断线后是端口置空、提示重开;鸿蒙端同样缺少断线检测与自动重连;
- v2.0.8 的流水线暂停了 Linux aarch64 构建,需要该架构可以取更早的 v2.0.7 及以前版本;
- 测试覆盖目前集中在接收流解码等核心逻辑,UI 交互与构建脚本的自动化回归还需要补。
鸿蒙端后续要重点补的验证:
- 接入真实 USB 转串口设备后的枚举、授权、打开和关闭稳定性;
- 文本模式与 HEX 模式下的长时间收发压力测试;
- GBK 中文日志、多字节截断和混合编码设备的兼容性;
- DTR/RTS 对目标开发板复位、进入下载模式等场景的实测;
- 文件发送、接收保存和异常中断后的数据完整性校验。
九、总结
SSCom 的重写不是把桌面串口工具换个外壳,而是把串口访问、编码转换、HEX 字节处理、文件能力和窗口交互重新放到 Rust 和 HarmonyOS 的应用模型里:桌面端用 egui 追求轻量与一致,鸿蒙端用 ArkUI 承接原生桌面体验,中间用 C++ NAPI 做边界桥接,底层用一套 Rust 核心库保证收发行为一致。
从真机截图看,应用已经能在 macOS、Windows 和鸿蒙 PC 上稳定启动,完成参数选择、HEX 模式、编码选择、无设备拦截和状态展示。后续接入真实串口硬件后,重点会放在权限、热插拔、长时间收发和异常恢复上。
串口工具的好用程度,往往不只体现在成功收发那一刻,更体现在设备没接好、参数选错、编码不一致的时候,能不能让开发者迅速判断问题卡在哪里。这也是继续把 SSCom 做下去的理由。
十、项目地址与社区
AtomGit 仓库(源码 / Issue / Release 都在这里)
bash
git clone https://atomgit.com/xiaohong-ai/sscom.git
开源鸿蒙 PC 社区
旋武社区
SSCom 是用 Rust 写的,而国内 Rust 生态的组织化阵地正是开放原子旋武开源社区。旋武社区是开放原子开源基金会下的 Rust 中国社区,由 9 家会员单位共建,提供一站式 Rust 发行版、学习资料、在线编码体验,同时孵化 Rust 开源项目。
如果你对 Rust 感兴趣,或者正在做 Rust + 终端 / 嵌入式 / 开源鸿蒙方向的东西,欢迎去社区看看:
社区里能拿到的东西挺实在:Rust 快速上手发行版(图形化安装 + 命令行一键安装,覆盖各系统与 CPU 架构)、Rust 在线体验环境、系列课程视频,以及「旋武社区 Rust 开源项目推荐」这类项目内容栏目。SSCom 也期待在社区里遇到更多同方向的开发者------不管是提 Issue、交 PR,还是聊聊你和串口调试工具的那些坑。
项目交流群
用微信扫码加入 SSCom 项目交流群,反馈问题、分享使用心得:

十一、与上游项目的关系与许可
SSCom 是开源串口调试助手 sscom 的 Rust 重构版 :在相近的使用场景下,用 Rust + egui 重新实现串口收发、编码与常见调试能力,不拷贝上游 Qt 源码。
- 源项目(Qt,Linux / Mac):https://github.com/kangear/sscom
- 本仓库:桌面端
egui + glow + serialport,鸿蒙端ArkUI + NAPI + Rust FFI
相比上游的主要改进:统一 UTF-8 / GBK / Latin1 多编码收发,从根上避免把 UTF-8 / GBK 字节误当 Latin1 导致的乱码;桌面端与鸿蒙端共享同一个 Rust 核心库,收发行为一致;覆盖 macOS / Windows / Linux 原生二进制 + HarmonyOS NEXT HAP 四类产物。
License:MIT
项目地址:https://atomgit.com/xiaohong-ai/sscom
开源鸿蒙 PC 社区:https://harmonypc.csdn.net/ · 旋武社区:https://xuanwu.openatom.org
如果这个项目帮你省掉了开虚拟机的麻烦,欢迎到仓库点个 Star ⭐