Print from USB Drive - Draft

Print from USB Drive

The following guide describes how to facilitate printing G-code files from USB drives that are externally connected to the host computer.

:information_source: Note:

  • This approach requires and/or generally assumes the standard installation of Klipper, as, for example, setup by KIAUH.
  • It also assumes the drive to be formatted as FAT32, or exFAT
  • Any deviation might require adjusting the settings, especially the path variables.

1. Setup USBmount

:warning: WARNING:

  • The described process will alter the mounting behavior for USB drives.
  • On more complex systems or systems with multiple USB drives, this might lead to changed behavior.
  • On regular Single Board Computers (SBC), like a Raspberry Pi, this probably goes largely unnoticed unless one is very familiar with headless Linux systems.

USBmount is a set of shell scripts that cleverly utilizes built-in Linux functions to increase control over the USB mounting process without the need for additional programs or services. As such, it should be quite robust and work on a wide range of different Linux versions.

To install USBmount on Debian-like distributions, log on to the host computer (e.g., via SSH) and follow these simple steps:

wget https://github.com/Sineos/useful_bits/raw/refs/heads/main/Linux/usbmount_0.0.26_all.deb
sudo apt install ./usbmount_0.0.26_all.deb
sudo apt install ntfs-3g exfat-fuse

If one prefers to build it locally, the repository contains the needed instructions to do so.

2. Configure USBmount

To mount the USB drives under the correct user that also runs Klipper and Moonraker, one needs the User-ID and Group-ID of this user. It can be obtained with the following commands:

# User-ID
id -u <your Klipper user here>
# Group-ID
id -g <your Klipper group here (likely the same as the user)>

Note down the result of the above two commands and then execute

sudo nano /etc/usbmount/usbmount.conf

In the usbmount.conf file, change the following items:

MOUNTPOINTS="/home/<your Klipper user here>/printer_data/gcode/external 
             /media/usb0 /media/usb1 /media/usb2 /media/usb3
             /media/usb4 /media/usb5 /media/usb6 /media/usb7"

MOUNTOPTIONS="sync,noexec,nodev"

FS_MOUNTOPTIONS="-fstype=vfat,rw,uid=1000,gid=1000 -fstype=exfat,rw,uid=1000,gid=1000"

The following changes are needed:

  • In MOUNTPOINTS, add the folder to Klipper’s printer_data folder as the future mount point, e.g., /home/<your Klipper user here>/printer_data/gcode/external. It needs to be the first entry, as the mount points are processed in the sequence as listed.
  • In MOUNTOPTIONS, remove noatime,nodiratime
  • In FS_MOUNTOPTIONS, add the settings shown above, replacing the User-ID obtained as uid and the Group-ID as gid (note that both appear twice)

Finally, create the folder as a mount point that is specified in the configuration above:

mkdir ~/printer_data/gcodes/external

3. Ensure Metadata Updates

When the USB drive is mounted, the G-code files will immediately become available in the web interfaces (Mainsail, fluidd, Klipper Screen, etc.), but the metadata and thumbnails will not be updated automatically.
To also ensure this functionality, a small Python script will be called upon a USB mount event that will scan the drive and force an update of the metadata:

sudo wget https://raw.githubusercontent.com/Sineos/useful_bits/refs/heads/main/Linux/90_refresh_meta -O /etc/usbmount/mount.d/90_refresh_meta
sudo chmod u+x /etc/usbmount/mount.d/90_refresh_meta

Edit the script to accommodate for the given setup:

sudo nano /etc/usbmount/mount.d/90_refresh_meta
# The username under which Klipper is running
USERNAME = "<your Klipper user here>"
# Name of the folder below the ROOT_PATH that is used to mount the external drive
EXTERNAL_DRIVE = "external"

4. Test the Setup

The functionality should be executed as soon as a USB drive is connected to the host. The contained G-code files should be shown in the external folder in the web interfaces. To actually show the metadata and thumbnails, a refresh of their file listing is needed.

If it does not work as intended, set VERBOSE="yes" in the usbmount.conf file and replug the drive. The relevant information is logged in /var/log/syslog.

5. Current Limitations

  1. KlipperScreen seems to only pick up the updated metadata when switching the files’ list from icons to details and back again.

6. Clean Eject (optional)

Since the USB is mounted with the sync option, no data corruption should happen when simply pulling the drive. Nevertheless, the correct way is to cleanly umount the drive.

sudo nano /etc/sudoers.d/<your Klipper user here>

