<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[# 🎬 用ESP32-S3实施廉价KVM-over-IP — 完整折腾报告]]></title><description><![CDATA[<blockquote>
<p dir="auto"><strong>项目仓库:</strong> <a href="https://github.com/peterhon168/esp32-kvm-webcontrol" rel="nofollow ugc">https://github.com/peterhon168/esp32-kvm-webcontrol</a><br />
<strong>源项目:</strong> <a href="https://github.com/KMChris/esp32-kvm-ip" rel="nofollow ugc">KMChris/esp32-kvm-ip</a><br />
<strong>日期:</strong> 2026-06-18</p>
</blockquote>
<hr />
<h1>第一部分：技术报告</h1>
<h2>一、缘起：为什么要做这个？</h2>
<p dir="auto">远程管理服务器通常需要：</p>
<ul>
<li><strong>iDRAC/iLO/iBMC</strong> — 企业级方案，贵，老主板没有（我手上另一块超微的X10-Dai也一样没有）</li>
<li><strong>PiKVM</strong> — 好方案，但树莓派被炒到天价，而且HDMI转CSI模块也不便宜</li>
<li><strong>串口/SSH</strong> — 能敲命令，但看不到BIOS、看不到启动过程、装系统抓瞎</li>
</ul>
<p dir="auto">目标：用 <strong>ESP32-S3（¥25）+ USB HDMI采集卡（¥25）</strong> 实现一个能看画面、能键鼠操作的远程KVM（供货商为PDD）。</p>
<h2>二、硬件清单 &amp; 成本</h2>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="text-align:left">组件</th>
<th style="text-align:left">型号</th>
<th style="text-align:left">成本</th>
<th style="text-align:left">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">主控</td>
<td style="text-align:left">ESP32-S3 N16R8</td>
<td style="text-align:left">~¥25</td>
<td style="text-align:left">双核240MHz，USB OTG，WiFi</td>
</tr>
<tr>
<td style="text-align:left">采集卡</td>
<td style="text-align:left">MS2103 USB HDMI (345f:2130)</td>
<td style="text-align:left">~¥25</td>
<td style="text-align:left">USB 3.0，支持1080p@30 MJPEG</td>
</tr>
<tr>
<td style="text-align:left">目标机</td>
<td style="text-align:left">X99双路工作站</td>
<td style="text-align:left">已有</td>
<td style="text-align:left">被控机器，HDMI+USB接入</td>
</tr>
<tr>
<td style="text-align:left">宿主主机</td>
<td style="text-align:left">TrueNAS SCALE / Ubuntu VM</td>
<td style="text-align:left">已有</td>
<td style="text-align:left">跑流服务+网桥</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>总硬件增量成本: ¥80</strong>（相比PiKVM动辄¥500+）</p>
<h2>三、系统架构</h2>
<pre><code>┌───────────────┐   HDMI    ┌──────────────────┐
│  目标机        │──────────→│ USB 2103 采集卡   │
│  (X99 工作站)  │           │ (MS2103芯片)      │
│               │   USB     │                   │
│               │──────────→│   ESP32-S3        │
└───────────────┘           │   (HID注入)       │
                            └────────┬─────────┘
                                     │ WiFi UDP :4210
                                     ▼
┌────────────────────────────────────────────────────┐
│              网络层                                 │
├────────────────────────────────────────────────────┤
│  KVM网桥 (Python): WebSocket ←→ UDP 转换           │
│  视频流: ffmpeg → Python MJPEG HTTP Server :8000    │
│  Web UI: http://kvm-bridge-ip:18088                 │
└────────────────────────────────────────────────────┘
</code></pre>
<h3>数据流</h3>
<pre><code>浏览器 ←WebSocket JSON→ KVM网桥 ←UDP 16字节包→ ESP32-S3 ←USB HID→ 目标机
浏览器 ←HTTP MJPEG──→ MJPEG流服务器 ←pipe── ffmpeg ←v4l2── 采集卡 ←HDMI── 目标机
</code></pre>
<h3>延迟实测</h3>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="text-align:left">环节</th>
<th style="text-align:left">延迟</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">视频采集 → 串流</td>
<td style="text-align:left">~33ms (30fps)</td>
</tr>
<tr>
<td style="text-align:left">网络传输</td>
<td style="text-align:left">&lt;1ms (局域网)</td>
</tr>
<tr>
<td style="text-align:left">鼠标键盘注入</td>
<td style="text-align:left">~5ms (UDP→ESP32→USB)</td>
</tr>
<tr>
<td style="text-align:left">端到端画面延迟</td>
<td style="text-align:left">~100-150ms (不含显示器)</td>
</tr>
</tbody>
</table>
<h2>四、ESP32-S3 固件</h2>
<h3>4.1 固件修改（vs 源项目）</h3>
<p dir="auto">源项目 <code>KMChris/esp32-kvm-ip</code> 原本设计是一个Windows客户端通过UDP直连ESP32，需要Windows跑一个hook程序捕获键鼠。我们改成了：</p>
<ol>
<li><strong>WiFi修复</strong>：认证模式 <code>WIFI_AUTH_WPA2_WPA3_PSK</code> → <code>WIFI_AUTH_WPA2_PSK</code>
<ul>
<li>原因：sdkconfig没开WPA3，连WiFi永远失败</li>
</ul>
</li>
<li><strong>重连逻辑修复</strong>：把事件回调内的 <code>vTaskDelay</code> 改为独立 <code>reconnect_task</code>
<ul>
<li>原因：WiFi事件循环内阻塞导致后续事件无法处理</li>
</ul>
</li>
<li><strong>UDP协议不变</strong>：16字节固定包格式，兼容原始协议</li>
</ol>
<h3>4.2 编译烧录</h3>
<pre><code class="language-bash"># 在Windows ESP-IDF环境
idf.py build flash monitor
# OTG口接目标机USB，UART口接电脑（可以同时插）
</code></pre>
<h3>4.3 ESP32 IP分配</h3>
<p dir="auto">ESP32连WiFi后通过DHCP获取IP。在我们的网络里分配到 <strong>192.168.0.209</strong>。</p>
<h2>五、USB HDMI采集卡 — MS2103 深坑记录</h2>
<h3>5.1 芯片识别</h3>
<pre><code>ID 345f:2130 MACROSILICON OCap Video
UVC 1.00, USB 3.0 SuperSpeed
支持格式:
  Raw:    YUYV 4:2:2 最高 3840×2160
  MJPEG:  Motion-JPEG 最高 3840×2160
</code></pre>
<h3>5.2 血泪坑：v4l2不兼容</h3>
<p dir="auto">这个MS2103芯片是出了名的奇葩——<strong>标准v4l2 ioctl全都不吃</strong>：</p>
<ul>
<li><code>VIDIOC_S_FMT</code> → <code>Inappropriate ioctl for device</code></li>
<li><code>VIDIOC_G_FMT</code> → 同上</li>
<li><code>VIDIOC_REQBUFS</code> → 同上</li>
<li><code>VIDIOC_STREAMON</code> → 同上</li>
<li>直接 <code>read()</code> → <code>Invalid argument</code></li>
</ul>
<p dir="auto">市面上唯一能驱动它的只有 <strong>ffmpeg</strong>（内置了MS210x的workaround）。</p>
<h3>5.3 最终方案</h3>
<pre><code class="language-bash"># 下载静态ffmpeg二进制（John Van Sickle编译版）
wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
tar -xf ffmpeg-release-amd64-static.tar.xz

# 捕获MJPEG帧并通过Python服务推流
ffmpeg -f v4l2 -input_format mjpeg -video_size 1920x1080 -framerate 30 \
  -i /dev/video0 -f image2pipe -vcodec copy - | python3 mjpeg_server.py
</code></pre>
<p dir="auto">Python MJPEG服务器用纯标准库（http.server + queue + threading），不需要装任何第三方包。</p>
<h2>六、宿主机的选择折腾</h2>
<h3>6.1 尝试路线</h3>
<p dir="auto">（Hermes Agent的宿主机truenas，ubuntu是.240，没有给他授权root权限，所以下面报了一堆错）</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="text-align:left">路线</th>
<th style="text-align:left">结果</th>
<th style="text-align:left">原因</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">直接插240 Ubuntu VM</td>
<td style="text-align:left"><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=9a87c0a6150" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> 不行</td>
<td style="text-align:left">VM没有物理USB口</td>
</tr>
<tr>
<td style="text-align:left">TrueNAS USB Passthrough到240</td>
<td style="text-align:left"><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=9a87c0a6150" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> 热插不生效</td>
<td style="text-align:left">需要重启VM，但Hermes网关在240上不能断</td>
</tr>
<tr>
<td style="text-align:left">TrueNAS直接装uStreamer(apt)</td>
<td style="text-align:left"><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=9a87c0a6150" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> 封锁</td>
<td style="text-align:left">TrueNAS策略禁用包管理器</td>
</tr>
<tr>
<td style="text-align:left">TrueNAS Docker</td>
<td style="text-align:left"><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=9a87c0a6150" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> 封锁</td>
<td style="text-align:left">dockerd需要root</td>
</tr>
<tr>
<td style="text-align:left">TrueNAS Python原生流服务</td>
<td style="text-align:left"><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=9a87c0a6150" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> 成功</td>
<td style="text-align:left">/var/tmp可写、有Python3.11+PIL、无noexec</td>
</tr>
<tr>
<td style="text-align:left">ffmpeg静态二进制跑在TrueNAS</td>
<td style="text-align:left"><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=9a87c0a6150" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> 成功</td>
<td style="text-align:left">下载到/var/tmp，可执行</td>
</tr>
<tr>
<td style="text-align:left">TrueNAS Init Script开机自启</td>
<td style="text-align:left"><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=9a87c0a6150" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> 成功</td>
<td style="text-align:left">midclt call initshutdownscript.create</td>
</tr>
</tbody>
</table>
<h3>6.2 架构决策</h3>
<p dir="auto">采集卡最终插在 <strong>TrueNAS (192.168.0.160)</strong> 上，因为：</p>
<ul>
<li>TrueNAS 有 USB 3.0 口</li>
<li>采集卡通过 VM USB Passthrough 热添加不成功（其实是成功的，全过程没有重启宿主机及VM）nid</li>
<li>TrueNAS 的 <code>/var</code> 是 ZFS 数据集（非 tmpfs），文件重启不丢</li>
<li>Python3.11 + PIL 可用</li>
<li><code>/var/tmp</code> 没挂 <code>noexec</code></li>
</ul>
<h3>6.3 开机自启配置（TrueNAS Init Script）</h3>
<pre><code class="language-bash">midclt call initshutdownscript.create '{
  "type": "SCRIPT",
  "script": "/var/tmp/mjpeg_server.py",
  "when": "POSTINIT",
  "enabled": true,
  "timeout": 60
}'
</code></pre>
<h2>七、KVM网桥</h2>
<h3>7.1 kvm_bridge 架构</h3>
<pre><code class="language-python"># server.py — 核心逻辑
# 1. WebSocket服务器 → 接收浏览器键鼠事件(JSON)
# 2. UDP客户端 → 转发给ESP32(16字节二进制包)
# 3. HTTP服务器 → 提供Web UI + 配置API

