换机不翻车:嵌入式开发环境整机迁移复盘
换机不翻车:嵌入式开发环境整机迁移复盘
换新电脑,最怕的不是装软件,而是「装好了却跑不起来」。这次把整套嵌入式开发环境从旧机器搬到新机器:PlatformIO 工具链、ESP32 工程、VMware 虚拟机、Ubuntu 开发机,一路撞上了四个隐藏得很深的坑。这篇文章把每个坑的现象、根因和解法都记下来——下次再搬家,照着做就是一条直线。
#一、背景与任务清单
旧机器留了一份打包目录(约 20GB),包含 PlatformIO 全套工具链(8.7GB)、ESP32 工程(397 个文件)、VMware 虚拟机(31.5GB 磁盘镜像)和各类文档。目标是在新机器上把它们全部恢复到「能用」状态,而不是「看起来装好了」。
| 任务 | 验收标准 |
|---|---|
| PlatformIO 工具链恢复 | pio run 全量编译通过并产出固件 |
| ESP32 工程部署 | RAM / Flash 占用正常打印,无报错 |
| VMware Workstation 安装 | 静默安装 + 数字签名验证通过 |
| Ubuntu 虚拟机迁移 | 开机进桌面 + 宿主机 SSH 免密连通 |
#二、工具链复活:8.7GB PlatformIO
直接把 .platformio 目录拷过来是不够的,有三件事必须做:
① 内核版本不够。拷贝来的平台包要求 Core ≥ 6.1.19,而目录里的 Core 是 6.1.18。解法:用 pip 原地升级 PlatformIO 到 6.2.0,版本对齐后平台包校验直接通过。
② aux 保留名文件。Linux/WSL 侧源码树里有 aux.c、aux.h 这类文件——aux 是 Windows 保留设备名,资源管理器和大多数解压工具根本无法创建它们。解法:用 Git Bash 的 tar 解包(MSYS2 层会正确处理保留名),文件原样落盘。
③ GitHub 直连超时。PIO 要补装一个新版工具链包(xtensa-esp-elf 14.2.0),默认直连 GitHub 必然超时。解法:让 pio 走本机代理客户端的 socks 端口。
powershell# Core 升级
python -m pip install -U platformio
# 编译走本机代理(10808 是代理客户端的 socks 端口)
$env:HTTP_PROXY = 'socks5h://127.0.0.1:10808'
$env:HTTPS_PROXY = 'socks5h://127.0.0.1:10808'
pio run
#三、第一坑:链接器不认中文路径
28 分钟的全量编译走完,所有 .c 文件编译、静态库归档全部成功,最后一步 Linking 突然报错:
textld.exe: cannot open map file D:/嵌入式开发/ESP32/esp32-demo/build/.../firmware.map: Invalid argument
collect2.exe: error: ld returned 1 exit status
编译器(cc1)对中文路径毫无意见,GNU 链接器 ld 写 map 文件时却不认非 ASCII 路径——「Invalid argument」就是这么来的。工程本身不用搬,把构建输出目录指到纯 ASCII 路径即可:
ini; platformio.ini → [platformio] 段
build_dir = C:/pio_build/esp32-demo
改完重新编译:构建缓存全部复用,只剩链接,70 秒出固件:
textRAM: [= ] 6.8% (used 22192 bytes from 327680 bytes)
Flash: [= ] 9.5% (used 317258 bytes from 3342336 bytes)
======================== [SUCCESS] Took 70.56 seconds ========================
#四、第二坑:GBK 与 UTF-8 的暗战
改完 build_dir 反而编译都起不来了,报 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb9。逐层排查(click 调用栈 → configparser → 字节位置 13)后真相大白:
| 环节 | 编码要求 | 实际行为 |
|---|---|---|
| 新版 PlatformIO 读 platformio.ini | 强制 UTF-8 | —— |
| 本机编辑工具写文件 | —— | 默认按系统区域写成 GBK |
「工程配置」四个字的「工」字 GBK 首字节正好是 0xB9——报错的字节位置严丝合缝。教训:换机后第一个要验证的就是本机工具链的默认编码。通用解法(先探测、后转换,避免把 UTF-8 误当 GBK 二次转码):
powershell$p = 'D:\path\platformio.ini'
$bytes = [System.IO.File]::ReadAllBytes($p)
try {
$null = (New-Object System.Text.UTF8Encoding($false, $true)).GetString($bytes)
Write-Host '已经是 UTF-8,无需转换'
} catch {
$text = [System.Text.Encoding]::GetEncoding('GB18030').GetString($bytes)
[System.IO.File]::WriteAllText($p, $text, (New-Object System.Text.UTF8Encoding($false)))
Write-Host 'GBK 已转换为 UTF-8 (无 BOM)'
}
#五、第三坑:VMware 静默安装连环坑
VMware 现在个人免费,但下载要注册 Broadcom 账号,且官方门户国内加载极慢、没有中国镜像。两个替代路径:
- 官方软件更新 CDN(
softwareupdate-prod.broadcom.com)不需要账号,但国内直连和代理都被断 TLS; - 可信镜像站的官方原版包:下载后验证数字签名(右键 → 属性 → 数字签名 → 签名者
Broadcom Inc、状态「有效」),签名通过即为原版。
安装本身的坑更典型。第一层:PowerShell 的 Start-Process -ArgumentList 按数组传参时会把 /v 和它的值拆开,msiexec 收到空属性,静默安装直接静默退出。第二层:换成单字符串精确传参后,MSI 日志显示 VM_ValidateEULAAccepted 返回值 3 → 错误 1603 回滚——EULA 属性没传进去。最终用 ProcessStartInfo 原样传参,并同时带上两种 EULA 属性名:
powershell$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = "$env:USERPROFILE\Downloads\VMware-workstation-full-17.6.4-24832109.exe"
$psi.Arguments = '/s /v"/qn EULAS_AGREED=1 EULAgreed=1 REBOOT=ReallySuppress"'
$psi.Verb = 'runAs' # 弹 UAC 提权
$psi.UseShellExecute = $true
$proc = [System.Diagnostics.Process]::Start($psi)
$proc.WaitForExit()
Test-Path 'C:\Program Files (x86)\VMware\VMware Workstation\vmware.exe' # → True
静默安装全程无窗口,验证方式就是上面最后一行 Test-Path——「没有消息」在静默模式下真的是坏消息,必须主动验证。
#六、第四坑:guestOS 标识大换血
虚拟机开机弹「不支持客户机操作系统"ubuntu64"」。vmx 是旧机器 Workstation 写的,而 17.6.4 精简了客户机系统标识:先试 ubuntu64Guest(ESX 风格的规范名)也被拒。与其翻文档猜,不如直接从安装目录的二进制里把合法 ID 扫出来:
powershell$text = [System.Text.Encoding]::ASCII.GetString(
[System.IO.File]::ReadAllBytes('C:\Program Files (x86)\VMware\VMware Workstation\vmwarebase.dll'))
[regex]::Matches($text, '[a-zA-Z0-9_-]*ubuntu[a-zA-Z0-9_-]*') |
ForEach-Object { $_.Value } | Sort-Object -Unique
# → ubuntu / ubuntu-64 / arm-ubuntu / arm-ubuntu-64 / ubuntuServer ...
结论:新版标识是带连字符的 ubuntu-64,老写法全部废弃。改 vmx 一行,开机直达 Ubuntu 桌面。顺带把 vmx 里指向旧机器 E 盘的 ISO 引用删掉,避免开机弹找不到光盘。
#七、虚拟机搬家与 SSH 免密验证
31.5GB 的虚拟机目录 robocopy 对拷只要 26 秒(1.28 GB/s)。开机时 VMware 问「移动还是复制」——整机搬迁选「我已移动」,保留 UUID 和 MAC;只有当同一台 VM 要复制多份并存时才选「复制」。
旧文档里记录的 IP(192.168.41.130)是旧机器的 NAT 网段,搬到新机器必然失效。不进系统也能拿到新 IP——vmrun 直接问 VMware 要:
powershell$vmrun = 'C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe'
& $vmrun -T ws getGuestIPAddress 'D:\嵌入式开发\VMs\OrangePi-Ubuntu\OrangePi-Ubuntu.vmx'
# → 192.168.254.128
# 免密 SSH 验证(密钥认证,不用输密码)
ssh -o BatchMode=yes zhangshaohua@192.168.254.128 "hostname"
# → zhangshaohua-virtual-machine
密钥认证直接通过——迁移前部署在宿主机 ~/.ssh 的公钥已经写在虚拟机镜像的 authorized_keys 里,整机搬家不影响。NAT 网段每台机器不同,且 DHCP 可能变化,文档里写死的 IP 迟早变坑,vmrun 一查才是真相。
#八、命令速查
text# 编码探测 + 转换(任何中文配置文件通用)
见第四节脚本
# 静默安装 VMware(记日志可加 /l*v "C:\vmmsi.log")
VMware-workstation-full-17.6.4.exe /s /v"/qn EULAS_AGREED=1 EULAgreed=1 REBOOT=ReallySuppress"
# 虚拟机管理
vmrun -T ws start "<vmx>" # 开机(弹 GUI)
vmrun -T ws list # 列出运行中的 VM
vmrun -T ws getGuestIPAddress "<vmx>" # 查 VM IP
# ESP32 编译(中文路径工程必配 build_dir)
$env:HTTP_PROXY='socks5h://127.0.0.1:10808'; $env:HTTPS_PROXY=$env:HTTP_PROXY
pio run
#九、防坑清单
- 工程路径只用 ASCII:源码目录可以中文,但构建输出目录(build_dir)必须纯英文——ld 写 map 文件会翻车。
- 换机先测编码:新机器上任何中文配置文件被编辑工具碰过之后,先验 UTF-8 再运行程序;检测后再转换,别对 UTF-8 文件做二次 GBK 转码。
- 静默安装 = 无反馈安装:一定留日志(
/l*v)+ 装完主动验证文件存在;Start-Process 传参用 ProcessStartInfo 单字符串,别用 ArgumentList 数组。 - 软件版本迁移先查「废弃列表」:vmx、配置文件里的老标识在跨大版本后可能失效;拿不准就从安装目录二进制里扫字符串,那是最权威的清单。
- IP 会变,密钥不变:跨机器迁移后第一时间用 vmrun 重查 IP,SSH 密钥认证随镜像走,不用重配。
- 第三方安装包先验签名:数字签名「有效」= 官方原版未改动;缺失或无效立即删除。
从工具链恢复到 SSH 连通,整场迁移约 4 小时,其中 90% 的时间花在四个坑上。把它们写下来,就是为了让下次搬家只花 40 分钟。
#十、相关阅读
- STM32 实战复盘:一次烧录验证串起的整条工具链——环境迁完之后拿真板子做的端到端验证
- 硬件 CRC 单元:一次实验看懂循环冗余校验
- 定时器与中断:1 秒心跳与五阶段状态机
#十、更新日志
| 日期 | 类型 | 更新内容 |
|---|---|---|
| 2026-09-15 | 新增 | 首次发布:PlatformIO / VMware / 虚拟机整机迁移全记录 |