Paste the following content into the file and then save it. This will allow calling sudo umount without being asked for a password:

<your Klipper user here> ALL=(ALL) NOPASSWD:/usr/bin/umount

For calling the umount command from within the web interfaces, the gcode_shell_command is needed, which can be installed with KIAUH.

After installing it, add the following macros to the printer.cfg

[gcode_shell_command UMOUNT_EXTERNAL]
command: sudo umount ~/printer_data/gcodes/external
timeout: 2.
verbose: False

[gcode_macro EJECT]
gcode:
  RUN_SHELL_COMMAND CMD=UMOUNT_EXTERNAL
5 Likes

The above is a draft version of a KB article we intend to publish.
Before doing so, we would like to get some feedback if this solution can be widely deployed and be considered as a “general approach”.

Feedback, critics, improvements are very welcome.

2 Likes

I tried it but it doesn’t seem to work.

probably gcodes would be correct

Thanks for trying. It runs pretty smoothly on two of my printers, so lets figure out, what is going wrong on your side.

Please attach /var/log/syslog to your next post

No. It needs to be a separate folder. If mounted correctly the folder will appear in your webinterface and you can open it.

winSCP denies access. is there another way to extract the log?

You need to login as root

klipper@mkspi:~$ mkdir ~/printer_data/gcode/external
mkdir: cannot create directory ‘/home/klipper/printer_data/gcode/external’: No such file or directory
but if I write gcodes, the folder is created and it is in the web interface (Current path:/gcodes/external)

Please attach your klippy.log file.

klippy(1).log (494.3 KB)

Ah, now I see it. There is indeed a bug in the description. The correct command is:

mkdir ~/printer_data/gcodes/external

I have Debian for IOT installed. Probably some package is missing. I will try to install another build and do everything again

Unlikely. Please provide the exact error message or a description of where things go wrong. This stuff pretty much relies on Linux standard behavior.

The printer does not see the file. The web interface has an empty folder. The thing is that I also can’t flash the MCU. I assume that the USB and sdcard port are not working correctly

I would need to see the syslog to provide further guidance. Please try following approach:

sudo cp /var/log/syslog /tmp/
sudo chown klipper /tmp/syslog

Now you should be able to use WinSCP to download it in the /tmp folder

log size 8.25mb. upload limit 8mb)))

Well, then zip it

syslog.zip (695.9 KB)

forgot that there is a zip format)) Sorry for my inattention