# 配置 config.env
ESP_IP=192.168.0.209        # ESP32 IP
ESP_PORT=4210                # ESP32 UDP端口
WS_PORT=18765                # WebSocket端口
HTTP_PORT=18088              # Web UI端口
USTREAMER_URL=http://192.168.0.160:8000/stream  # MJPEG视频流
</code></pre>
<h3>7.2 Web UI 特性</h3>
<ul>
<li>单页面HTML + CSS + JS（无框架依赖）</li>
<li>WebSocket自动重连</li>
<li>鼠标 Pointer Lock API（按Alt+L切换锁定/解锁）</li>
<li>BIOS兼容（相对坐标模式）</li>
<li>视频流iframe内嵌uStreamer画面</li>
<li>连通性检测（ESP32在线→绿，离线→黄色警告）</li>
</ul>
<h3>7.3 PM2进程管理</h3>
<pre><code class="language-bash">pm2 start server.py --name kvm-bridge
pm2 save
</code></pre>
<h2>八、完整搭建步骤（从零开始）</h2>
<h3>Step 1: 硬件连接</h3>
<pre><code>目标机HDMI → 采集卡 → 宿主机USB
目标机USB  → ESP32-S3 OTG口
ESP32-S3 UART口 → 电脑（烧录用，运行时不用）
</code></pre>
<h3>Step 2: 烧录ESP32固件</h3>
<pre><code class="language-bash"># Windows + ESP-IDF
idf.py build flash monitor
# 记下ESP32的IP（从路由器DHCP表查）
</code></pre>
<h3>Step 3: 宿主机上部署流服务</h3>
<pre><code class="language-bash"># 如果是Ubuntu（最简单）
apt install ustreamer
ustreamer -d /dev/video0 -m MJPEG -p 8000 -r 1920x1080

# 如果是TrueNAS（需要折腾）
# 下载ffmpeg静态二进制 + Python MJPEG服务器脚本
# 部署到 /var/tmp/，配Init Script开机自启
</code></pre>
<h3>Step 4: 部署KVM网桥</h3>
<pre><code class="language-bash"># 任何有Python3的机器
pip3 install websockets
cp config.env.example config.env
# 编辑 config.env 填入ESP32 IP和流URL
python3 server.py

# 或通过PM2守护
pm2 start server.py --name kvm-bridge
</code></pre>
<h3>Step 5: 打开浏览器</h3>
<pre><code>http://&lt;bridge-ip&gt;:18088
→ 点击「连接」建立WebSocket
→ 点击「加载」显示视频流
→ 点击视频区域 → 锁定鼠标 → 开始操作
</code></pre>
<h2>九、避坑总结</h2>
<pre><code>┌─────────────────────────────────────────────────────────────┐
│ 🕳️ 坑1: MS2103采集卡标准v4l2 ioctl不能用                   │
│    ✅ 解决: 只能用ffmpeg（有内置workaround）                 │
│                                                             │
│ 🕳️ 坑2: TrueNAS是"安全加固"系统                            │
│    ✅ 解决: apt/Docker都被锁，用静态二进制+Python纯std库    │
│                                                             │
│ 🕳️ 坑3: TrueNAS默认不许跑二进制(noexec)                    │
│    ✅ 解决: /var/tmp 没有noexec，放那                      │
│                                                             │
│ 🕳️ 坑4: VM USB热插不生效                                   │
│    ✅ 解决: 既然Hermes在VM上不能重启，物理机直插跑服务      │
│                                                             │
│ 🕳️ 坑5: ESP32连不上WiFi                                    │
│    ✅ 解决: 固件认证改 WPA2_PSK，重连改独立task             │
│                                                             │
│ 🕳️ 坑6: ESP32断线后不自动重连                              │
│    ✅ 解决: 事件回调内vTaskDelay阻塞 → 独立reconnect_task   │
└─────────────────────────────────────────────────────────────┘
</code></pre>
<blockquote>
<p dir="auto"><strong>项目地址:</strong> <a href="https://github.com/peterhon168/esp32-kvm-webcontrol" rel="nofollow ugc">https://github.com/peterhon168/esp32-kvm-webcontrol</a><br />
<strong>参考:</strong> <a href="https://github.com/KMChris/esp32-kvm-ip" rel="nofollow ugc">https://github.com/KMChris/esp32-kvm-ip</a><br />
<strong>采集卡问题参考:</strong> <a href="https://www.mjt.me.uk/posts/fixing-missing-macrosilicon-ms2109/" rel="nofollow ugc">https://www.mjt.me.uk/posts/fixing-missing-macrosilicon-ms2109/</a></p>
</blockquote>
<p dir="auto"><img src="https://upload.lcz.me/uploads/7e5cb498-64c9-49c7-abf8-b87bcdc407d7.jpeg" alt="c8e2170e-2cd4-4d69-a2cb-d17862727432-image.jpeg" class=" img-fluid img-markdown" /><br />
<img src="https://upload.lcz.me/uploads/f0dcac58-7fdc-400b-9e0c-c52839a586fd.jpeg" alt="4db94f99-dabd-4091-9857-b62d5f934b5b-image.jpeg" class=" img-fluid img-markdown" /></p>
]]></description><link>https://lcz.me/topic/607/用esp32-s3实施廉价kvm-over-ip-完整折腾报告</link><generator>RSS for Node</generator><lastBuildDate>Wed, 01 Jul 2026 16:54:34 GMT</lastBuildDate><atom:link href="https://lcz.me/topic/607.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 18 Jun 2026 06:34:49 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to # 🎬 用ESP32-S3实施廉价KVM-over-IP — 完整折腾报告 on Sun, 21 Jun 2026 08:05:06 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/capri-swicord" aria-label="Profile: Capri-Swicord">@<bdi>Capri-Swicord</bdi></a> 哈哈，这个可以有！</p>
]]></description><link>https://lcz.me/post/7667</link><guid isPermaLink="true">https://lcz.me/post/7667</guid><dc:creator><![CDATA[abaalei]]></dc:creator><pubDate>Sun, 21 Jun 2026 08:05:06 GMT</pubDate></item><item><title><![CDATA[Reply to # 🎬 用ESP32-S3实施廉价KVM-over-IP — 完整折腾报告 on Sun, 21 Jun 2026 07:50:37 GMT]]></title><description><![CDATA[<p dir="auto">666，像我这种不愿意折腾的，买维谛的av3108，加上vga适配器</p>
]]></description><link>https://lcz.me/post/7666</link><guid isPermaLink="true">https://lcz.me/post/7666</guid><dc:creator><![CDATA[hyaska]]></dc:creator><pubDate>Sun, 21 Jun 2026 07:50:37 GMT</pubDate></item><item><title><![CDATA[Reply to # 🎬 用ESP32-S3实施廉价KVM-over-IP — 完整折腾报告 on Sun, 21 Jun 2026 03:44:13 GMT]]></title><description><![CDATA[<p dir="auto">牛逼，建议整个套件挂淘宝，多一个选择</p>
]]></description><link>https://lcz.me/post/7656</link><guid isPermaLink="true">https://lcz.me/post/7656</guid><dc:creator><![CDATA[Capri Swicord]]></dc:creator><pubDate>Sun, 21 Jun 2026 03:44:13 GMT</pubDate></item><item><title><![CDATA[Reply to # 🎬 用ESP32-S3实施廉价KVM-over-IP — 完整折腾报告 on Fri, 19 Jun 2026 09:27:20 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/abaalei" aria-label="Profile: abaalei">@<bdi>abaalei</bdi></a> 很利害了</p>
]]></description><link>https://lcz.me/post/7478</link><guid isPermaLink="true">https://lcz.me/post/7478</guid><dc:creator><![CDATA[fly86]]></dc:creator><pubDate>Fri, 19 Jun 2026 09:27:20 GMT</pubDate></item><item><title><![CDATA[Reply to # 🎬 用ESP32-S3实施廉价KVM-over-IP — 完整折腾报告 on Fri, 19 Jun 2026 06:09:40 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/fly86" aria-label="Profile: fly86">@<bdi>fly86</bdi></a> 没有，S3在这套系统内就只作为虚拟键盘使用，就是因为它的性能不够，才需要调用truenas中闲置的性能来推流，不过我这都是穷折腾罢了</p>
]]></description><link>https://lcz.me/post/7468</link><guid isPermaLink="true">https://lcz.me/post/7468</guid><dc:creator><![CDATA[abaalei]]></dc:creator><pubDate>Fri, 19 Jun 2026 06:09:40 GMT</pubDate></item><item><title><![CDATA[Reply to # 🎬 用ESP32-S3实施廉价KVM-over-IP — 完整折腾报告 on Fri, 19 Jun 2026 03:01:30 GMT]]></title><description><![CDATA[<p dir="auto">牛逼，S3这么强</p>
]]></description><link>https://lcz.me/post/7427</link><guid isPermaLink="true">https://lcz.me/post/7427</guid><dc:creator><![CDATA[fly86]]></dc:creator><pubDate>Fri, 19 Jun 2026 03:01:30 GMT</pubDate></item><item><title><![CDATA[Reply to # 🎬 用ESP32-S3实施廉价KVM-over-IP — 完整折腾报告 on Fri, 19 Jun 2026 02:58:01 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/cs6" aria-label="Profile: CS6">@<bdi>CS6</bdi></a> 不用不用，我也是在youtube看到有博主提了一嘴（在一条评测10元？20元？显卡的那里），然后跟gemini讨论，获得了操作建议，然后找kiro对源仓库代码进行修改，然后最终实施部署就给hermes进行</p>
]]></description><link>https://lcz.me/post/7425</link><guid isPermaLink="true">https://lcz.me/post/7425</guid><dc:creator><![CDATA[abaalei]]></dc:creator><pubDate>Fri, 19 Jun 2026 02:58:01 GMT</pubDate></item><item><title><![CDATA[Reply to # 🎬 用ESP32-S3实施廉价KVM-over-IP — 完整折腾报告 on Thu, 18 Jun 2026 19:50:37 GMT]]></title><description><![CDATA[<p dir="auto">大佬請收下我膝蓋，我偷懶直接買了  IPKVM</p>
]]></description><link>https://lcz.me/post/7382</link><guid isPermaLink="true">https://lcz.me/post/7382</guid><dc:creator><![CDATA[CS6]]></dc:creator><pubDate>Thu, 18 Jun 2026 19:50:37 GMT</pubDate></item></channel></rss>