1. 为什么你需要一个嵌入式设备的“远程桌面”

做嵌入式开发的朋友,尤其是做工业设备、物联网终端或者消费电子产品的,肯定都经历过这种场景:设备卖出去,部署到天南海北的客户现场,运行得好好的,突然有一天客户一个电话过来,说设备“抽风”了,日志不打印了,功能异常了,或者干脆就“死”给你看。这时候你怎么办?让客户拍个照?照片能看出啥?让客户接上串口线抓个日志?别说客户会不会,就算会,设备可能装在几十米高的塔吊上,或者密封在机柜深处,物理接触都难。

以前我处理这种问题,要么是让现场工程师带着电脑和调试器去,成本高、响应慢;要么就是尝试在设备里预埋一些简陋的远程脚本,通过公网IP或者内网穿透来访问,但稳定性差、安全性也让人提心吊胆。直到我遇到了 Rtty/Rttys 这套组合拳,才算真正找到了一个既开源免费、又稳定可靠的嵌入式远程调试“银弹”。

简单来说,Rtty 是跑在你嵌入式设备(比如ARM板、MIPS路由器)上的一个轻量级客户端,而 Rttys 是部署在你可控服务器(比如云服务器、公司内网服务器)上的服务端。设备上的Rtty会通过加密的WebSocket连接,主动“报到”到你的Rttys服务器。之后,你只需要打开一个浏览器,访问Rttys提供的Web界面,就能像在设备现场接上了串口一样,获得一个完整的Shell终端。所有的操作,都像是在本地进行,但实际网络链路可能跨越了千山万水。

这套方案最吸引我的几个点:第一是开源,代码全透明,不用担心后门,也方便定制;第二是协议友好,基于HTTP/WebSocket,能穿透绝大多数防火墙和代理,部署极其简单;第三是安全,支持Token认证、SSL加密,甚至可以做设备白名单,不用担心被“黑”;第四是轻量,Rtty客户端编译后只有几百KB,对资源紧张的嵌入式环境非常友好。接下来,我就手把手带你,从一台干净的云服务器开始,到最终在ARM开发板上获得一个可用的远程Shell,把整个搭建过程、我踩过的坑以及优化技巧,毫无保留地分享给你。

2. 第一步:搭建你的指挥中心——Rttys服务器

服务器端是我们的“指挥中心”,所有设备都会连接到这里,我们也通过这里管理所有设备。选择一台你有控制权的Linux服务器,Ubuntu、CentOS、Debian都可以,我习惯用Ubuntu 20.04 LTS。建议使用有公网IP的云服务器,这样你的设备无论在哪里都能连接上。

2.1 获取与运行Rttys

官方提供了编译好的二进制文件,对于快速部署来说,这是最省事的方式,避免了在服务器上折腾编译环境。

首先,我们通过wget命令直接下载最新版本的Rttys。你可以去GitHub的Release页面看看,找到对应你服务器架构(通常是linux-amd64)的包。这里以v3.4.1版本为例:

# 创建一个专用目录并进入
mkdir ~/rttys && cd ~/rttys
# 下载Linux 64位版本的Rttys压缩包
wget https://github.com/zhaojh329/rttys/releases/download/v3.4.1/rttys-linux-amd64.zip
# 解压
unzip rttys-linux-amd64.zip
# 赋予可执行权限
chmod +x rttys

解压后,你会得到一个名为rttys的可执行文件。先别急着运行,我们需要先配置它。

2.2 关键配置详解:安全与访问控制

直接运行./rttys会使用默认配置,但为了安全和符合我们的需求,必须创建并修改配置文件。创建一个名为rttys.conf的文件:

nano rttys.conf

下面是我根据实际项目调整后的一个配置示例,我会逐行解释关键部分:

# 设备连接端口。你的嵌入式设备上的Rtty客户端将通过这个端口连接到服务器。
addr-dev: :5912

# 用户Web访问端口。你通过浏览器管理设备时,访问的就是这个端口。
addr-user: :5913

# Token认证密钥。这是最重要的安全设置之一,相当于服务器和设备之间的共享密码。
# 你可以用命令生成一个:openssl rand -hex 16
token: your_strong_token_here_replace_me

# 设备白名单。这里设置为"*",意味着任何知道token的设备都可以连接。
# 如果你需要更严格的控制,可以改为具体的设备ID,如 "device_001 device_002"
white-list: "*"

# 数据库配置。默认使用SQLite,数据会保存在当前目录的rttys.db文件中。
# 对于轻量级使用,SQLite完全足够。如果设备量巨大,可以考虑MySQL。
db: sqlite://rttys.db

# 是否禁用新用户注册。如果只是你自己或小团队使用,建议设为true,手动在数据库添加用户。
disable-sign-up: true

# 本地访问是否免认证。设为false,意味着即使从服务器本机访问Web界面,也需要登录。
local-auth: false

这里有几个我踩过的坑要特别提醒你:

  1. Token务必修改:示例中的your_strong_token_here_replace_me一定要换成你自己用openssl命令生成的复杂字符串,这是防止未授权设备接入的第一道防线。
  2. 端口选择:5912和5913是默认端口,确保你的服务器防火墙(如ufw或安全组规则)已经放行了这两个端口。命令类似sudo ufw allow 5912/tcp和sudo ufw allow 5913/tcp。
  3. 禁用注册:对于生产环境,强烈建议将disable-sign-up设为true。初始的管理员用户需要通过命令行工具来创建,后面会讲到。这样可以避免公开的注册页面被滥用。

2.3 启动服务与创建管理员

配置好后,就可以在后台启动Rttys服务了。使用nohup或systemd来守护进程,这里先用简单的nohup方式:

nohup ./rttys run --conf rttys.conf > rttys.log 2>&1 &

现在,打开你的浏览器,访问 http://你的服务器IP:5913。如果看到登录界面,说明服务端已经成功跑起来了。但是因为我们禁用了注册,现在还没有账号。

我们需要回到服务器终端,使用Rttys自带的命令行工具创建第一个管理员用户:

# 添加一个用户,角色设为admin
./rttys user add -u admin -p your_admin_password -r admin

执行成功后,你就可以用admin和刚才设置的密码在Web界面登录了。登录后,你会看到一个简洁的仪表盘。虽然现在还没有任何设备连接,但你的“指挥中心”已经就绪。这个Web界面就是未来你管理所有远程设备的控制台,可以在这里看到在线的设备列表,点击任何一个就能直接打开它的Shell终端,非常直观。

3. 第二步:让设备“开口说话”——交叉编译Rtty客户端

服务器搭好了,接下来就是重头戏:让我们的嵌入式设备能够运行Rtty客户端。99%的嵌入式设备都是ARM或其他非x86架构,这意味着我们必须进行交叉编译。这个过程是新手最容易“卡壳”的地方,我会把依赖处理和静态编译这两个核心难题讲透。

3.1 准备交叉编译工具链

首先,你得有对应你目标板子的交叉编译工具链。比如你的板子是ARM Cortex-A53内核,那么工具链名字可能类似arm-linux-gnueabihf-gcc。请从你的芯片供应商或像Linaro这样的社区获取正确的工具链,并确保其bin目录已经加入系统的PATH环境变量。你可以通过arm-linux-gnueabihf-gcc -v来验证工具链是否可用。

3.2 获取Rtty源码与处理依赖“三巨头”

Rtty的编译依赖三个主要的库:libev(高性能事件循环)、libssl(OpenSSL,用于加密通信)和 libcrypto(也是OpenSSL的一部分)。官方仓库的README可能一笔带过,但这里恰恰是坑最多的地方。

# 克隆Rtty源码
git clone https://github.com/zhaojh329/rtty.git
cd rtty

如果你直接按照常规的cmake流程,指定交叉编译工具链进行编译,很大概率会失败,报错找不到libev.so、libssl.so等。这是因为你的交叉编译工具链目录里,可能没有预编译好这些库的动态链接库(.so文件)和头文件(.h文件)。

我的解决方案是:手动为交叉编译环境编译并安装这些依赖库。这不是最优雅的方法,但是最直接、最可控的。

  1. 编译安装 libev:

    wget http://dist.schmorp.de/libev/libev-4.33.tar.gz
    tar -zxvf libev-4.33.tar.gz
    cd libev-4.33
    ./configure --host=arm-linux-gnueabihf --prefix=/path/to/your/toolchain/sysroot
    make && make install
    

    这里的--host指定了目标平台,--prefix指定了安装路径,这个路径应该指向你的交叉编译工具链的sysroot目录(通常工具链目录下会有arm-linux-gnueabihf或sysroot这样的子目录)。安装后,对应的.so文件和头文件就会被放置到工具链的库路径和头文件路径下。

  2. 编译安装 OpenSSL: 这一步更关键,也更容易出错。你需要为你的目标ARM架构配置并编译OpenSSL。

    wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz
    tar -zxvf openssl-1.1.1w.tar.gz
    cd openssl-1.1.1w
    ./Configure linux-generic32 --prefix=/path/to/your/toolchain/sysroot --cross-compile-prefix=arm-linux-gnueabihf-
    make && make install
    

    注意这里的linux-generic32是针对32位ARM的配置,如果你的设备是64位的(如Cortex-A53),可能需要使用linux-aarch64。--cross-compile-prefix参数告诉配置脚本使用我们的交叉编译工具前缀。

完成这两步后,你的交叉编译工具链的lib和include目录下应该就有了对应的文件。这时候再回去编译Rtty,它就能找到这些依赖了。

3.3 一劳永逸的“绝招”:静态编译

即便解决了动态库依赖,把编译好的rtty可执行文件放到设备上运行时,还可能因为设备系统里缺少对应版本的libev.so或libssl.so而报错。为了彻底摆脱对设备系统库的依赖,最稳妥的办法是静态编译,把所有的依赖库都打包进最终的可执行文件里。

这需要在编译Rtty时,修改它的CMake配置。我通常的做法是创建一个toolchain.cmake文件,在里面指定交叉编译器和静态链接选项:

# toolchain.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

# 设置你的交叉编译工具链绝对路径
set(TOOLCHAIN_DIR /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf)
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/bin/arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/bin/arm-linux-gnueabihf-g++)

# 设置find命令的根路径,让CMake在工具链目录里找库
set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_DIR}/arm-linux-gnueabihf)

# 最关键的两行:强制静态链接
set(CMAKE_EXE_LINKER_FLAGS "-static")
set(BUILD_SHARED_LIBS OFF)

# 显式链接OpenSSL库(有时CMake找不到静态库,需要手动指定)
# 假设你已经将静态库(.a)文件安装到了工具链的lib目录
target_link_libraries(rtty ${TOOLCHAIN_DIR}/arm-linux-gnueabihf/lib/libssl.a ${TOOLCHAIN_DIR}/arm-linux-gnueabihf/lib/libcrypto.a ${TOOLCHAIN_DIR}/arm-linux-gnueabihf/lib/libev.a)

然后,在Rtty源码目录下,使用这个工具链文件进行编译:

mkdir build && cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake
make -j$(nproc)

编译成功后,在build目录下会生成一个名为rtty的可执行文件。用file命令查看一下,如果显示statically linked,那就大功告成了。这个文件通常有几MB大小(因为包含了所有库),但它是完全自包含的,可以扔到任何同架构的Linux设备上直接运行。

4. 第三步:连接与调试——从设备到云端Shell

现在,我们有了静态编译的rtty客户端,把它拷贝到你的嵌入式设备上(通过U盘、scp、或者直接打包进文件系统镜像)。是时候建立从设备到服务器的桥梁了。

4.1 在设备上运行Rtty客户端

在设备的终端里,运行Rtty的命令格式如下:

./rtty -I 'MyDevice_001' -h 'your.server.ip' -p 5912 -s -a -v -d 'Office_Router'

我来拆解一下这几个参数,它们决定了客户端的行为:

  • -I 'MyDevice_001':这是设备的唯一标识符(ID)。给设备起个有意义的名字,比如Factory_Line3_Sensor,方便在Web界面识别。这是必填项。
  • -h 'your.server.ip':你的Rttys服务器的公网IP地址或域名。
  • -p 5912:连接端口,对应服务器配置里的addr-dev。
  • -s:启用SSL/TLS加密。强烈建议在生产环境加上,除非你只是在测试内网环境。
  • -a:自动重连。如果网络波动断开,客户端会不断尝试重新连接,这对稳定性至关重要。
  • -v:输出详细日志,方便初期调试查看连接状态。
  • -d 'Office_Router':设备的描述信息,会在Web界面显示。

第一次运行,如果一切正常,你会看到客户端尝试连接服务器,并最终显示连接成功的消息。如果失败,请检查:服务器IP和端口是否正确、服务器防火墙是否放行、设备上的rtty文件是否有可执行权限、以及服务器配置中的token是否与命令匹配(Rtty客户端默认会从/etc/rttys/token文件或RTTY_TOKEN环境变量读取token,如果没设置,需要在命令中用-t参数指定)。

4.2 在Web界面进行远程操作

回到你的电脑,打开浏览器,登录Rttys的Web界面(http://your.server.ip:5913)。在设备列表里,你应该能看到刚刚上线的设备MyDevice_001,状态是在线的。

点击设备ID,一个基于浏览器的终端窗口就会弹出来。恭喜你!你现在获得的这个Shell,和你通过串口线或者SSH直接登录到设备上获得的Shell,功能是完全一样的。你可以运行ls、ps、top,可以查看日志tail -f /var/log/messages,可以重启服务systemctl restart xxx,甚至可以进行简单的文件编辑。

这感觉非常奇妙,就像你瞬间“穿越”到了千里之外的设备旁边。我最早用这个功能,是调试一个部署在偏远地区气象站的设备,当时设备上报数据异常,通过这个远程Shell,我很快发现是一个后台进程僵死了,一条kill命令重启服务就解决了问题,省去了至少两天的出差时间和高昂的成本。

4.3 进阶技巧:让远程调试更强大

基本的Shell访问已经解决了80%的远程调试需求。但如果你想做得更专业,这里还有几个我实践过的进阶技巧:

  1. 开机自启动:不能让设备每次重启后都需要人工登录去执行rtty命令。将运行rtty的命令写入设备的/etc/rc.local文件,或者创建一个systemd服务单元文件,是最佳实践。这样设备一上电,就会自动连接到你的调试服务器。

    # 示例:创建一个简单的systemd服务 /etc/systemd/system/rtty.service
    [Unit]
    Description=Rtty Remote Terminal
    After=network.target
    
    [Service]
    Type=simple
    ExecStart=/usr/local/bin/rtty -I MyDevice_001 -h your.server.ip -p 5912 -s -a
    Restart=always
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target
    

    然后使用systemctl enable rtty启用它。

  2. 串口转发:很多嵌入式调试的“灵魂”在于串口打印的内核信息。你可以写一个简单的后台脚本,使用socat或ser2net这类工具,将设备的物理串口(如/dev/ttyS0)映射到一个本地TCP端口。然后,在Rtty提供的远程Shell里,用telnet或nc连接这个本地TCP端口,就能实时看到串口输出,实现了“远程串口”的功能。这对于分析内核启动异常、驱动问题至关重要。

  3. 文件传输:虽然Rtty本身不直接提供文件传输界面,但在其提供的Shell里,你可以轻松使用scp、wget甚至curl来上传下载文件。或者,你也可以在服务器端写一个简单的HTTP文件服务器,方便设备端拉取更新包。

  4. 多设备管理与分组:当你有成百上千台设备时,在Web界面里找一台设备会变得困难。Rttys支持给设备打标签(Tag),你可以在运行Rtty客户端时通过-T参数指定标签,比如-T "branch:shanghai, type:gateway"。然后在Web界面就可以按标签筛选和分组管理,效率大大提升。

走到这一步,一个功能完整、稳定可靠的嵌入式设备远程调试系统就已经构建完成了。从服务器配置、交叉编译的坑里爬出来,到最终在浏览器里流畅地操作远程设备,这个过程虽然有些技术细节需要攻克,但所有的付出都是值得的。它不仅仅是一个调试工具,更是一种运维能力的质变,让你能从容应对遍布各地的设备,实现真正的“运筹帷幄之中,决胜千里之外”。这套基于Rtty/Rttys的方案,已经在我负责的多个物联网项目中稳定运行了数年,成为了故障排查和日常维护不可或缺的利器。

更多推荐