跟着思兼学习Klipper(26): 大卸八块 Klipper 远程控制实验汇总

又名《给创想三维 K1 找个"强力外援"》

前言

原创文章,转载引用请务必注明链接,水平有限,如有疏漏,欢迎交流指正。

文章如有更新请访问 DFRobot 社区cnblogs 博客园,前者内容较全,后者排版及阅读体验更佳。

我们约定:主板 指 MCU 部分,上位机 指运行 Klippy 的 MPU Linux 部分。

玩了好长时间魔兽世界,我又回来了!赶在年前,之前写的一篇论文接收了,申请的专利也通过了,忙里偷闲来更新。

本文的缘起是 HahnZ 找我讨论了一下创想的 K1 系列 3D 打印机,我发现它的控制系统(软件 + 硬件)很独特,设想一个思路,通过外置第二个性能更强的上位机,与 K1 控制板通过有线网络连接,接管 K1上的 Klipper 全家桶部分功能来提升使用体验。现将部分方案汇总如下。

image-20240128200758104

[!CAUTION]

由于我并没有 K1 系列打印机,以下所有内容依赖我查到的资料以及个人经验,由于 K1 系列控制系统的特殊性,可能和实际情况有出入。如有机会和创想的工程师进行交流确认,后续会再进行补充验证,虽然大概率没门。

[!IMPORTANT]

本文介绍 Klipper 固件的组成结构,并探讨逐一拆开各部分,改成远程通讯。我会以常见的 Debian-Based Linux 发行版作为示例,并对 K1 可能遇到的问题进行提示,提出可能的解决方法。

当然最后可能大家看完之后的感想和我一样,没啥实际意义,再加上其他一些原因,我其实原本并不想整理成稿,后来想着虽然意义不大,但是挺有趣的,就记录下来吧。

为了方便叙述,我们将 K1 控制系统称为服务端 Server,外援上位机称为客户端 Client。

一、K1 控制系统介绍

1.1 K1 控制系统软硬件规格

主板如下图所示,正式版接口更全,比如这里网络接口就没焊接。

【图1. K1 主板】来源: Creality WiKi

我们看看它的硬件规格:

  1. SoC 采用君正 Ingenic X2000E,基于 MIPS32 架构,有两颗 32-bit MIPS Xburst 2 核心,以及一颗 MIPS Xburst 0 实时核心 (协处理器),主频最高 1.5 Ghz
  2. SoC 内置 256MB LPDDR2 内存
  3. 内置 VPU,没有GPU,支持 H.264 和 JPEG 编解码加速 | 所以 LVGL
  4. 板载 AP6212 无线模块,2.4Ghz 单频,单天线传输最高 72.2Mbps。搭载蓝牙 5.2
  5. 以太网接口(千兆百兆未知)
  6. 显示接口采用 RGB 而非 HDMI 等
  7. 具有四线摄像头接口,猜测为 USB 协议
  8. 板载没有标准 USB Type-A 接口
  9. MPU、RAM、MCU 部分没有散热片,代表整体发热不高,当然也可能提示性能不够强
  10. 打印机控制部分采用 GD32F303 主控,主频 120Mhz

[!NOTE]

小结一下:

  • CPU: 没有采用常见的 ARM 或 x86_64 架构,大家听说 MIPS 多是龙芯处理器或者路由器,之前富源盛的 MiX 主板和创想自己的 Creality WiFi Box 也采用此架构模块开发。缺点就是性能不够强,缺少部分指令集如 NEON 等。另外软件、编译工具链等也是问题。
  • RAM: 采用 256MB DDR2 内存,高于之前 Fly3D 全志 T113 内置的 128MB 内存,之前使用 vmstat 做过分析,当内存不足频繁使用大量 swap 交换空间时,会成为明显的卡顿。不过 K1 的软件架构也与众不同,所以体验反馈还好。
  • 内置 VPU,无 GPU:各种因素导致没有采用 KlipperScreen + Xorg 方案,毕竟这俩玩意即使不接屏幕开着也能占用 ~200MB 内存。另外不知道板载的摄像头有无享受用上 VPU 的硬件加速。
  • 存储空间:我记得不大,8G 么?不过支持创想云,相当于拓展了空间。
  • WiFi:低速单频无线模块,网络体验实际如何?

