gif gif

The interesting corner

gif gif

Using an Android tablet to run Klipper on my Ender 3 V3 SE

Introduction

I recently got this Lenovo Tab 4 10 To use as the brains for my Ender 3 V3 SE 3d printer. I decided to use a tablet instead of a raspberry pi because I got this tablet for the same price as a 2GB pi 4b+, and it's specs are basically double the specs of the pi. Even the extra overhead of running android is kinda negated by it having double the ram (and me wanting to put lineageOS or something like that on it).

All the files that I used for this are available here:

Rooting that mf

The first thing to do is of course to root it, which I did using this link. After connecting the tablet to my PC and running adb devices -l the tablet showed up nicely:

  List of devices attached
  360606f6               device product:LenovoTB-X704L model:Lenovo_TB_X704L device:X704L transport_id:1
  

After entering the bootloader with adb reboot bootloader I was presented with the bootloader:

bootloader

After that, I booted to twrp by issuing fastboot boot twrp-3.2.3-0-tb_x704l.img (some time later I rebooted back into recovery and used fastboot flash recovery twrp-3.2.3-0-tb_x704l.img to permanently flash TWRP). I got into TWRP, kept the system read-only and backed up the boot and system partitions. After that, I copied the SuperSU zip file to the tablet and installed it using TWRP. After that, I rebooted the device, installed the root checker apk and lo and behold: I had root access!

root access

Installing LineageOS

After successfully getting root access, I wanted to install LineageOS. I used This reddit post (first comment by u/Junods) to get the instructions (skipping the unlocking and installing TWRP parts as I had already done that). After rebooting I successfully installed lineageOS!

lineageOS

I first thought about installing postmarketOS, as that seems to be a bit more what I'm looking for: running linux on this tablet so I can use it as a replacement for a raspberry pi, but according to the postmarketOS device page for the Tab 4, the screen is not working yet.

Rooting that mf (part 2)

