DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

60studay /

60studay/dsh-serial-whalesong

Verified

DSH ????(serial/UART/COM):9 ??? + ???,AI ???????????????????/????????????????????????? CH340 / HC-05?

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHubProject homepage
READMESource: main@1315f9da

鲸歌 · dsh-whalesong(测试版)

English: a serial-port plugin for DeepSeek Harness (DSH) — 9 tools + a web UI, so an AI agent and a human can share one UART line. Keywords: serial · serial port · COM port · UART · RS232 · USB-serial · CH340 · Modbus · AI agent

一句话

让 AI 真的能动手 —— 不是点一下网页上的按钮,是把指令发进一根真实的线里。

它解决的是哪件事

你的板子就在桌上,AI 在屏幕里。中间只差一根 USB 线和一个能让双方都说话的人。

鲸歌就是那个"人":9 个工具 + 一个网页,人和 AI 共用同一根串口线。 谁发的命令、设备回了什么、哪条超时了,一行不落,两个方向都看得见。

为什么值得装

别人演示硬件是"我发指令,它亮了"。 你演示的是"我说一句话,它自己发指令、读回显、发现不对、再问一句"。

这就是 agent 和"高级串口助手"的区别:它能闭环。而闭环的第一步,是让它摸得到真实的字节。

规则(就三条)

  1. 通用:不认识任何一块特定的板子。设备知识全在 protocols/<场景>/config.json,换硬件不改代码。
  2. 人优先:AI 默认不能发命令。你说"人类接管",它立刻停手 —— 这条不是配置项。
  3. 看得见:每一帧收发都有来源(AI / 我 / 定时 / 规则 / 设备)和时间戳;出错只记日志,不吞掉。

目的

把它做成一根所有 AI 都能用的线:不绑 DSH,不绑某块板,不绑某种芯片。 今天它只认真验证过 CH340 和 HC-05 —— 所以我把测试版放出来,借你的设备,把它磨到能用。


虚拟与现实的桥梁,说起来虚。 具体一点:DeepSeek 的鲸鱼娘,从聊天窗口里走出来,第一次摸到了真实世界的电平。 这一步,就是从这根线开始的。

🫱 一句实话:上面这个结尾是冲着"让人愿意点进来"写的,我承认。 但底下的代码是真的 —— 自检 450+ 项、真机验过、每条修复都有提交号,你可以一条条翻。


⚠️ 测试版说明

本插件目前是测试版,不代表最终版本。 功能和接口都可能变动,请勿用于生产环境。

使用中如遇到问题、发现 bug,或有改进建议,欢迎联系:

B 站账号 @60流时留时

反馈时如果能附上:串口型号、设备类型、报错信息、复现步骤,会更容易定位问题。


🧪 验证范围(重要,请先看这里)

目前只有 CH340 一种串口芯片、配合 HC-05 蓝牙模块做过真机验证。

已真机验证 说明
CH340(USB 转串口,VID:PID 1A86:7523) 完整验证:二进制收发、921600 高波特率、1024 字节长帧、连续 100 条零错位
HC-05 蓝牙 SPP 透传 主从配对、AT 配置、透传收发

以下均未验证,可能存在兼容性问题:

未验证项 风险
CP210x / FT232 / PL2303 等其他 USB 转串口芯片 端口匹配逻辑通用,但未实测
板载 UART 直连(非 USB 转串口) 未实测
WiFi / TCP 透传模块 尚未实现(规划中)
CAN / SPI / I2C 总线 尚未实现(规划中)
GBK 等非 UTF-8 编码设备 会显示乱码(规划中)
RTS/CTS 硬件流控 尚不支持
Linux / macOS 只在 Windows 上验证过

为什么开源这个测试版

这个插件要真正完善,需要大量真实硬件和实践样本 —— 各种芯片、各种协议、各种奇葩设备。 一个人没那么多设备,也没那么多时间。

