[AI Native][OpenWrt] 给犀牛 R66S 编译 OhMyWrt:从一句话到 TF 卡直接启动

这是一次完整的 AI Native 固件构建:我只提出目标,AI 通过 SSH 调查设备支持、配置并编译 OhMyWrt、校验产物、设计无 HDMI 验收流程;我唯一做的事情,是用 balenaEtcher 把镜像写进 TF 卡,然后插卡、接网线、上电。

Tags: AI Native OpenWrt OhMyWrt FastRhino R66S ARM64 Homelab


目录


这次到底是谁做了什么

这次不是“AI 给几条命令,我自己照着敲”的传统辅助模式。

我给出的原始目标基本只有一句:

连接到构建机,给犀牛 R66S 编译 OhMyWrt,目标是 ARM 架构。

后面的工作全部由 AI 在实际环境中完成:

graph TD
    A["我提出目标:给 R66S 编译 OhMyWrt"] --> B["AI SSH 登录构建机"]
    B --> C["调查源码、设备树和网络定义"]
    C --> D["确认 rockchip/armv8 与 R66S profile"]
    D --> E["生成配置并处理软件包依赖"]
    E --> F["执行交叉编译"]
    F --> G["校验 SHA256、fwtool 元数据和 gzip 数据"]
    G --> H["给出 TF 卡刷写与无 HDMI 验收方案"]
    H --> I["我用 balenaEtcher 写卡、插卡、上电"]
    I --> J["R66S 正常启动"]

我实际做的事情只有:准备一张 TF 卡,打开 balenaEtcher,选择 AI 编译好的 SquashFS 镜像,写卡,然后把卡插进 R66S,接上网线和电源。

没有手动进入 menuconfig,没有自己排查依赖,没有手工处理编译失败,也没有接显示器或串口。写完卡,插进去,就起来了。

这才是我理解的 AI Native:AI 不是在旁边解释步骤,而是直接进入真实环境,把任务从目标推进到可以使用的产物;人只保留必须接触物理设备的最后一步。


为什么 R66S 需要单独编译

犀牛 R66S 和常见的 x86 软路由不是同一种目标:

项目 x86 软路由 犀牛 R66S
CPU 架构 x86_64 aarch64
OpenWrt target x86/64 rockchip/armv8
软件包架构 x86_64 aarch64_generic
启动介质 SATA、NVMe 或虚拟磁盘 TF 卡
本地显示 通常有 HDMI、DP 或 VGA 没有 HDMI 输出
首次验收 屏幕或虚拟控制台 网口、SSH,必要时 UART

构建目标必须精确到:

Target:     rockchip
Subtarget:  armv8
Device:     lunzn_fastrhino-r66s
Packages:   aarch64_generic

完整路径是:

x86_64 Ubuntu 构建机
    ↓
AArch64 交叉编译工具链
    ↓
rockchip/armv8
    ↓
lunzn,fastrhino-r66s
    ↓
SquashFS / Ext4 TF 卡镜像

确认目标:ARM64 不等于随便一个 ARM 固件

AI 首先在 OhMyWrt 源码中确认了 R66S 的设备定义:

CONFIG_TARGET_rockchip=y
CONFIG_TARGET_rockchip_armv8=y
CONFIG_TARGET_rockchip_armv8_DEVICE_lunzn_fastrhino-r66s=y

展开后的目标信息:

CONFIG_TARGET_BOARD="rockchip"
CONFIG_TARGET_SUBTARGET="armv8"
CONFIG_TARGET_PROFILE="DEVICE_lunzn_fastrhino-r66s"
CONFIG_TARGET_ARCH_PACKAGES="aarch64_generic"

源码还明确给出了 R66S 的网络定义:

ucidef_set_interfaces_lan_wan 'eth0' 'eth1'

eth0 = LANeth1 = WAN。源码能确认逻辑接口,但不能仅靠这行配置判断机身左口或右口哪个是 LAN;第一次上电时,如果一个物理口没有获得地址,最直接的办法就是换另一个口。


构建环境

项目 配置
操作系统 Ubuntu 24.04.4 LTS
主机架构 x86_64
CPU 20 vCPU
内存 15 GiB
构建方式 普通用户,非 root
OhMyWrt 提交 7dc7471e025142475c96d994e291a6c411d604bf
固件内核 Linux 6.12.63

OpenWrt 构建阶段应该使用普通用户。只有安装系统依赖时需要 sudo,不应该用 root 运行 make。构建目录也应避免空格、非 ASCII 字符、Windows 混入的异常 PATH,以及被 root 创建并遗留的文件。


准备 OhMyWrt