【图2. Ingenic X2000 系列处理器结构框图】来源:cnx-software.com

软件系统(推测):

  1. 使用 BusyBox
  2. 系统采用类似于 OpenWRT 系统的 opkg 进行包管理
  3. 整体操作让我想起全志 R818 的 Tina Linux
  4. 默认没有安装 Moonraker 及网页界面
  5. 具有彩色触摸屏界面,上面说过 KlipperScreen 非常占用资源,所以这里是采用 LVGL 之类的工具创建的界面,屏幕元素直接和 Klippy 通讯,所以也就不需要 Moonraker。类似的项目还有 Guppy Screen for Klipper
  6. 其他关于主板和操作系统的介绍与分析:github.com

1.2 K1 系统短板与我的思路

最高 1.5GHz 双核 MIPS 处理器,256M DDR2 内存,8G 存储(?),低速单频无线模块,非主流 Klipper 操作系统,令我很好奇这么选择的原因是什么。

虽然原装主板掣肘颇多,但是我们 又不得不用它,原因包括二合一主板,屏幕直接和主板通讯,想要进行替换各方面成本比较高,所以我想,可否请个强力外援帮它一起干活?所谓的 强力外援就是一台性能更强的上位机设备。这也是这篇文章我觉得有趣的起源。

image-20240127012527340

【图3. Klipper 全家桶结构图】注意新版 Klipper 有所更新,可以查看 klippy.py 的帮助文件得知文件名称有所变化。

pi@raspberry:~ $ klippy-env/bin/python /home/pi/klipper/klippy/klippy.py --help
Usage: klippy.py [options] <config file>

Options:
  -h, --help            show this help message and exit
  -i DEBUGINPUT, --debuginput=DEBUGINPUT
                        read commands from file instead of from tty port
  -I INPUTTTY, --input-tty=INPUTTTY
                        input tty name (default is /tmp/printer)
  -a APISERVER, --api-server=APISERVER
                        api server unix domain socket filename
  -l LOGFILE, --logfile=LOGFILE
                        write log to file instead of stderr
  -v                    enable debug messages
  -o DEBUGOUTPUT, --debugoutput=DEBUGOUTPUT
                        write output to file instead of to serial port
  -d DICTIONARY, --dictionary=DICTIONARY
                        file to read for mcu protocol dictionary
  --import-test         perform an import module test
  
pi@raspberry:~ $ cat /home/pi/printer_data/systemd/klipper.env
KLIPPER_ARGS="/home/pi/klipper/klippy/klippy.py /home/pi/printer_data/config/printer.cfg -I /home/pi/printer_data/comms/klippy.serial -l /home/pi/printer_data/logs/klippy.log -a /home/pi/printer_data/comms/klippy.sock"

所以也就是:

旧名称 新名称 类型及备注
/tmp/printer /home/pi/printer_data/comms/klippy.serial 虚拟串口,用于传统马林模式的访问,如 OctoPrint 和 Printrun 等
/tmp/klippy_uds /home/pi/printer_data/comms/klippy.sock Klipper API 服务器的 Unix Socket,用于进程间通讯,Moonraker会访问它

这里我们为了减轻上位机负担,将一些功能模块通过网络远程交予强力外援负责,两者通过网线连接。故而我们会探讨依次将图中 K1 控制系统内各部分断开,通过远程连接由外援上位机对应部分负责。外援上位机优点如下:

  1. 可以使用熟悉的软件环境,各种工具、命令等
  2. 方便拓展其他软件功能
  3. 有更多 USB 接口可以连接拓展模块
  4. 硬件配置更高,运行速度更快
  5. 其它待补充

二、使用远程 Klippy 与下位机主板通讯

这其实又分为两种:上位机软件 Klippy 与主板通过 USB 物理通讯;主板串口通过 ESP 模块实现无线透传。先讲1。

[!IMPORTANT]

原理与方法:Klippy 通过 USB 与下位机主板通讯,使用 USB-Over-IP 将 USB 设备通过网络共享

实现功能: 外援机上的 Klippy 可以直接与主板通讯,不再使用 K1 上的软件,USB 摄像头等设备也可以通通转发。触摸屏幕需要转发请求到外援机的 Klippy Socket 才能正常使用。不过既然外援机了,KlipperScreen 可以搞起来。

此处通过网络转发 USB 设备,实际上很少用到这种方式,因为相当于两个上位机了。但是 K1 的自带上位机不是需要帮忙嘛,而且作为二合一主板不方便升级更换上位机部分。

参考文档:USB/IP | ArchWiki (推荐看中文版)

USB-IP 是一个将通用的USB设备通过IP网络进行共享的系统。为了在计算机之间共享具有全部功能的USB设备,USB-IP将“USB I/O消息”封装到 TCP/IP 中,并在计算机之间传输,以实现远程共享或访问USB设备。

K1 和外援机的连接方式可以选择有线以太网,也可以选择无线网,推荐前者,速度快、干扰小、更稳定。

2.1 K1 Server 服务端设置

我这里没有,使用其他设备代替,配置如下:

  • 使用 x86_64 架构的 Intel Z3735F 处理器,运行 RaspberryPi OS bullseye 操作系统(基于 Debian)。

  • MKS Monster8 v1 主板

sudo su

# 安装 usbip 软件包
apt install usbip

# 服务端提供 USB 设备映射,请确保已连接上物理 USB 设备,并且加载 usbip_host USB-IP 内核模块
modprobe usbip-host

usbip list --local # 显示本地USB设备
usbip bind --busid=1-1.2.4.1 # 假设主板 busid 为1-1.2.4.1,进行绑定

# usbipd -4 -d # 打开 DEBUG 模式进行测试,只使用 IPv4
usbipd -4 -D # 上述确认无误,打开后台运行模式

## 其他命令
# 在服务端上取消绑定USB设备
usbip unbind --busid=busid

# 为避免更换USB接口后busid改变,可以绑定设备id
/usr/sbin/usbip list -p -l | grep '#usbid=1d50:614e#' | cut '-d#' -f1
usbip bind --$(/usr/sbin/usbip list -p -l | grep '#usbid=%i#' | cut '-d#' -f1)

2.2 外援机 Client 客户端设置

演示用外援机配置如下:使用 ARM64 架构的 RK3399 处理器,运行 Ubuntu 22.04.3 操作系统(基于 Debian)。

sudo su
# 安装 usbip 包,注意和 RPi OS 不同
apt install linux-tools-virtual hwdata
update-alternatives --install /usr/local/bin/usbip usbip `ls /usr/lib/linux-tools/*/usbip | tail -n1` 20

# 客户端加载远程 USB 设备,需要加载 vhci-hcd 内核模块
modprobe vhci-hcd

usbip list --remote=192.168.0.101 # 获取服务端共享的 USB 设备列表
usbip attach --remote=192.168.0.101 -b 1-1.2.4.1 # 挂载服务端对应的 USB 设备

# 在客户端上查看已映射的 USB 端口:
usbip port

## 其他命令
# 在客户端上断开某个已连接的 USB 端口(端口号由上述命令获得,如01):
usbip detach --port=端口号

# 确认本地挂载的 USB 设备(打印机主板、存储设备、摄像头等)

image-20240127031952919

【图4. 远程连接 K1 打印机主板】通过有线网络将外援机与 K1 上位机部分连接,实现在外援机上远程访问 K1的主板部分。

2.3 注意

  • usbip 默认启用,如果 K1 中没有,可以手动编译内核。参考一参考二

  • usbipd 的具体使用手册:https://man.archlinux.org/man/usbipd.8

  • 只有在客户端上断开设备 (detach) 后才能取消绑定 (unbind)

  • 关闭服务端原来的 klipper 服务 sudo systemctl stop klipper,防止占用主板 USB 接口

  • 系统在休眠后需要重新进行绑定

  • 如果需要自行编译 usbip 软件工具,可以去官网查看源码,不过官方这样介绍的,所以作为针对网络优化的 OpenWrt 应该有内置该工具。

    For Linux, the source code of usbip was merged into the staging tree, and finally has been moved to the mainline since Linux-3.17. Development is ongoing in the kernel community, not here. Linux distributions will provide binary packages of usbip. Just for historical records, the project page keeps old download files of the Linux version. Do not use them.

  • 点击 重启Firmware 后,主板会重启,此时需要重新绑定 (服务端)、挂载 (客户端) USB 设备

image-20240127024946174

【图5. 远程重启打印机主板】此时需要重新绑定重新挂载。

2.4 进一步补充

需要补充的细节如下:

2.4.1 转发挂载多个设备

我们可以把 USB 摄像头,打印头工具板等也转发过去,其规则如下:

  1. 服务端开启后台服务,这个一直运行不用管:sudo usbip -4 -D

  2. 查询设备可以使用 usbip list --localusbip port

  3. 想要添加新的 USB 设备需要:

    a. 设备在服务端进行绑定 (bind)

    b. 设备在客户端进行挂载 (attach)

  4. 如果断开重连也要重复 绑定 + 挂载 的步骤

2.4.2 开机自动加载内核驱动及绑定、挂载

  • 可以将上述内核驱动以写入 /etc/modules 的方式实现开机自动加载。

  • 服务端可以使用 SysVinit 来管理 usbipd 自动启动

  • 客户端可以使用 systemd 来管理自动挂载,顺便实现自动重连?

2.4.3 断连后自动重绑定、挂载

服务端的话在我之前的文章 使能Klipper从U盘读取Gcode并打印 介绍过,使用 udev 实现热插拔、以及自动挂载,这里不再赘述。

客户端的话,原始重启主板使用 Firmware Restart 命令实现,由于此时 Klipper 断开,无法运行宏,我们可以写一个 moonraker 组件,调用 shell_cmd 实现 发送主板重启指令 + 循环等待服务端设备可用 + 重新挂载 USB 设备 + 循环查询 klipper 状态并必要时重启 klipper 服务来连接主板

2.4.4 K1 自带触摸屏幕的使用

我们可以选择更自由、拓展性更强的 KlipperScreen 屏幕接在外援上位机上。如果想要使用原来的 LVGL 触摸屏,一个思路是让自带屏幕与远程外援机的 Klipper API Server Socket 连接,具体方法可以查看第二段,进行远程转发。问题在于 K1 的轻量级系统可能采用 DropBear 等而非 OpenSSH,虽然前者资源占有更小,但是转发性能有待观察。

2.4.5 指定有线网络连接

由于 K1 自带 WiFi,外援上位机为了控制方便,也会额外采用 WiFi 连接。为了更好的稳定性和性能,K1 和 外援机使用有线连接转发 USB 设备。WiFi 和 Ethernet 同时存在,需要避免两者使用 WiFi 通讯。

  • 方法一:简单粗暴,断开 K1 的无线网连接。
  • 方法二:设置网络连接优先级。使用 ip 工具设置默认网关优先级,具体未测试,可以查看 此文章
  • 方法三:指定程序从哪个网卡通讯。上面设置 remote IP 地址时,起始就指定了通讯链路。当然也可以进一步设置 iptables 根据来源和目的地 IP 分流。

2.4.6 K1 和 外援机网线连接自动配置

想要服务端和客户端通讯,需要知道各自的地址。常见的也有三种方法:

  1. 手动配置服务端和客户端的 IP 地址
  2. 在客户端安装 DHCP 服务器(如 isc dhcp serverdnsmasq 等)
  3. 使用 mdns 自动配置,如 Linux 常用的 avahi-deamon,这个不知道 K1 系统上安装方便不

2.5 可用性测试

连接是连上了,但是要可用性满足需求才行。有群友使用过类似方案: [ BeagleBoneBlack (server) + RaspberryPI (client) ],说是木有问题。我们测试下:

2.5.1 传输带宽测试

首先我们转发一个 U 盘,对比本地和远程读写速度:

## 挂载 U 盘并创建一个 200M 的测试文件,内容随机
sudo mount /dev/sda1 /mnt
sudo dd if=/dev/urandom of=test.file bs=20MB count=10

# 【服务端】 测试读取速度
rsync --progress /mnt/test.file ~

# 【服务端】 对比 md5 值,确保传输无误
md5sum /mnt/test.file
md5sum ~/test.file

# 【服务端】 使用 usbtop 命令查看 USB 带宽占用情况
sudo modprobe usbmon
sudo apt install usbtop
sudo usbtop

# 取消挂载 U 盘,使用客户端挂载 U 盘设备,并同样挂载到客户端 /mnt【略】

##【客户端】 测试读取速度,计算 md5 值,必要时使用 sync 命令【略】

结果如下:

rsync --progress /mnt/BB7B/test.file .
pi@kbox  ~  rsync --progress /mnt/BB7B/test.file .
test.file
    200,000,000 100%    4.01MB/s    0:00:47 (xfr#1, to-chk=0/1)
4MB/s
rsync --progress /media/pi/TRANSFER/test.file .

pi@raspberry:~ $ rsync --progress /media/pi/TRANSFER/test.file .
test.file
    200,000,000 100%   12.67MB/s    0:00:15 (xfr#1, to-chk=0/1)

image-20240127030239206

模式 传输速度
单频无线传输 ~ 886 KB/s
百兆有线传输 ~ 4 MB/s
本机直接读取 ~ 12 MB/s

结果可能的原因:

  • 受限于双机无线模块规格较低且干扰较强,无线传输结果不理想。
  • 千兆有线网络结果会更好,不过百兆网络带宽理论上也是足够的。
  • 直接读取和网络转发传输还是存在性能损失,具体瓶颈在哪有待分析。

[!NOTE]

  1. 千兆网络接口需要搭配超五类以上的网线
  2. 如何分辨千兆网口,以及查看是否千兆网口工作在百兆模式(驱动不兼容、对方为百兆)可以使用 cat /sys/class/net/{ethXXX}/speed 或者使用 ethtool 之类的工具。
  3. 可以使用 ip 命令查看网络传输丢包率,也可以用 iperf 工具核实传输带宽、网络波动等情况

参考资料:

2.5.2 实际打印测试

和之前一样,我们创建了一个接近真实打印机的虚拟打印机配置文件,捏造一台 “皇帝的打印机” (只需要主板连接上位机不需要组装其他部件)。 为了进行压力测试,虚拟机主要修改点:

  1. 允许最大速度和加速度
  2. 设置打印成型尺寸,尺寸过小还没加速完成就要减速
  3. XY 改为 AWD
  4. 电机驱动采用普通 STEP/DIR 模式控制
  5. 更改驱动细分
  6. 挤出头热敏允许最低值接近绝对零度
  7. 取消热床
  8. 添加一个 SET0 的宏,设置当前坐标
  9. 其他和通用测试文件相同

对于切片软件的更改主要包括:

  1. 起始 Gcode,调用 SET0 设置当前坐标
  2. 速度/加速度/驱动细分/模型复杂度逐渐提高
  3. 模型打印温度为 0

这样就可以上传 Gcode 进行打印了,目前 测试没有问题

也可以使用之前文章介绍的,使用 force_move 跑圆形/方形/十字形/直线等。

三、 使用远程 Moonraker 与 K1 Klippy 通讯

[!IMPORTANT]

原理与方法:Moonraker 与 Klippy 在本地通过 Unix Socket 进行通信。使用 openSSH + socat 将远程 Unix Socket 转发到本地

实现功能: 外援上位机正常安装除 Klipper 之外的各种组件,控制 K1 上的 klipper,触摸屏正常使用。相比第 1 个不常见的使用场景,这个更通用一些。而且 Klippy 和主板之间的通讯量很大且对通讯质量要求高,Moonraker 和 Klippy 之间的通讯量较小。

socat 是我们的老朋友了,无论是手机Klipper还是在Klipper上使用Marlin屏幕,都离不开它。这次借助 socatopenSSH 在客户端开启 Unix Socket 转发,外援机的 moonraker 访问本地的 rklippy.sock 相当于访问 K1 上的 klippy.sock。为了提高转发性能开启 fork 和 reuseaddr 参数。具体命令如下

# 【/home/pi/rklippy.sock】为本地自定义 socket 名称
# 用户名和 K1 的 IP 地址根据实际情况修改,这里为示例【pi】、【192.168.0.114】
socat "UNIX-LISTEN:/home/pi/rklippy.sock,reuseaddr,fork" \
EXEC:'ssh pi@192.168.0.114 socat STDIO UNIX-CONNECT\:/home/pi/printer_data/comms/klippy.sock'

socat 是一个命令行程序,这就意味着他不能在同一个终端下建立多个进程,这对于高并发场景来说意味着多条TCP抢socat,延迟也会因此有着不可忍受的波动,所以我们使用 reuseaddr 参数绑定一个本地端口,同时使用 fork 参数将socat转变为多链接模式,即当一个链接建立后,自动复制一个同样的端口监听,这样就能有效提高高并发场景下的性能表现。

使用 tmux 或者 写一个 systemd 服务 自动运行即可。但实际还有几个问题需要解决:

3.1 开启 ssh 免密登录

否则转发时会频繁要求你输入 K1 的访问密码,导致通讯故障。最简单的方法就是使用密钥登录,方法如下:

# 创建本地密钥,可以不设置密码
ssh-keygen -t rsa
# 将公钥上传到 K1,注意替换【username】和【remote-server】
ssh-copy-id username@remote-server
# 使用 ssh 登录一次即可实现之后免输入密码自动登录
ssh username@remote-server

更多信息可以移步查看 SSH无密码登录:只需两个简单步骤

3.2 修改 moonraker 配置文件

如下图所示修改两处,否则无法正常使用。

image-20240128035011739

[!CAUTION]

  1. moonraker 是本地运行,读取本地的 moonraker.conf 配置文件
  2. klipper 是在 K1 上运行,本地浏览器中显示的 printer.cfg 等文件不生效!安装组件、修改配置等需要在 K1 上执行。
  3. 读取的系统信息到底是本地还是 K1 的,容读者自己思考,哈哈

image-20240128035924720

三、使用远程 KlipperScreen 和 WebUI 与 K1 Moonraker 通讯

此方案需要在 K1 安装mrk,然后在外援机安装 Fluidd 网页控制界面 + Nginx 网页服务器,以及 KlipperScreen 等。只需要在外援机配置文件中添加 K1 的 moonraker 访问地址即可,不再赘述。

四、番外 | Klippy 与主板真·无线连接

此时不需要打印机主板与上位机物理连接。

方案一 socat 中转

我最初的方案是使用 ESP8266 等模块进行串口无线透传,方案要点:

  1. ESP8266 与打印机主板 UART 接口连接,实现 UART-to-TCP
  2. 上位机通过 socat 将 TCP 转成一个虚拟串口
  3. Klippy 访问本地虚拟串口连接网络

image-20240128202835986

已有无线 UART 透传项目:esp-link

已有示例:wireless-klipper

上述项目测试发现:ESP32 AP 模式最稳定。

方案二 添加 Klipper 支持

这种方案则更进一步,通过修改 Klipper 源码,使能支持通过 TCP 和主板通讯,相关示例如下:

原理都懂,方法可参考上述项目。

posted @ 2024-01-28 21:01  思兼  阅读(1464)  评论(0编辑  收藏  举报