所以把测试版放出来,希望有设备的人一起测。 你遇到问题提 issue, 我这边改,改完大家都能用。这是这类工具类插件最现实的完善方式。

如果你测了某个设备能用,欢迎告诉我,我会把结果补进上面的表格,让后面的人少踩坑。


给 DSH 装上串口能力:装一次,之后每个新会话都能直接收发串口。 这是一个通用串口工具,不认识任何特定设备——设备知识全在 protocols/<场景>/ 里。

📌 开发进度记录:本项目用 _需求文档/进度总账.md 记录"现在做到哪儿了" (当前版本、已完成、未完成、卡在哪、踩过的坑)。 贡献者/接手者请先读它,再看 _需求文档/代码地图.md 找代码位置。

⚠️ 升级 DSH 之前(1.29-c)

先在隔离实例上验一次,别在唯一那个实例上试。

dsh --profile web --no-open --port 8905     # 拿它返回的带 token 的 URL 打开

为什么写死这一条:2026-11-08 实测,一个插件的客户端 bundle 抛一次异常,就能把整个 DSH 界面拖在 「Loading plugins…」不动(聊天区、输入框全没有)。这不是鲸歌独有的问题,是加载器的行为。 所以我们能做的只有两件事:自己这一半永远不向外抛,以及给你一条能立刻恢复的路。

设置面板出问题时:安全模式

鲸歌的浏览器半端(DSH 里那个「自动打开网页」设置页)坏了,不会影响 9 个工具,但你可能想干脆不加载它:

  • 打开页面时在 URL 后面加 ?ws-safe=1,或
  • 浏览器控制台执行 localStorage.setItem('dsh-whalesong.safe', '1')

这两种情况下,插件只注册工具、跳过面板。恢复:去掉 ?ws-safe=1 或 localStorage.removeItem('dsh-whalesong.safe')。

9 个工具

工具 作用
serial_list 列出串口(含 VID:PID)+ 每个场景现在会落到哪个口
serial_send 发一条命令,返回回显(文字 + hex),端口每次自动重新匹配;wait:false 可发完就走
serial_read 回看该场景最近的收发日志
serial_info 场景参数、匹配规则、watch 配置、完整协议档案
serial_status 控制权、AI 现在能不能发、最近告警
serial_watch 读/改两个独立的监视开关(无需开网页)
serial_control 交接控制权(无需开网页)
serial_daemon 启停管家(常驻持有连接,连发多条命令时用)
serial_web 开关/查询可视化网页,直接拿到可点的 URL(后台运行,不阻塞调用)

设计原则:默认不常驻、不占口。 每次调用临时打开串口 → 收发 → 立刻关闭, 不会和串口助手抢 COM 口。

需要连发多条命令时,可以让 AI 起一个管家(常驻持有连接),见下节。

网页(可选:可视化)

python bridge.py webctl --action open  --scene 蓝牙AT   # 后台起,返回可点 URL
python bridge.py webctl --action status
python bridge.py webctl --action close                  # 停掉并**真的释放端口**

AI 侧对应工具 serial_web。三个特点:

  • 幂等:同场景已有实例就直接复用,不会起第二个(两个进程会抢串口)
  • 自动挑端口:从 8898 起找可用端口(8899 常被 DSH 自己占着),返回真实端口
  • 不阻塞:后台子进程运行,工具调用立刻拿到 URL,可以直接贴给人点

管家(可选:常驻连接)

python bridge.py daemon --scene 蓝牙AT --action start   # 起(幂等,重复调不会起第二个)
python bridge.py daemon --scene 蓝牙AT --action status
python bridge.py daemon --scene 蓝牙AT --action stop

AI 侧对应工具 serial_daemon。

  • 管家在跑时,serial_send 自动走它(via=daemon),命令提交耗时降到 0~2ms
  • 端口在管家运行期间被占用,串口助手打不开;空闲 10 分钟自动退出并释放(--idle 可调)
  • 网页没开也照样能用——速度不取决于网页
  • 注意:回包耗时仍受设备限制。本仓库验证用的底盘首字节就要 500~600ms (X/H/A/P 实测都一样),那是硬件,不是软件开销。