git clone --filter=blob:none \
  git@github.com:ohmywrt/ohmywrt-next.git

cd ohmywrt-next
git checkout 7dc7471e025142475c96d994e291a6c411d604bf
./scripts/feeds update -a
./scripts/feeds install -a

构建过程使用隔离工作目录,不污染已有的 x86 构建环境。R66S 是 aarch64_generic,x86 是 x86_64,工具链、内核模块和软件包索引都不同,不应该在同一份未清理的输出目录中来回切换目标。


配置 R66S 与软件包

设备选择对应:

Target System  → Rockchip
Subtarget      → ARMv8 boards
Target Profile → Lunzn FastRhino R66S

核心配置:

CONFIG_TARGET_rockchip=y
CONFIG_TARGET_rockchip_armv8=y
CONFIG_TARGET_rockchip_armv8_DEVICE_lunzn_fastrhino-r66s=y
CONFIG_TARGET_ROOTFS_EXT4FS=y
CONFIG_TARGET_ROOTFS_SQUASHFS=y
CONFIG_TARGET_IMAGES_GZIP=y
CONFIG_TARGET_KERNEL_PARTSIZE=16
CONFIG_TARGET_ROOTFS_PARTSIZE=160

LuCI、网络服务与驱动:

CONFIG_LUCI_LANG_zh_Hans=y
CONFIG_PACKAGE_luci=y
CONFIG_PACKAGE_luci-i18n-base-zh-cn=y
CONFIG_PACKAGE_kmod-r8125=y
CONFIG_PACKAGE_adguardhome=y
CONFIG_PACKAGE_ddns-go=y
CONFIG_PACKAGE_luci-app-ddns-go=y
CONFIG_PACKAGE_luci-app-ksmbd=y
CONFIG_PACKAGE_luci-app-upnp=y
CONFIG_PACKAGE_luci-app-wol=y
CONFIG_PACKAGE_luci-app-nlbwmon=y
CONFIG_PACKAGE_kmod-wireguard=y
CONFIG_PACKAGE_wireguard-tools=y
CONFIG_PACKAGE_luci-proto-wireguard=y

还包含 curlbind-digethtool-fullhtopiftopiperf3pciutilsetherwake 等现场排障工具。最关键的网卡驱动是 kmod-r8125

本次沿用了项目现有的 iptables legacy 栈:

CONFIG_PACKAGE_firewall=y
# CONFIG_PACKAGE_firewall4 is not set
CONFIG_PACKAGE_xtables-legacy=y
CONFIG_PACKAGE_iptables-zz-legacy=y
CONFIG_PACKAGE_dnsmasq_full_ipset=y
# CONFIG_PACKAGE_dnsmasq_full_nftset is not set

这组配置需要整体保持一致,不要在保留 legacy iptables、FullCone、GeoIP、TProxy 和 miniupnpd-iptables 的同时,随手打开 firewall4 与 nftset。

make defconfig
./scripts/diffconfig.sh > config-r66s.diffconfig

开始编译

make download -j"$(nproc)"
find dl -type f -size -1024c -print
make -j"$(nproc)"

如果并行编译失败,降低并发并输出详细日志:

make -j1 V=s

本次构建成功,产物位于:

bin/targets/rockchip/armv8/

验证构建产物

生成了两种 R66S 镜像:

ohmywrt-rockchip-armv8-lunzn_fastrhino-r66s-ext4-sysupgrade.img.gz
ohmywrt-rockchip-armv8-lunzn_fastrhino-r66s-squashfs-sysupgrade.img.gz
镜像 大小 SHA256
Ext4 约 46 MiB ff9606fc088956b8e01006552896611b29b85b581b532a4d540af46127c4ec48
SquashFS 约 38 MiB 302cfd5b160cd835bdb4971e81597d25c11eee522eab457c5f66cc572c66e803

项目生成的 checksum 全部通过:

cd bin/targets/rockchip/armv8
sha256sum -c sha256sums

直接运行 gzip -t 可能看到 decompression OK, trailing garbage ignored。这不代表固件损坏:OpenWrt 会在 gzip 数据后追加 fwtool 元数据。正确验证方式是:

staging_dir/host/bin/fwtool \
  -i /dev/null \
  -T bin/targets/rockchip/armv8/ohmywrt-rockchip-armv8-lunzn_fastrhino-r66s-squashfs-sysupgrade.img.gz \
  | gzip -t

镜像内可以确认:

ARM64 OpenWrt Linux-6.12.63

用 balenaEtcher 写入 TF 卡

本次实际使用的是 balenaEtcher,不是命令行 dd

选择的镜像是:

ohmywrt-rockchip-armv8-lunzn_fastrhino-r66s-squashfs-sysupgrade.img.gz

SquashFS 使用只读 /rom 加可写 overlay,并支持 failsafe 和恢复出厂。R66S 没有 HDMI,配置出错以后本地救援手段较少,因此这套恢复语义比离线修改方便的 Ext4 更重要。

实际操作只有:

  1. 把 TF 卡插入电脑。
  2. 打开 balenaEtcher。
  3. 选择 SquashFS 的 .img.gz 文件。
  4. 选择正确的 TF 卡。
  5. 点击 Flash。
  6. 等待写入和验证完成。
  7. 安全弹出 TF 卡。

Etcher 可以直接处理压缩镜像,不需要手动解压。唯一需要反复确认的是目标磁盘:选错设备不是“刷写失败”,而是把另一块磁盘完整覆盖。


没有 HDMI,怎么知道它真的启动了

写卡完成后,R66S 断电插卡,电脑用网线直连其中一个网口,暂时不接上级路由器,然后上电等待首次启动。

默认管理地址:

http://192.168.1.1

默认用户名是 root,密码为空:

ping 192.168.1.1
curl -I http://192.168.1.1/
ssh root@192.168.1.1

如果电脑没有自动获得地址,就换另一个物理网口,或把电脑设置为 192.168.1.2/24 后重试。

本次实际体验比预想简单:TF 卡用 Etcher 写完,插入 R66S,上电以后网络就起来了。没有接 HDMI,因为它本来就没有 HDMI;也没有接 UART,因为不需要。

第一次登录后立即设置密码:

passwd

登录后的完整验收

型号与内核:

ubus call system board
cat /proc/device-tree/model
echo
uname -a

期望看到 Lunzn FastRhino R66Saarch64 和 Linux 6.12.63

网口与驱动:

ip -br link
lsmod | grep r8125
dmesg | grep -Ei 'r8125|eth'

TF 卡与 overlay:

dmesg | grep -Ei 'mmc|sdhci|error|fail'
mount | grep -E ' /rom | /overlay | / '
df -h

SquashFS 固件应看到只读 /rom 与可写 /overlay。持续出现 MMC I/O error、timeout 或 CRC error,通常意味着 TF 卡、读卡器或供电存在问题。

WAN 与 DNS:

ifstatus wan
ping -c 3 1.1.1.1
nslookup openwrt.org

最后验证重启持久化:

date > /etc/r66s-boot-test
sync
reboot

重新连接后文件仍存在,就说明 TF 卡 overlay 可以正常写入并跨重启保存:

cat /etc/r66s-boot-test
rm /etc/r66s-boot-test

如果网络没有起来,排障顺序应是:确认 Etcher 验证成功、等待首次启动、检查两个网口 Link、交换网口、设置静态 IP、尝试 SquashFS failsafe,最后才使用 UART。

UART 参数为 1500000 8N1,必须使用 3.3V TTL,只连接 GND、TX 和 RX,不连接 VCC。


最终结果

Target:        rockchip/armv8
Device:        lunzn,fastrhino-r66s
Architecture:  aarch64_generic
Kernel:        Linux 6.12.63
Storage:       TF card
LAN/WAN:       eth0 / eth1
Driver:        kmod-r8125

整个过程的角色分工非常明确:

环节 执行者
SSH 登录构建机 AI
调查 OhMyWrt 与 R66S 支持 AI
确认 ARM64 target/profile AI
生成和检查配置 AI
执行完整编译 AI
校验 checksum 与镜像元数据 AI
设计无 HDMI 启动验收方案 AI
用 balenaEtcher 写入 TF 卡
插卡、接网线、上电
系统启动 一次成功

以前这种事情通常意味着:找教程、猜 profile、进 menuconfig、处理 feeds、盯编译日志、区分设备树、确认网卡驱动,然后再担心写卡以后没有显示器该怎么办。

这次变成了:

我:给犀牛 R66S 编译 OhMyWrt。

AI:完成,镜像和校验结果在这里,推荐写 SquashFS。

我:打开 Etcher,写入 TF 卡,插卡,上电。

R66S:启动。

AI Native 的价值不只是少敲几条命令。真正的变化是:人不再需要亲自承担每一个中间步骤,只需要给出目标、保留关键决策,并完成 AI 无法替代的物理操作。

对于这次 R66S,我实际接触到的复杂度只剩下:

选择镜像
→ 写入 TF 卡
→ 插入设备
→ 上电

剩下的目标识别、交叉编译、依赖处理、固件校验和无显示验收设计,全部由 AI 在真实环境中完成。

这才是这次构建最值得记录的部分。