After installing LineageOS, my device was no longer rooted. Since it now runs android 11, I can't use SuperSU anymore, so I used this blogpost to gain root access. Below is an overview of the commands I used:

            
            Z:\webserver\public\img\hardware\lenovotab4\files>ls
            Magisk-v27.0.apk                             minimal_adb_fastboot_v1.4.3_setup.exe
            SuperSU-v2.82-201705271822.zip               root-checker-6-5-3.apk
            lineage-18.1-20231009-UNOFFICIAL-TBX704.zip  supersu-2-82.apk
            magisk-tb8054-remount-v17.1.zip              twrp-3.2.3-0-tb_x704l.img
            
            Z:\webserver\public\img\hardware\lenovotab4\files>adb install Magisk-v27.0.apk
            Success
            
            Z:\webserver\public\img\hardware\lenovotab4\files>adb root
            
            Z:\webserver\public\img\hardware\lenovotab4\files>adb shell dd if=/dev/block/by-name/boot of=/sdcard/boot.img
            131072+0 records in
            131072+0 records out
            67108864 bytes (64 M) copied, 1.511906 s, 42 M/s
            
            Z:\webserver\public\img\hardware\lenovotab4\files>adb shell ls /sdcard/Download/*.img
            /sdcard/Download/magisk_patched-27000_rf9iZ.img
            
            // now, go into the magisk app, click on install, select the boot.img file and patch it

            Z:\webserver\public\img\hardware\lenovotab4\files>adb pull /sdcard/Download/magisk_patched-27000_rf9iZ.img
            /sdcard/Download/magisk_patched-27000_rf9iZ.img: 1 file pulled. 1.6 MB/s (67108864 bytes in 40.665s)
            
            Z:\webserver\public\img\hardware\lenovotab4\files>dir
             Volume in drive Z is interesting-corner-share
             Volume Serial Number is 9258-B060
            
             Directory of Z:\webserver\public\img\hardware\lenovotab4\files
            
            02-09-2024  22:43    <DIR>          .
            02-09-2024  22:22    <DIR>          ..
            02-09-2024  21:51       665.088.830 lineage-18.1-20231009-UNOFFICIAL-TBX704.zip
            02-09-2024  20:25            11.305 magisk-tb8054-remount-v17.1.zip
            02-09-2024  21:05        12.498.796 Magisk-v27.0.apk
            02-09-2024  22:44        67.108.864 magisk_patched-27000_rf9iZ.img
            02-09-2024  19:22         1.053.464 minimal_adb_fastboot_v1.4.3_setup.exe
            02-09-2024  21:15        10.334.757 root-checker-6-5-3.apk
            02-09-2024  21:11         6.201.401 supersu-2-82.apk
            02-09-2024  21:29         5.903.921 SuperSU-v2.82-201705271822.zip
            02-09-2024  19:20        31.404.032 twrp-3.2.3-0-tb_x704l.img
                           9 File(s)    799.605.370 bytes
                           2 Dir(s)  402.824.945.664 bytes free
            
            Z:\webserver\public\img\hardware\lenovotab4\files>adb reboot bootloader
            
            Z:\webserver\public\img\hardware\lenovotab4\files>fastboot devices
            360606f6        fastboot
            
            Z:\webserver\public\img\hardware\lenovotab4\files>fastboot flash boot magisk_patched-27000_rf9iZ.img
            target reported max download size of 536870912 bytes
            sending 'boot' (65536 KB)...
            OKAY [ 23.337s]
            writing 'boot'...
            OKAY [  0.304s]
            finished. total time: 23.641s
            
            Z:\webserver\public\img\hardware\lenovotab4\files>fastboot reboot            
            

Now I was successfully rooted:

Z:\webserver\public\img\hardware\lenovotab4\files>adb shell
TBX704:/ $ su
TBX704:/ # id
uid=0(root) gid=0(root) groups=0(root) context=u:r:magisk:s0
                    
Magisk installed Root checker showing root access

Installing Klipper, Moonraker, Mainsail and KlipperScreen

I used this github (using mainsail) to install everything. First off, getting linux deploy working was kind of a pain in the ass. I eventually installed busybox as a Magisk module and used SELinuxModeChanger to change the SELinux mode to permissive. I also needed to set the PATH variable in linux deploy to /data/user/0/ru.meefik.linuxdeploy/files:/system/xbin and change the architecture to arm64. After that though, the container installed and started without the error that it failed mounting and I could SSH into the container:

ssh android@100.100.100.106
Warning: Permanently added '100.100.100.106' (ED25519) to the list of known hosts.
android@100.100.100.106's password:
Linux localhost 3.18.140-lineageos-gf21bb40e154a #1 SMP PREEMPT Mon Oct 9 16:45:05 CEST 2023 aarch64
Debian GNU/Linux 10 (buster) [running via Linux Deploy]
$ chsh -s /bin/bash
Password:
android@localhost:~$

Now it was time to install all the programs. cloned the kiuah repo and started the script:

              
sudo apt install git
git clone https://github.com/th33xitus/kiauh.git
kiauh/kiauh.sh
              
            

Installing Klipper, Moonraker and Mainsail went without a hitch. However, installing KlipperScreen was a PAIN in the a s s. First, the problem was the python3 version. The latest version for debian buster (10) is 3.7, but the script required 3.8 or above. That wasn't that much of a problem, I could just install it from source. I opted to use python3.11 instead of 3.9 to be nice and up to date

              
sudo apt update
sudo apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev libreadline-dev libffi-dev curl libbz2-dev
wget https://www.python.org/ftp/python/3.11.8/Python-3.11.8.tar.xz
tar -xvf Python-3.11.8.tar.xz
cd Python-3.11.8
./configure --enable-optimizations # optimizes the python binary by running multiple tests, makes the building slower though
make -j$(nproc) # use max amount of cores to compile (in my case this was 8)
sudo make altinstall # do not install as main version
python3.11 --version # check if it's installed properly
              
            

After building and alt-installing python3.11, I changed it to the default with this:

sudo update-alternatives --config python3
There are 2 choices for the alternative python3 (providing /usr/bin/python3).

  Selection    Path                       Priority   Status
------------------------------------------------------------
* 0            /usr/local/bin/python3.11   1         auto mode
  1            /usr/bin/python3.7          1         manual mode
  2            /usr/local/bin/python3.11   1         manual mode

Press <enter> to keep the current choice[*], or type selection number: 

Note about changing the default python3 binary

After I set python3.11 as my default choice, I got an error about a module not being found when I tried to run add-apt-repository. Changing it back to python3.7 (default for debian buster) fixed this. Python3.11 had no effect on any other packages.

After changing the Python binary, I got 2 other errors:

Dependency gobject-introspection-1.0 found: NO. Found 1.58.3 but need: ‘>= 1.64.0’

DEPRECATION: 
            CMake support for versions <3.17 is deprecated since Meson 0.62.0. 
            | 
            | However, Meson was only able to find CMake 3.13.4.
        

The CMake error was easy to fix. The latest debian buster version of CMake is 3.13.4, so I downloaded the source and installed it:

      
wget https://github.com/Kitware/CMake/releases/download/v3.30.3/cmake-3.30.3.tar.gz
tar -xvf cmake-3.30.3.tar.gz
cd cmake-3.30.3
./bootstrap
make -j$(nproc)
sudo make install
      
    

The other error about gobject-introspection-1.0 however, I spent a few hours on trying to fix. The first thing I tried, was to install the libgirepository1.0-dev package. But, as the message said, it was already installed (1.58.3) but it needed a version of or above 1.64.0. I thought "well, that's easy, I could just install it from source again", so I downloaded the source (first 1.81, then 1.70, 1.66 and 1.64 (T_T) ) and started to build. However, I got the error doctemplates: file not found.

I got this on every version I tried, and almost gave up. I asked CoPilot, and he gave me a pretty good suggestion: temporarily add the debian bullseye PPA and install the pacakge from there. I added the line deb http://deb.debian.org/debian bullseye main to my /etc/apt/sources.list file and updated using sudo apt-get update Then I could install the packages:

      
sudo apt-get install -t bullseye libgirepository1.0-dev
sudo apt-get install -t bullseye gobject-introspection
        

To prevent the whole system from updating, I added this to /etc/apt/preferences.d/pin-gobject-introspection:

Package: gobject-introspection
Pin: release a=bullseye
Pin-Priority: 990

And this to /etc/apt/preferences.d/pin-libgirepository:

Package: libgirepository1.0-dev
Pin: release a=bullseye
Pin-Priority: 990

Now, I could finally install KlipperScreen, and it installed without any problems []~( ̄▽ ̄)~*

Now that everything was installed, I wasn't there yet. There were still a few problems:

Getting mainsail and moonraker connected

Getting mainsail and moonraker connected to eachother took a bit more work. The first thing I had to do was change the socket in the config file of both moonraker and klipper. The KLIPPY_SOCKET=/tmp/klippy_uds variable in the /etc/default/klipper file needed to be changed to KLIPPY_SOCKET=/home/android/printer_data/comms/klippy.sock. In the /home/android/printer_data/config/moonraker.conf moonraker config file I also had to make sure the klippy socket was correct: klippy_uds_address: /home/android/printer_data/comms/klippy.sock. The moonraker socket also needed to change in /etc/default/moonraker to MOONRAKER_SOCKET=/home/android/printer_data/comms/moonraker.sock.

The second thing I had to change was in the nginx configuration. According to the first comment on this Reddit post, I needed to change the /etc/nginx/nginx.conf file to use user android instead of using the www-data user. After this, mainsail and moonraker connected to eachother!

Installing Klipper on my 3d printer

I used this github to get the printer.cfg for my printer. I also followed parts of this guide. I ran the menuconfig to create the bin file using

      
cd ~/klipper/
make menuconfig
        

And I used the settings:

- Micro-controller architecture: STMicroelectronics STM32
- Processor model: STM32F103
- Bootloader offset: 28KiB
- Communication interface: Serial (on USART1 PA10/PA09)
            

After that, create the file using make. Format an SD card with FAT32 and 4069 cluster size, put the klipper.bin file on the root of the SD card, stick it in your printer and Klipper is flashed! On an ender 3 V3 SE the screen will just show an empty background without logo, as Klipper does not use that screen.

Getting klipper running on the tablet

I still got the error about Polkit. On top of this, Klipper also did not want to run. Remember where I installed Python 3.9? That seemed to be the issue now. Running the klipper sysvinit script resulted in it failing to start. When I tried to start the command manually by running /home/android/klippy-env/bin/python /home/android/klipper/klippy/klippy.py /home/android/printer_data/config/printer.cfg -l /home/android/printer_data/logs/klippy.log -a /home/android/printer_data/comms/klippy.sock I got this error:

  Could not find platform independent libraries <prefix>
  Could not find platform dependent libraries <exec_prefix>
  Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
  Fatal Python error: initfsencoding: unable to load the file system codec
  ModuleNotFoundError: No module named 'encodings'
  
  Current thread 0x0000007b4e7c6010 (most recent call first):
  Aborted
          

I thought the problem was maybe the python virtual environment, so I tried to get the version of the environment binary. When I executed /home/android/klippy-env/bin/python -VI got this error:

  Could not find platform independent libraries <prefix>
  Could not find platform dependent libraries <exec_prefix>
  Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
  Python 3.7.3          
          

I recreated the venv and reinstalled the requirements:

      
rm -rf /home/android/klippy-env
python3 -m venv /home/android/klippy-env
source /home/android/klippy-env/bin/activate
pip install -r /home/android/klipper/scripts/klippy-requirements.txt
        

However, when installing the requirements I got the error AttributeError: 'HTMLParser' object has no attribute 'unescape'. This is where I got the idea that python3.9 was the issue, as the HTMLParser got deprecated in python3.9.

I removed it by running sudo apt remove python3.9. When I now tried to install the requirements, I got this error:

  ImportError: cannot import name 'sysconfig' from 'distutils' (/usr/lib/python3.9/distutils/__init__.py)          

My next thing to try was to reinstall the python3.7 packages:

      
sudo apt-get install --reinstall python3.7 python3.7-venv python3.7-dev
sudo apt-get install --reinstall python3-distutils
      
    

However, reinstalling the packages did not work, as I got the error:

The following packages have unmet dependencies:
python3.7-dev : Depends: libpython3.7-dev (= 3.7.3-2+deb10u3) but it is not going to be installed
                Depends: libexpat1-dev but it is not going to be installed
                Recommends: libc6-dev but it is not going to be installed or
                            libc-dev
E: Unable to correct problems, you have held broken packages.            

I also got the error moonraker-env/bin/python: /lib/aarch64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by moonraker-env/bin/python) when trying to run moonraker. To finally fix this, I ended up upgrading to debian 11. This finally fixed it and I could run klipper without issue.

Getting KlipperScreen to run

After installing KlipperScreen, getting it to run was a whole other can of worms. The first part was to make sure my tablet recognized the printer. I wanted to do this by using an OTG + charge cable. I first tried to make a Y cable from a usb-c to usb-c cable from which I disconnected the 5V and GND wires to connect them to a power supply, and that worked for a bit when I connected it to my PC, but after a while Windows gave a message that the device caused a malfunction and it didn't work anymore. Then, I bought this RUXELY USB-C OTG-adapter so I could charge the device through a power brick without having to DIY an OTG cable. After installing octo4a on the tablet, connecting the cable and turning on the printer, it successfully recognized my printer on address /dev/bus/001/002 (this can change on every reconnect. We will account for this later).

Following the Github article, I closed the debian VM and added a mount by editing the container:

Mounts:
  enable: yes
  Mount point:
    Source: /data/data/com.octo4a/files
    Target: /home/android/octo4a

Now, I restarted the container and checked if it worked. However, I got the error:

mcu 'mcu': Unable to open serial port: [Errno 13] could not open port /home/android/octo4a/serialpipe: 
[Errno 13] Permission denied: '/home/android/octo4a/serialpipe'
        

I managed to fix this by fixing the permissions:

      
sudo chmod 666 /home/android/octo4a/serialpipe
sudo chown android:android -R /home/android/octo4a
      
      

This fixed the mcu error, but now I got these two errors when starting klipperscreen:

Running /home/android/KlipperScreen/scripts/launch_KlipperScreen.sh
setting DISPLAY=:0 and running xterm to start klipperscreen
Unable to init server: Could not connect: Connection refused
Unable to init server: Could not connect: Connection refused
20240907 23:12:01,801 [functions.py:log_start] - --------------------KlipperScreen Log Start--------------------
20240907 23:12:01,802 [functions.py:log_start] - KlipperScreen Version: v0.4.3-58-g7fd759d5
20240907 23:12:01,802 [functions.py:log_start] - Python version: 3.9
Unable to init server: Could not connect: Connection refused
20240907 23:12:01,808 [screen.py:__init__] - Gtk couldn't be initialized. Use Gtk.init_check() if you want to handle this case.

Traceback (most recent call last):
  File "/home/android/KlipperScreen/screen.py", line 81, in __init__
    super().__init__(title="KlipperScreen")
  File "/home/android/.KlipperScreen-env/lib/python3.9/site-packages/gi/overrides/Gtk.py", line 508, in __init__
    raise RuntimeError(
RuntimeError: Gtk couldn't be initialized. Use Gtk.init_check() if you want to handle this case.
Traceback (most recent call last):
  File "/home/android/KlipperScreen/screen.py", line 81, in __init__
    super().__init__(title="KlipperScreen")
  File "/home/android/.KlipperScreen-env/lib/python3.9/site-packages/gi/overrides/Gtk.py", line 508, in __init__
    raise RuntimeError(
RuntimeError: Gtk couldn't be initialized. Use Gtk.init_check() if you want to handle this case.
          
(==) Log file: "/var/log/Xorg.0.log", Time: Sat Sep  7 23:13:29 2024
(==) Using system config directory "/usr/share/X11/xorg.conf.d"
(EE)
Fatal server error:
(EE) parse_vt_settings: Cannot find a free VT: Inappropriate ioctl for device
(EE)
(EE)
Please consult the The X.Org Foundation support
          at http://wiki.x.org
  for help.
(EE) Please also check the log file at "/var/log/Xorg.0.log" for additional information.
(EE)
(EE) Server terminated with error (1). Closing log file.            
          

I managed to fix this by creating the file /home/android/KlipperScreen/scripts/launch_KlipperScreen.sh:


echo "setting DISPLAY=:0 and running xterm to start klipperscreen"
export DISPLAY=:0
xterm
      

UPDATE 10-11-2024: The launch_KlipperScreen script is automatically called by the KlipperScreen-start.sh script. The KlipperScreen-start script checks if the launch_KlipperScreen script exists, and if it does, it executes it. It's shortly mentioned here on the docs. Thanks Stefan for pointing this out!

I also created a sysvinit file for klipperscreen at /etc/init.d/klipperscreen

        
#!/bin/sh
### BEGIN INIT INFO
# Provides:          klipperscreen
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start KlipperScreen service
# Description:       This file starts and stops KlipperScreen.
### END INIT INFO

NAME=klipperscreen
USER=android
KS_DIR="/home/android/KlipperScreen"
KS_EXEC="$KS_DIR/scripts/KlipperScreen-start.sh"
PIDFILE="/var/run/$NAME.pid"

. /lib/lsb/init-functions

start() {
    log_daemon_msg "Starting KlipperScreen service"
    start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --chuid $USER --exec $KS_EXEC \
    >> /home/android/printer_data/logs/klipperscreen.log 2>&1 # 2>&1 means redirect stderr to stdout
    log_end_msg $?
}

stop() {
    log_daemon_msg "Stopping KlipperScreen service"
    start-stop-daemon --stop --quiet --pidfile $PIDFILE --retry 5
    log_end_msg $?
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart|reload|force-reload)
        stop
        start
        ;;
    status)
        status_of_proc "$KS_EXEC" "$NAME" && exit 0 || exit $?
        ;;
    *)
        echo "Usage: /etc/init.d/klipperscreen {start|stop|restart|status}"
        exit 1
        ;;
esac

exit 0
        
      

The result

Finally, after all this, I had mainsail, moonraker, klipper and klipperscreen all working on the tablet

klipperscreen mainsail

I also updated the octo4a questionnaire and created a PR to add my changes.

UPDATE 15-10-2024
Sometimes, when I restart the container or the tablet, for some reason octoprint just forgets it's installed and I have to clear the data and install it again. The steps I then have to do to make sure everything works again are:

  1. clear data of octoprint and reinstall it
  2. turn off printer
  3. remove power cable from usb hub
  4. start printer
  5. insert power cable from usb hub
  6. reset the permissions on the octoprint serialpipe file: sudo chmod 666 /home/android/octo4a/serialpipe; sudo chown android:android -R /home/android/octo4a
  7. Restart klipper and klipperscreen: sudo /etc/init.d/klipper restart; sudo /etc/init.d/klipperscreen restart