v2 新增能力

1. 串口自动匹配(不再怕换口)

场景配置里按优先级匹配,永远先扫描:

{
  "com": "COM7",                // 首选:精确号(可能过期)
  "match": "CH340",             // 次选:描述模糊匹配(不区分大小写)
  "match_vid_pid": "1A86:7523"  // 最精确:VID:PID
}

顺序:match_vid_pid → match → com。匹配到多个会列出候选让你选。 自动纠正:配置写 COM7、实际在 COM10,会直接用 COM10 并在返回值里注明。

实测:写这份文档期间设备正好从 COM7 换到了 COM10,插件自动跟上了。

2. 三类故障分开报

情况 你会看到
设备没插 没找到匹配的串口设备 + 请检查 USB/驱动 + 当前可用串口清单
口被占用 COM10 被其他程序占用 + 提示关掉串口助手/监视模式
设备不回 已连接 COM10,但设备无响应 + 提示查波特率/供电/TX-RX

Python 堆栈永远不会出现在你面前。

3. 网页版不长期占口 + 退出按钮

python bridge.py web --scene <场景>            # 默认轻模式,不占口
python bridge.py web --scene <场景> --mode watch   # 监视模式,占口但能看连续流
能力 轻模式(默认) 监视模式
发命令看回复 ✅ ✅
与 AI 的 serial_send 共存 ✅ ❌(会占口)
与你的串口助手共存 ✅ ❌
看连续数据流 / 主动上报 ❌ 会漏(可点「读一次」临时读一帧) ✅

轻模式下网页不会主动读串口:/poll 直接返回跳过,只有你点「读一次」才临时开口。 这样网页开着的时候,串口助手和 AI 都不会被挤开。

右上角 [彻底退出] 会停服务并立即放开 COM 口; 只想临时把口让给别人,用侧栏的 [暂停并释放串口](网页还留着)。

网页还有:串口下拉框、波特率下拉框、[AI 自动选择]、文字/十六进制切换、 连接模式切换、两个监视开关、四键控制权、可滚动的状态轨道。

4. 控制权状态机

human ──[交给AI]──→ ai ──[暂停AI]──→ paused
  ↑                  │                 │
  └───[人类接管]──────┴───[继续AI]─────┘
  • 状态存在共享文件里,网页和插件进程看到的是同一份
  • 人类命令永远优先:AI 托管时你发任何命令,立即拉回 human
  • 按钮合法性严格按文档的按钮表(12 种组合都已实测)
  • 重复点同一个按钮是幂等的(返回 200 + "已经在目标状态"),不会报 400

门禁:只有 ai 才允许 AI 发命令

serial_send 执行前必查控制权,human / paused 一律拒绝,并给出可操作的下一步:

控制权 AI 能否发 被拒绝时提示你去做什么
ai ✅ —
human ❌ 在网页点[交给AI],或 serial_control action=ai
paused ❌ 在网页点[继续AI],或 serial_control action=resume

早期版本做过"没人开网页就放行"的宽松门禁,实测表明这会让控制权形同虚设 (切到 human 后 AI 照样能发),已取消。现在无论有没有开网页,规则都一样。

要单人使用,直接把控制权设成 ai 即可——网页、serial_control、 命令行 bridge.py control 三条路都能改。

5. 定时询问:两个完全独立的开关

这是 v2 的核心设计。轮询和判定拆开,任一出错都不牵连另一个:

{
  "watch": {
    "send_enabled": false,          // 开关1:是否定时发 command
    "command": "P",
    "interval_ms": 1000,

    "check_enabled": false,         // 开关2:是否对收到的数据跑 rules
    "check_source": "watch_only",   // watch_only / manual_only / both
    "rules": [ ... ],
    "allowed_auto_commands": ["R", "X"]
  }
}
send check 效果
❌ ❌ 纯手动串口助手
✅ ❌ 定时发,自己看回显
❌ ✅ 手动操作 + 自动查异常
✅ ✅ 全自动监控

四种通用判据(没有第五种)

type 参数 含义
contains value 回显包含某字符串
equals value 回显完全等于某值
regex pattern(可选 condition) 正则匹配
no_response consecutive 连续 N 次无回显

隔离(防止规则拖垮收发)

  • 判定跑在常驻子进程里;卡住就 kill 并重启,收发线程不受影响
  • 非法正则只记日志,其它规则照常
  • 同一规则有冷却期,防止触发风暴
  • 连续 N 次判定失败 → 熔断停判定,串口照常收发
  • 自动命令受 allowed_auto_commands 白名单限制,白名单为空则禁止一切自动命令

⚠️ 别把运动类命令加进白名单。

关于"超时保护"的一个重要实现说明:第一版用线程 + join(timeout), 实测会卡死——CPython 的 re 在灾难性回溯期间持有 GIL,主线程连切换都做不到。 所以改成常驻子进程,超时直接 kill()。

6. 告警怎么通知你

DSH 是回合制,AI 无法被主动唤醒。所以:

异常发生 → 写 alerts.jsonl → 网页顶部红条 → 你决定是否叫 AI
                                        → 或 AI 下次被唤起时自己读 serial_status

不会承诺"AI 自动弹消息"。

场景 = 一个硬件

protocols\<场景名>\ 一个目录一个场景,已带两个:

场景 内容
蓝牙AT HC-05 + 底盘命令集(含全部实测记录与踩坑)
文字游戏 暗号表模板
Modbus从站 通用性验证用例:9600 8N1、无换行、hex 命令、正则判据

新增硬件 = 新建目录,不用改代码。判断标准只有一句:

换一台完全不同的串口设备,我需要改代码吗?要改 → 设计错了。

安装

  1. DSH → Plugins → Add plugin
  2. 填 <插件目录>
  3. Install → Enable now
  4. 开新会话,说"列一下串口"

配置

插件级(cordis.patch.yml,Plugins 页可改):

键 说明
pythonPath 跑内核的解释器。默认 DSH 自带;找不到会自动退到 python / py -3
logRoot 日志根目录,默认 <你的日志目录>
protocolRoot 可选:把场景目录指到插件外面

场景级(protocols/<场景>/config.json):

键 默认 说明
com / match / match_vid_pid — 见上文匹配规则
baud 115200 波特率
newline \r\n 每条命令自动补的结尾
wait_first_byte_ms 1500 等首字节上限(设备慢就调大)
quiet_ms 200 线路安静多久算收完
watch.* 见上文 两个开关 + 规则 + 白名单

不用给 Python 装 pyserial:插件自带(vendor/pyserial-3.5,BSD), 通过 PYTHONPATH 注入,不污染目标解释器。

v2.1 修复(实测反馈)

控制权与串口占用已解耦(P0)

以前「人类接管」只改了状态,串口还攥在网页手里——你想开串口助手,得先关网页。 现在控制权决定谁有权发命令,占用决定谁持有句柄,两者分开:

控制权 串口句柄
ai 网页打开并持有(watch 模式)
human 立刻关闭句柄,串口助手可以打开
paused 立刻关闭句柄

释放动作挂在"状态变化"上,不是挂在按钮上

这一点很关键。控制权存在共享文件里,改它的路径有三条:

  1. 网页按钮 POST /control
  2. serial_control 工具(走 CLI 子进程)
  3. 你在终端跑 bridge.py control

如果释放动作只写在网页按钮里,第 2、3 条改完状态后网页根本不知道, 仍然攥着串口——这正是实测反馈里遇到的坑。

