Background
I replaced my original 3D printer with a smaller Flashforge Adventurer 5M Pro, but the Mosaic Palette 2 wasn’t connecting when plugged into the printer’s USB. dmesg
showed the device was seen but not mounted, indicating the kernel was missing the USB-to-serial driver module.

I spent time trying to compile the modules using the kernel.org sources, but got “Oops” errors when enabling them on the printer. A forum comment pointed to a “Tina Linux” kernel source which supposedly worked for their own use case (compiling the wifi module).

Sadly, the comment was from 2024 and the repo was gone, but I found a copy and cloned it. On to the solution!
Prerequisites
- A Debian/Ubuntu (or compatible) VM with
root
or sudo privileges - Network access to the printer (for
scp
) - Enough storage and CPU to compile kernel modules in a timely manner
- Basic familiarity with
make
,scp
,insmod
, and editing files on the printer - In theory these commands won’t brick the printer, but do these steps at your own risk
Solution
1 – Start with a fresh Debian (or your favorite distro) VM with the basic configuration

2 – Update Packages

3 – Install build tools
apt-get install -y build-essential git wget xz-utils bc bison flex libssl-dev rsync file python3 pkg-config

4 – Setup Linaro 7.1 Toolchain
cd /root
mkdir -p toolchains
cd toolchains
wget https://releases.linaro.org/components/toolchain/binaries/7.4-2019.02/arm-linux-gnueabi/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabi.tar.xz
tar -xf gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabi.tar.xz
export CROSS=/root/toolchains/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabi/bin/arm-linux-gnueabi-

5 – Get the correct kernel source
cd /root
git clone https://github.com/cfelicio/tina-linux-5.4.git linux-5.4.61-tina
cd linux-5.4.61-tina
rm -rf .git

The Tina Linux source matches the kernel used on the printer (5.4.61).
6 – On the printer: get current kernel info and config
uname -a
zcat /proc/config.gz > printer.config

7 – Back on the VM: copy the printer.config file from the printer via SCP
scp -O [email protected]:/root/printer.config /root/printer.config

8 – Copy printer.config
into the kernel tree as .config
cd /root/linux-5.4.61-tina
cp /root/printer.config .config

9 – The actual build process
chmod +x scripts/config
./scripts/config --disable CONFIG_LOCALVERSION_AUTO
./scripts/config --set-str CONFIG_LOCALVERSION ""
make ARCH=arm CROSS_COMPILE="$CROSS" olddefconfig

# Enable drivers as modules
./scripts/config --module CONFIG_USB_SERIAL
./scripts/config --module CONFIG_USB_SERIAL_FTDI_SIO
# Re-resolve dependencies and prep build
make ARCH=arm CROSS_COMPILE="$CROSS" olddefconfig
make ARCH=arm CROSS_COMPILE="$CROSS" modules_prepare
# Build only the subdir to save time
make -j"$(nproc)" ARCH=arm CROSS_COMPILE="$CROSS" M=drivers/usb/serial modules


(If you see warnings, the build & zImage sequence below avoids them), but this is not crucial
make -j"$(nproc)" ARCH=arm CROSS_COMPILE="$CROSS" zImage modules
make -j"$(nproc)" ARCH=arm CROSS_COMPILE="$CROSS" M=drivers/usb/serial modules

10 – Verify the modules were built successfully and match your printer’s kernel (from step 6)
file drivers/usb/serial/usbserial.ko
modinfo -F vermagic drivers/usb/serial/usbserial.ko
modinfo -F vermagic drivers/usb/serial/ftdi_sio.ko

vermagic
must match the running kernel version (e.g., 5.4.61
). If it doesn’t, you will get invalid module format
errors.
11 – Copy the modules over to the printer via SCP
scp -O drivers/usb/serial/ftdi_sio.ko [email protected]:/root/ftdi_sio.ko
scp -O drivers/usb/serial/usbserial.ko [email protected]:/root/usbserial.ko


12 – Load the modules and review dmesg for any errors
insmod usbserial.ko
insmod ftdi_sio.ko

13 – Plug in your Palette 2. It should show up under ttyUSB0, indicating success!

14 – If everything worked well and you want to make the change permanent, move the ko files to your modules, and have them auto start
mv ftdi_sio.ko /lib/modules/5.4.61
mv usbserial.ko /lib/modules/5.4.61
vi /etc/init.d/S50usbserialmod
chmod 755 /etc/init.d/S50usbserialmod
Insert the following into your startup script:
#!/bin/sh
#
# start usb-serial modules for Palette 2 communication
#
insmod /lib/modules/5.4.61/usbserial.ko
insmod /lib/modules/5.4.61/ftdi_sio.ko

15 – Modify your printer.cfg to include palette2
[palette2]
serial: /dev/ttyUSB0
baud: 115200

Klipper Fix
Despite the connection being successful, I was getting an error when issuing the PALETTE_CONNECT command on Klipper. I opened a PR, but will likely take a while to make it downstream:
https://github.com/Klipper3d/klipper/pull/7085
Internal error on command:”PALETTE_CONNECT”
Internal Error on WebRequest: gcode/script
Traceback (most recent call last):
File “/opt/klipper/klippy/webhooks.py”, line 257, in _process_request
func(web_request)
File “/opt/klipper/klippy/webhooks.py”, line 438, in _handle_script
self.gcode.run_script(web_request.get_str(‘script’))
File “/opt/klipper/klippy/gcode.py”, line 216, in run_script
self._process_commands(script.split(‘\n’), need_ack=False)
File “/opt/klipper/klippy/gcode.py”, line 198, in _process_commands
handler(gcmd)
File “/opt/klipper/klippy/gcode.py”, line 135, in
func = lambda params: origfunc(self._get_extended_params(params))
File “/opt/klipper/klippy/extras/palette2.py”, line 173, in cmd_Connect
self._wait_for_heartbeat()
File “/opt/klipper/klippy/extras/palette2.py”, line 225, in _wait_for_heartbeat
currTs – SETUP_TIMEOUT) and startTs > (
TypeError: ‘<‘ not supported between instances of ‘NoneType’ and ‘float’
MCU ‘mcu’ shutdown: Command request
File location (might be present on multiple places on your printer, use find): klipper/klippy/extras/palette2.py
def _wait_for_heartbeat(self):
startTs = self.reactor.monotonic()
currTs = startTs
while True:
currTs = self.reactor.pause(currTs + 1.0)
if (
self.heartbeat is not None
and self.heartbeat >= (currTs - SETUP_TIMEOUT)
):
break
if currTs - startTs > SETUP_TIMEOUT:
self.signal_disconnect = True
raise self.printer.command_error(
"No response from Palette 2"
)
Bonus: Building other modules
This guide is specifically designed for the Palette 2 with the FT230X USB to Serial chip, but you can use the same pattern for any in-tree module that you might need
1- Find the Kconfig symbol and path and grep the kernel tree for your driver (example for the CH341):
grep -R "config USB_SERIAL_CH341" -n .
#Enable the config symbol
./scripts/config --module CONFIG_USB_SERIAL_CH341
make ARCH=arm CROSS_COMPILE="$CROSS" olddefconfig
make ARCH=arm CROSS_COMPILE="$CROSS" modules_prepare
#Build the module
make -j"$(nproc)" ARCH=arm CROSS_COMPILE="$CROSS" M=drivers/usb/serial modules

- For other classes:
- CDC ACM: CONFIG_USB_ACM; path: M=drivers/usb/class
- PL2303: CONFIG_USB_SERIAL_PL2303; path: M=drivers/usb/serial
- CH341: CONFIG_USB_SERIAL_CH341; path: M=drivers/usb/serial
- UVC camera: CONFIG_USB_VIDEO_CLASS; path: M=drivers/media/usb/uvc
- Install to the printer and insmod in dependency order (core first, then subdriver). Examples:
- For serial: usbserial.ko before subdrivers (ftdi_sio.ko, pl2303.ko, ch341.ko)
- For USB storage: usbcore/usb-common dependencies load before storage class modules
Common failure modes and fixes:
- Invalid module format; version magic ‘5.4.61+ …’ should be ‘5.4.61 …’
- Remove .git from kernel source, disable CONFIG_LOCALVERSION_AUTO, set CONFIG_LOCALVERSION=””
- Invalid module format without details
- Verify uname -r is 5.4.61; your .ko vermagic must be 5.4.61 exactly
- Built with wrong ABI/toolchain
- Use the vendor toolchain (Linaro 7.4.1 arm-linux-gnueabi-). Rebuild modules with that cross-compiler.
Conclusion
After all this work, you get to print with multiple colors. Yay! If you want to take the easy road, I have the modules pre-compiled here, but no guarantees it will work for your specific printer:
http://carlosfelic.io/5.4.61modules/modules.zip