2025-02-09T19:55:20.176925+03:00 mkspi kernel: [  526.335201]  sda:
2025-02-09T19:55:20.177037+03:00 mkspi kernel: [  526.335334] sd 0:0:0:0: [sda] Attached SCSI removable disk
2025-02-09T19:55:20.381336+03:00 mkspi systemd[1]: Starting usbmount@dev-sda.service...
2025-02-09T19:55:20.393533+03:00 mkspi usbmount[2113]: loaded usbmount configurations
2025-02-09T19:55:20.403430+03:00 mkspi usbmount[2113]: trying to acquire lock /var/run/usbmount/.mount.lock
2025-02-09T19:55:20.415466+03:00 mkspi usbmount[2113]: acquired lock /var/run/usbmount/.mount.lock
2025-02-09T19:55:22.138690+03:00 mkspi kernel: [  528.299075] sd 0:0:0:0: [sda] tag#0 UNKNOWN(0x2003) Result: hostbyte=0x07 driverbyte=DRIVER_OK cmd_age=1s
2025-02-09T19:55:22.138752+03:00 mkspi kernel: [  528.299100] sd 0:0:0:0: [sda] tag#0 Sense Key : 0x4 [current] 
2025-02-09T19:55:22.138762+03:00 mkspi kernel: [  528.299109] sd 0:0:0:0: [sda] tag#0 ASC=0x0 ASCQ=0x0 
2025-02-09T19:55:22.138770+03:00 mkspi kernel: [  528.299120] sd 0:0:0:0: [sda] tag#0 CDB: opcode=0x28 28 00 01 dc ff 80 00 00 08 00
2025-02-09T19:55:22.138776+03:00 mkspi kernel: [  528.299129] I/O error, dev sda, sector 31260544 op 0x0:(READ) flags 0x80700 phys_seg 1 prio class 0
2025-02-09T19:55:22.253706+03:00 mkspi usbmount[2113]: /dev/sda contains filesystem type vfat
2025-02-09T19:55:22.268952+03:00 mkspi usbmount[2113]: mountpoint /home/klipper/printer_data/gcodes/external is available for /dev/sda
2025-02-09T19:55:22.284840+03:00 mkspi usbmount[2113]: executing command: mount -tvfat -osync,noexec,nodev,rw,uid=1001,gid=1001 /dev/sda /home/klipper/printer_data/gcodes/external
2025-02-09T19:55:22.294661+03:00 mkspi kernel: [  528.457319] FAT-fs (sda): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.
2025-02-09T19:55:22.324368+03:00 mkspi usbmount[2113]: executing command: run-parts /etc/usbmount/mount.d
2025-02-09T19:55:22.819504+03:00 mkspi python[1511]: [extensions.py:__init__()] - Unix Socket Opened - Client ID: 281472733710736, Process ID: 2163, User ID: 0,  Group ID: 0
2025-02-09T19:55:23.459683+03:00 mkspi python[1511]: [shell_command.py:pipe_data_received()] - INFO:metadata:mmu_server: Running MMU enhanced version of metadata
2025-02-09T19:55:23.466648+03:00 mkspi python[1511]: [shell_command.py:pipe_data_received()] - INFO:metadata:Object Processing is disabled
2025-02-09T19:55:23.791647+03:00 mkspi python[1511]: [shell_command.py:pipe_data_received()] - INFO:metadata:mmu_server: Pre-processing file: /home/klipper/printer_data/gcodes/external/spinning+top3_PETG_48m43s.gcode
2025-02-09T19:55:25.142614+03:00 mkspi python[1511]: [shell_command.py:pipe_data_received()] - INFO:metadata:Reading placeholders took 1.35s. Detected gcode by slicer: OrcaSlicer
2025-02-09T19:55:25.143081+03:00 mkspi python[1511]: [shell_command.py:pipe_data_received()] - INFO:metadata:No MMU metadata placeholders found in file: /home/klipper/printer_data/gcodes/external/spinning+top3_PETG_48m43s.gcode
2025-02-09T19:55:25.285844+03:00 mkspi python[1511]: [shell_command.py:_check_proc_success()] - Command (/home/klipper/moonraker-env/bin/python /home/klipper/moonraker/moonraker/components/mmu_server.py -m -n -p /home/klipper/printer_data/gcodes -f "external/spinning+top3_PETG_48m43s.gcode") successfully finished
2025-02-09T19:55:25.290438+03:00 mkspi python[1511]: [extensions.py:_on_close()] - Unix Socket Closed: ID: 281472733710736, Close Code: None, Close Reason: Read Exit
2025-02-09T19:55:26.022712+03:00 mkspi kernel: [  532.184374] sd 0:0:0:0: [sda] tag#0 UNKNOWN(0x2003) Result: hostbyte=0x07 driverbyte=DRIVER_OK cmd_age=0s
2025-02-09T19:55:26.022772+03:00 mkspi kernel: [  532.184400] sd 0:0:0:0: [sda] tag#0 CDB: opcode=0x28 28 00 00 00 80 10 00 00 01 00

According to the log, it seems we have a half working situation.

USB drive detected:

2025-02-09T19:55:20.177037+03:00 mkspi kernel: [  526.335334] sd 0:0:0:0: [sda] Attached SCSI removable disk

Mount process runs with the correct parameters and with the correct folder. At the end it complains about a corrupted filesystem on the drive (hard to tell, could just be an unclean removal):

2025-02-09T19:55:22.268952+03:00 mkspi usbmount[2113]: mountpoint /home/klipper/printer_data/gcodes/external is available for /dev/sda
2025-02-09T19:55:22.284840+03:00 mkspi usbmount[2113]: executing command: mount -tvfat -osync,noexec,nodev,rw,uid=1001,gid=1001 /dev/sda /home/klipper/printer_data/gcodes/external
2025-02-09T19:55:22.294661+03:00 mkspi kernel: [  528.457319] FAT-fs (sda): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.

The process to extract the metadata from the gcode file triggers as well for the file spinning+top3_PETG_48m43s.gcode:

2025-02-09T19:55:25.285844+03:00 mkspi python[1511]: [shell_command.py:_check_proc_success()] - Command (/home/klipper/moonraker-env/bin/python /home/klipper/moonraker/moonraker/components/mmu_server.py -m -n -p /home/klipper/printer_data/gcodes -f "external/spinning+top3_PETG_48m43s.gcode") successfully finished

Then things get bad, maybe due to faulty hardware:

2025-02-09T19:55:26.022712+03:00 mkspi kernel: [  532.184374] sd 0:0:0:0: [sda] tag#0 UNKNOWN(0x2003) Result: hostbyte=0x07 driverbyte=DRIVER_OK cmd_age=0s
2025-02-09T19:55:26.022772+03:00 mkspi kernel: [  532.184400] sd 0:0:0:0: [sda] tag#0 CDB: opcode=0x28 28 00 00 00 80 10 00 00 01 00
2025-02-09T19:55:26.022780+03:00 mkspi kernel: [  532.184409] I/O error, dev sda, sector 32784 op 0x0:(READ) flags 0x80700 phys_seg 1 prio class 0
2025-02-09T19:55:26.022785+03:00 mkspi kernel: [  532.184648] usb 4-1.2: USB disconnect, device number 5
2025-02-09T19:55:26.038750+03:00 mkspi kernel: [  532.199404] device offline error, dev sda, sector 32784 op 0x0:(READ) flags 0x80000 phys_seg 16 prio class 0
2025-02-09T19:55:26.038806+03:00 mkspi kernel: [  532.199462] FAT-fs (sda): Directory bread(block 32784) failed
2025-02-09T19:55:26.038813+03:00 mkspi kernel: [  532.199516] device offline error, dev sda, sector 32785 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0
2025-02-09T19:55:26.038818+03:00 mkspi kernel: [  532.199534] FAT-fs (sda): Directory bread(block 32785) failed
2025-02-09T19:55:26.038823+03:00 mkspi kernel: [  532.199551] device offline error, dev sda, sector 32786 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0
2025-02-09T19:55:26.038829+03:00 mkspi kernel: [  532.199565] FAT-fs (sda): Directory bread(block 32786) failed
2025-02-09T19:55:26.038835+03:00 mkspi kernel: [  532.199581] device offline error, dev sda, sector 32787 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0
2025-02-09T19:55:26.038840+03:00 mkspi kernel: [  532.199593] FAT-fs (sda): Directory bread(block 32787) failed
2025-02-09T19:55:26.038846+03:00 mkspi kernel: [  532.199609] device offline error, dev sda, sector 32788 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0
2025-02-09T19:55:26.038854+03:00 mkspi kernel: [  532.199621] FAT-fs (sda): Directory bread(block 32788) failed
2025-02-09T19:55:26.038859+03:00 mkspi kernel: [  532.199636] device offline error, dev sda, sector 32789 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0
2025-02-09T19:55:26.038864+03:00 mkspi kernel: [  532.199649] FAT-fs (sda): Directory bread(block 32789) failed
2025-02-09T19:55:26.038870+03:00 mkspi kernel: [  532.199664] device offline error, dev sda, sector 32790 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0
2025-02-09T19:55:26.038875+03:00 mkspi kernel: [  532.199677] FAT-fs (sda): Directory bread(block 32790) failed
2025-02-09T19:55:26.038880+03:00 mkspi kernel: [  532.199692] device offline error, dev sda, sector 32791 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0
2025-02-09T19:55:26.038885+03:00 mkspi kernel: [  532.199704] FAT-fs (sda): Directory bread(block 32791) failed
2025-02-09T19:55:26.038890+03:00 mkspi kernel: [  532.199722] FAT-fs (sda): Directory bread(block 32792) failed
2025-02-09T19:55:26.038896+03:00 mkspi kernel: [  532.199740] FAT-fs (sda): Directory bread(block 32793) failed
2025-02-09T19:55:26.038901+03:00 mkspi kernel: [  532.199803] FAT-fs (sda): error, corrupted directory (invalid entries)
2025-02-09T19:55:26.038906+03:00 mkspi kernel: [  532.199811] FAT-fs (sda): Filesystem has been set read-only
2025-02-09T19:55:26.038911+03:00 mkspi kernel: [  532.199857] Buffer I/O error on dev sda, logical block 2270, lost sync page write
2025-02-09T19:55:26.038916+03:00 mkspi kernel: [  532.202987] Buffer I/O error on dev sda, logical block 1, lost async page write
2025-02-09T19:55:26.038922+03:00 mkspi kernel: [  532.203061] Buffer I/O error on dev sda, logical block 32768, lost async page write
2025-02-09T19:55:26.038926+03:00 mkspi kernel: [  532.203094] Buffer I/O error on dev sda, logical block 38992, lost async page write
2025-02-09T19:55:26.107475+03:00 mkspi systemd[1]: usbmount@dev-sda.service: Main process exited, code=killed, status=15/TERM

Can you try a different USB pen drive?

another drive, another port USB = empty