所以现在串口占用由一个看门狗每秒对齐一次控制权(reconcile_port), 三条路径改的状态都会被收敛。实测:CLI 把状态改成 human 后, 1 秒内串口就被释放,外部程序随即能打开。

轻模式轮询也让开

轻模式下网页每 700ms 轮询一次 /poll,每次都要临时开口。 human 状态下这个轮询直接返回 skipped,一次都不碰串口。

实测:human 状态下让外部程序连续占用 12 秒(每 0.5 秒开关一次), 24 次打开全部成功,0 次被网页抢走。

暂停 ≠ 退出

按钮 网页 串口
暂停并释放串口 仍可访问 释放
彻底退出 关闭 释放

只想临时把口让给串口助手,点第一个就够了,不用重开网页。

网页可直接编辑规则

侧栏能增删改规则(名称 / 判据 / 自动命令 / 重试次数),保存时写回场景 config.json(保留文件里其它键),写盘前会校验:非法正则、未知判据、 缺字段都会被拒绝并说明原因。

占用者一目了然

状态栏显示 可用 / 被本网页占用 / 被其他程序占用,带具体说明。

/control 的 400 会说清楚缺什么

{"action": ...} 缺字段时返回明确的 config 错误和请求体示例,而不是空白 400。

目录结构

dsh-whalesong\
├── index.js                    工具注册(零设备知识)
├── bridge.py                   启动器
├── serial_bridge\
│   ├── cli.py                  命令行入口,stdout 永远是一行 JSON
│   ├── errors.py               结构化错误(三类故障)
│   ├── ports.py                扫描 + 自动匹配
│   ├── transport.py            传输层:Transport 接口 + SerialTransport(加新方式只加类)
│   ├── io.py                   业务级收发(不 import pyserial,只调 Transport)
│   ├── daemon.py               管家:常驻持有连接 + 本地 HTTP(/send /poll /status)
│   ├── log.py                  TX/RX 日志(CLI 与管家共用)
│   ├── rules.py                四种判据(纯判定,无设备语义)
│   ├── runner.py               常驻判定子进程(超时可 kill)
│   ├── state.py                跨进程控制权 + 告警
│   ├── config.py               公共+场景配置合并
│   └── supervisor.py           网页 / 监视进程
├── web\index.html              网页界面
├── protocols\                  场景:蓝牙AT / 文字游戏 / Modbus从站
├── vendor\pyserial-3.5         自带 pyserial
├── selfcheck_*.py              自测脚本(见下节)
├── _需求文档\                  需求 / 方案 / 验收 / 修复记录
└── _归档\                      已被取代的旧文档

文档在哪

想干什么 看这个
用插件(工具、配置、场景) 本文件
改代码前先看规则 _需求文档/README.md(索引 + 三条铁律)
知道这轮修了什么、为什么 _需求文档/修复记录.md
下一轮要修什么 _需求文档/Bug修复清单.md
验收怎么跑 _需求文档/自检提示词.md
后面要加什么功能 _需求文档/功能规划清单.md
某个功能的详细设计 _需求文档/AI侧增强方案.md / _需求文档/场景提示词设计.md

自测

python selfcheck_rules.py          # 32 项:四种判据 / 非法正则 / 超时 / 白名单 / 限流 / 熔断 / 通用性
python selfcheck_port_hold.py      # 16 项:控制权↔串口句柄 的解耦
python selfcheck_control_gate.py   # 34 项:AI 门禁 + /control 幂等与按钮矩阵
python selfcheck_autosend.py       # 14 项:定时发送 + 轻模式不占口(假设备,无需硬件)
python selfcheck_hex.py            # 11 项:十六进制按原始字节发送(假回环,无需硬件)
python selfcheck_paths.py          # 5 项:Python 与 JS 的默认日志目录必须一致(防 state 分叉)
python selfcheck_transport.py      # 16 项:传输层抽象(换一种传输方式照样能收发)
python selfcheck_daemon.py         # 10 项:管家的提交延迟 / 门禁 / 空闲退出 / 句柄释放(需要硬件)
python selfcheck_watch.py          # 15 项:双开关独立性 / check_source / 规则出错不影响收发(需要硬件)

