SSCom 重构全记录:用 Rust 把串口调试助手送上鸿蒙 PC、macOS、Windows和Linux

欢迎加入旋武社区:https://xuanwu.openatom.org

项目开源地址https://atomgit.com/xiaohong-ai/sscom

一、为什么要重写一个串口助手

串口调试工具属于开发现场里使用频率很高、但又很容易被低估的一类软件。调单片机、模组、网关、开发板,或者跑产线测试脚本时,开发者要反复完成同一套动作:枚举串口、配参数、文本/HEX 收发、切编码、控 DTR/RTS、存日志。传统桌面串口工具在 Windows、Linux、macOS 上已经非常成熟,但真正落到日常工作时,还是有三件事绕不开。

第一件是平台锁死。大量经典串口工具是 Windows 单平台,工程主力机一旦换成 macOS 或 Linux,就只能开虚拟机凑合。

第二件是中文乱码 ,这也是最经典的一个坑。上游 Qt 版接收侧用 QString::fromLatin1、发送侧用 toLatin1(),会把 UTF-8 / GBK 字节一律按 Latin1 解释。设备明明回的是 温度=26.5℃,界面上给你的是一串乱码。这不是"编码选项没选对",而是工具从设计上就没打算正确处理多字节编码。

第三件是鸿蒙生态缺位。鸿蒙 PC 进入开发桌面场景之后,想在本地窗口、权限、文件访问和设备模型下原生调串口,基本没有顺手的工具。

SSCom 就是冲着这三件事做的。它是开源串口调试助手 kangear/sscomRust 重构版 :在相近的使用场景下,用 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.CoreFileKitDocumentViewPicker

同时也要说清不做什么: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 转串口模块之后,按这个顺序走一遍就上手了:

  1. 刷新串口列表 ------点左上角「刷新」,选中你的设备。macOS 常见路径是 /dev/cu.wchusbserial*,Windows 是 COM1 这类,Linux / 鸿蒙是 /dev/ttyUSB*。看不到设备,先怀疑驱动和数据线。
  2. 配参数 ------波特率(默认 115200)、数据位、校验、停止位、流控。串口打开状态下也能改,SSCom 支持在线热重配,不用关了再开。
  3. 打开串口 ------指示灯变绿、状态栏出现 设备已打开 115200 8-None-1 就说明连上了。
  4. 看接收、发数据 ------接收区持续输出设备日志;在发送区输入内容点「发送」。多数 AT 指令和 shell 需要行尾,勾上发送新行 CRLF 会自动补 \r\n
  5. 要调寄存器就切 HEX ------勾上「HEX 显示 / HEX 发送」,发送框按空格分隔十六进制填,例如 48 65 6C 6C 6F;接收区同步换成等宽字体,方便按字节对齐排查。
  6. 中文乱码就换编码------默认收发都是 UTF-8;设备是国标串口协议就把「接收解码」改成 GBK。
  7. 周期性发心跳用定时发送------勾选并填间隔(ms,默认 1000)。要整包下发长报文或固件指令,用「打开文件」把文件内容送进发送区。
  8. 换主题------左下角「界面主题」里有浅色、黑色、黑紫、梯度渐变、公主芭比粉五套预设,点一下立即生效,并且会持久化,下次打开还是你选的那套。

配置(端口、参数、编码、主题、定时发送设置)都会自动存成 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: HarmonyOSapiType: stageMode
目标 ABI arm64-v8a
Native 编译 nativeCompiler: BiSheng(毕昇编译器,经 CMake 链接 Rust staticlib)
窗口模式 supportWindowMode: ["fullscreen", "floating"],最小 800×600,最大高度 1600
连接方式 hdc
权限声明 ohos.permission.ACCESS_DDK_USBohos.permission.ACCESS_DDK_USB_SERIAL
Rust 交叉目标 aarch64-unknown-linux-ohos

以下图片均为鸿蒙 PC 真机运行截图。

3. 主界面与关键交互

应用启动后,左侧是连接参数与数据格式,右侧是接收日志和发送区,顶部保留当前连接状态和打开串口入口。这个布局适合串口调试的高频操作:波特率、数据位、校验位、停止位、流控、HEX 显示、HEX 发送、编码选择都能在同一屏完成确认。

① 空态要先做稳。 截图中当前未接入 USB 串口设备,状态栏停留在「串口已关闭」。这类空态对串口工具很重要,因为真实现场经常先遇到的不是收发成功,而是数据线未连接、设备未授权、串口被占用或设备节点尚未生成。应用没有在空设备状态下崩溃,而是把状态稳定地留在界面和底部状态栏上------这比"能用"更重要。

② 刷新后自动选中第一个端口。 扫描到设备后,如果当前还没选端口,应用会自动把第一个可用端口填进去。看似一行代码的事,但省掉的是现场最常见的一次多余点击。

③ 打开与关闭是同一个按钮。 按钮文案随连接状态在「打开串口」和「关闭串口」之间切换,避免两个并列按钮带来的误操作空间------串口调试现场最不想干的事,就是在设备正在跑的时候手滑点错。

④ 状态栏把实际生效参数摊开。 打开成功后状态栏会拼出完整一行:

text 复制代码
/dev/ttyUSB0 已打开 115200 8-None-1

端口、波特率、数据位-校验-停止位一次给全。串口调不通时,第一步永远是确认"我以为的参数"和"实际生效的参数"是不是一回事,这行字就是为了把这个确认成本压到最低。

⑤ 波特率用固定档位下拉,不自由输入。 常用档位从 3002000000 直接选,默认保持 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 termiosopenO_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_USBohos.permission.ACCESS_DDK_USB_SERIAL,但声明权限并不等于所有设备都能直接访问 。真机上还要考虑 USB 授权、设备热插拔、系统签名、普通应用权限级别和 /dev/tty* 节点可见性。

这也解释了为什么鸿蒙 PC 版的截图保留的是无设备状态:这不是"没来得及接设备",而是这个路径在现场非常常见,也最能看出应用是否处理得稳定。等接入真实 USB 转串口后,用户只需要刷新列表、选设备、确认波特率,再打开连接即可。

难点四:PC 窗口体验需要重新组织

手机应用常见的单列页面并不适合串口调试。PC 用户更习惯一边看接收日志,一边调整参数和发送数据。鸿蒙 PC 版本采用左侧参数、右侧日志和发送区的布局,并把状态栏固定在底部,减少来回切页。

工程侧对应的是窗口配置:supportWindowMode 开放 fullscreenfloating 两种模式,最小窗口 800×600、最大高度 1600。适配时还要注意系统避让区、桌面任务栏和浮窗形态,保证在真实 PC 桌面上不遮挡核心控件。

难点五:构建环境要和工程配置严格匹配

鸿蒙项目对 SDK、签名材料、Hvigor、CMake、NDK linker 和 Rust target 的匹配要求比较高。本工程面向 HarmonyOS SDK 6.1.0(23) 配置(compatibleSdkVersiontargetSdkVersion 一致)、目标 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_rsdecode_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.mdCHANGELOG.mdCOMMITTERS.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 都在这里)

https://atomgit.com/xiaohong-ai/sscom

bash 复制代码
git clone https://atomgit.com/xiaohong-ai/sscom.git

开源鸿蒙 PC 社区

https://harmonypc.csdn.net/

项目申请入口:https://atomgit.com/OpenHarmonyPCDeveloper

旋武社区

SSCom 是用 Rust 写的,而国内 Rust 生态的组织化阵地正是开放原子旋武开源社区。旋武社区是开放原子开源基金会下的 Rust 中国社区,由 9 家会员单位共建,提供一站式 Rust 发行版、学习资料、在线编码体验,同时孵化 Rust 开源项目。

如果你对 Rust 感兴趣,或者正在做 Rust + 终端 / 嵌入式 / 开源鸿蒙方向的东西,欢迎去社区看看:

旋武社区https://xuanwu.openatom.org

社区里能拿到的东西挺实在:Rust 快速上手发行版(图形化安装 + 命令行一键安装,覆盖各系统与 CPU 架构)、Rust 在线体验环境、系列课程视频,以及「旋武社区 Rust 开源项目推荐」这类项目内容栏目。SSCom 也期待在社区里遇到更多同方向的开发者------不管是提 Issue、交 PR,还是聊聊你和串口调试工具的那些坑。

项目交流群

用微信扫码加入 SSCom 项目交流群,反馈问题、分享使用心得:

十一、与上游项目的关系与许可

SSCom 是开源串口调试助手 sscom 的 Rust 重构版 :在相近的使用场景下,用 Rust + egui 重新实现串口收发、编码与常见调试能力,不拷贝上游 Qt 源码。

相比上游的主要改进:统一 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 ⭐

相关推荐
李游Leo1 小时前
HarmonyOS 7 端侧 AI 视觉能力实战 06:把识别、增强与搜索串成完整处理链
harmonyos
大雷神1 小时前
【共创稿事节】ArkGraphics 3D开发环境安装实战:从官网下载编辑器,到 DevEco Studio 离线装插件
harmonyos
李游Leo1 小时前
HarmonyOS 7 实战开发 02:用状态驱动复杂页面交互
harmonyos
大雷神1 小时前
【共创稿事节】HarmonyOS ArkGraphics 3D 实操——给智能音箱做一台结构探索台
harmonyos
李游Leo2 小时前
HarmonyOS 7 性能优化:List 长列表懒加载与缓存实践
harmonyos
威哥爱编程3 小时前
HarmonyOS 碰一碰与隔空传送实战:真正的坑在 3 秒铁律和生命周期
harmonyos
威哥爱编程3 小时前
HarmonyOS 扫码直达接入实战:系统扫、应用落,三步送用户进履约页
华为·harmonyos·arkts
Kapaseker3 小时前
Rust 为什么越来越受欢迎?开发者角度谈
rust
威哥爱编程3 小时前
HarmonyOS 6.0 智感握姿实战:一道安检门、五态分诊、一静一响两个坑
华为·harmonyos·arkts