前七个不需要串口,拔掉设备也能跑;selfcheck_daemon.py / selfcheck_watch.py 找不到设备时会打印 SKIP 并以 0 退出。

已知限制

  • 轻模式默认不读串口,所以设备主动上报的数据会漏——要看连续流请切监视模式, 或点「读一次」临时读一帧。
  • 网页和控制权状态按场景隔离;不同场景之间互不影响。
  • 监视模式占口期间,AI 的 serial_send 会报"被占用",这是预期行为。
  • 同一场景同时只允许一个网页实例(启动时会探测,已有实例则直接退出)。
  • 单次工具调用超时 25 秒。

📋 需求与验收文档

开发相关的需求、方案、验收清单都在 _需求文档\ 目录:

文档 用途
_需求文档\README.md 索引 + 三条铁律(先看这个)
_需求文档\修复记录.md 本轮修了什么、根因、验证证据、未完成项
_需求文档\Bug修复清单.md 待修问题(按性质分为 bug / 设计 / UI)
_需求文档\自检提示词.md 修完后的 7 项验收(复制给 AI 执行)
_需求文档\功能规划清单.md 阶段 2-5 的功能需求
_需求文档\AI侧增强方案.md 阶段 2 详细设计
_需求文档\场景提示词设计.md 阶段 4 详细设计

先看 _需求文档\README.md,里面写了阅读顺序。


许可协议(License)

作者与署名:本项目作者为 刘实(GitHub:@60studay)。

早期提交中出现的 whalesong-dev <dev@local>、dsh-baleen <baleen@local> 是开发过程中使用的临时占位名,实际作者均为刘实。

开发过程的完整记录(两个 AI 的对话存档)见 _对话存档/。

本项目自 v2.1.0 起采用 PolyForm Noncommercial License 1.0.0。

白话说明

你可以 你不可以
个人使用、学习、研究 用于商业用途(卖产品、卖服务、公司内部生产使用)
修改、二次开发 未经许可将本项目用于盈利
分享给朋友 拿本项目去接商业项目赚钱
用于教学、论文、比赛

历史版本授权

v2.0.0(commit 92a2bbd)及更早的版本仍采用 MIT 许可, 不受本协议约束,可以自由使用(含商业用途)。

本协议从 v2.1.0 开始适用。

商业授权

如需商业使用,请联系作者协商授权:

  • B 站:@60流时留时

无论是商业授权、定制开发、特定设备接入适配,还是技术支持,都欢迎联系。

作者能提供什么 / 不能提供什么

作者是个人开发者,不是公司。为了双方预期一致,先说明边界:

能提供的:

项 说明
✅ 设备协议适配 把你的设备的命令表写进场景配置,让插件认识它
✅ 定制功能开发 合理的需求,可以聊
✅ 使用问题解答 装不上、跑不通、报错看不懂,都可以问
✅ Bug 反馈跟进 提 issue,会看会改

不能提供的:

项 说明
❌ 硬件设计 / 生产 / 销售 作者只写软件,不做板子
❌ 7×24 技术支持 学生,有课,回复可能不及时
❌ 大规模部署运维 个人项目,不适合关键生产环境
❌ 承诺交付周期 按兴趣和时间来,不敢接死线

把边界说清楚,合作才愉快。 能力范围内的事,一定尽力;范围外的,也会直说。

为什么用这个协议

作者是个学生,写这个插件花了很多时间和 token。 不指望靠它赚钱,但也不希望别人拿它去赚钱而自己一无所获。

个人用、学习用、玩票用,完全免费,随便用。 只有想拿它做商业产品的情况,才需要联系。

—/ 5

No ratings yet

Verified DSH bundle

Commit 1315f9da5049

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout