16 Commits

Author SHA1 Message Date
Sims
7c564e8ab8 Merge pull request #166 from suchmememanyskill/dev
Hopefully fix webflash for s3
2025-02-09 01:57:02 +01:00
suchmememanyskill
5c0ee08135 Hopefully fix webflash for s3 2025-02-09 01:14:07 +01:00
Sims
e6b5d17f6a Merge pull request #164 from suchmememanyskill/dev
v2.1.0
2025-02-01 19:39:18 +01:00
suchmememanyskill
d8a9b13fe1 Fix compile 2025-02-01 17:37:06 +01:00
suchmememanyskill
fbc2964ad8 Make serial script work with both sudo and not sudo 2025-02-01 17:20:06 +01:00
suchmememanyskill
d616ef5a6d Allow running service under root 2025-01-28 21:50:21 +01:00
suchmememanyskill
9bfbb1cb0e Change keyboard for bambu setup (#161) 2025-01-28 21:23:02 +01:00
suchmememanyskill
8d8e36fde9 Merge #152 2025-01-28 21:20:22 +01:00
Kire Dyfvelsten
4e3cd69b63 Sunton esp32 2.2" c (#151)
* Update readme

* Update README.md

* driver definition? for a vertical 2.2" with touch

* Update platformio.ini

* Update esp32-2432S022C-vertical.json

* Update esp32-2432S022C-vertical.json

---------

Co-authored-by: Sims <38142618+suchmememanyskill@users.noreply.github.com>
2025-01-28 21:19:36 +01:00
Sims
3aeb67cfdd esp32-CROWPANEL-35C (#159)
* :)

* Correct screen rotation

* Remove brightness adjustment

* Silence buzzer

* Fixes from atomique
2025-01-28 21:16:46 +01:00
suchmememanyskill
8f46b9972d Add aliexpress links 2025-01-15 22:18:36 +01:00
Sims
5756b31744 Update README.md 2025-01-10 18:15:56 +01:00
suchmememanyskill
99b70622fe Update readme 2025-01-09 23:14:15 +01:00
Sims
b3d405b355 Merge pull request #139 from suchmememanyskill/dev
v2.0.0 - Happy new year!
2025-01-09 23:05:35 +01:00
Sims
9a96f9336f Merge pull request #125 from suchmememanyskill/dev
v1.8.0
2024-08-31 11:49:48 +02:00
Sims
5c46764c7c Merge pull request #122 from suchmememanyskill/dev
v1.7.0
2024-08-02 21:39:42 +02:00
9 changed files with 258 additions and 40 deletions

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -31,7 +31,7 @@ jobs:
# ~/.platformio/.cache
# key: ${{ runner.os }}-pio-cyd-klipper
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: '3.9'
@@ -43,13 +43,13 @@ jobs:
python3 ci.py
- name: Upload artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: firmware
path: ./out
- name: Upload GitHub Page Artifact
uses: actions/upload-pages-artifact@v2
uses: actions/upload-pages-artifact@v3
deploy:
environment:
@@ -65,5 +65,5 @@ jobs:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
uses: actions/deploy-pages@v4

View File

@@ -0,0 +1,127 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32_DEV'",
"'-D ESP32_2432S022C'",
"'-D LCD_WIDTH=240'",
"'-D LCD_HEIGHT=320'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT/8)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=0'",
"'-D LCD_ST7789_I80'",
"'-D ST7789_I80_BUS_CONFIG_CLK_SRC=LCD_CLK_SRC_PLL160M'",
"'-D ST7789_I80_BUS_CONFIG_DC=16'",
"'-D ST7789_I80_BUS_CONFIG_WR=4'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D8=15'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D9=13'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D10=12'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D11=14'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D12=27'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D13=25'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D14=33'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D15=32'",
"'-D ST7789_I80_BUS_CONFIG_BUS_WIDTH=8'",
"'-D ST7789_I80_BUS_CONFIG_MAX_TRANSFER_BYTES=(LVGL_BUFFER_PIXELS * sizeof(lv_color_t))'",
"'-D ST7789_I80_BUS_CONFIG_PSRAM_TRANS_ALIGN=64'",
"'-D ST7789_I80_BUS_CONFIG_SRAM_TRANS_ALIGN=4'",
"'-D ST7789_IO_I80_CONFIG_CS_GPIO_NUM=17'",
"'-D ST7789_IO_I80_CONFIG_PCLK_HZ=12000000'",
"'-D ST7789_IO_I80_CONFIG_TRANS_QUEUE_DEPTH=10'",
"'-D ST7789_IO_I80_CONFIG_LCD_CMD_BITS=8'",
"'-D ST7789_IO_I80_CONFIG_LCD_PARAM_BITS=8'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_IDLE_LEVEL=0'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_CMD_LEVEL=0'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_DUMMY_LEVEL=0'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_DATA_LEVEL=1'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_CS_ACTIVE_HIGH=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_REVERSE_COLOR_BITS=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_SWAP_COLOR_BYTES=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_PCLK_ACTIVE_NEG=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_PCLK_IDLE_LOW=0'",
"'-D ST7789_DEV_CONFIG_RESET_GPIO_NUM=GPIO_NUM_NC'",
"'-D ST7789_DEV_CONFIG_COLOR_SPACE=ESP_LCD_COLOR_SPACE_RGB'",
"'-D ST7789_DEV_CONFIG_BITS_PER_PIXEL=16'",
"'-D ST7789_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH=false'",
"'-D ST7789_DEV_CONFIG_VENDOR_CONFIG=NULL'",
"'-D ST7789_RD_GPIO=2'",
"'-D LCD_SWAP_XY=false'",
"'-D LCD_MIRROR_X=false'",
"'-D LCD_MIRROR_Y=false'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_CST816S_I2C'",
"'-D CST816S_I2C_HOST=I2C_NUM_0'",
"'-D CST816S_I2C_CONFIG_SDA_IO_NUM=21'",
"'-D CST816S_I2C_CONFIG_SCL_IO_NUM=22'",
"'-D CST816S_I2C_CONFIG_SDA_PULLUP_EN=GPIO_PULLUP_ENABLE'",
"'-D CST816S_I2C_CONFIG_SCL_PULLUP_EN=GPIO_PULLUP_ENABLE'",
"'-D CST816S_I2C_CONFIG_MASTER_CLK_SPEED=400000'",
"'-D CST816S_I2C_CONFIG_CLK_FLAGS=0'",
"'-D CST816S_IO_I2C_CONFIG_DEV_ADDR=ESP_LCD_TOUCH_IO_I2C_CST816S_ADDRESS'",
"'-D CST816S_IO_I2C_CONFIG_CONTROL_PHASE_BYTES=1'",
"'-D CST816S_IO_I2C_CONFIG_DC_BIT_OFFSET=0'",
"'-D CST816S_IO_I2C_CONFIG_LCD_CMD_BITS=8'",
"'-D CST816S_IO_I2C_CONFIG_LCD_PARAM_BITS=0'",
"'-D CST816S_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D CST816S_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE=true'",
"'-D CST816S_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D CST816S_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D CST816S_TOUCH_CONFIG_RST_GPIO_NUM=GPIO_NUM_NC'",
"'-D CST816S_TOUCH_CONFIG_INT_GPIO_NUM=GPIO_NUM_NC'",
"'-D CST816S_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D CST816S_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=false'",
"'-D TOUCH_SWAP_X=false'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=5'",
"'-D TF_SPI_MOSI=23'",
"'-D TF_SPI_SCLK=18'",
"'-D TF_SPI_MISO=19'",
"'-D BOARD_HAS_SPEAK'",
"'-D SPEAK=26'",
"-DCYD_SCREEN_GAP_PX=8",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35",
"-DCYD_SCREEN_VERTICAL=1",
"-DCYD_SCREEN_FONT=lv_font_montserrat_14",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_10",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=40",
"-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1",
"-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1",
"-DCYD_SCREEN_DISABLE_INVERT_COLORS=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-2432S022C-V",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005006284154750.html",
"vendor": "Sunton"
}

View File

@@ -84,7 +84,7 @@ board = esp32-3248S035C-smartdisplay
board = esp32-4827S043C-smartdisplay
[env:esp32-4827S043R-SD]
board = esp32-4827S043C-smartdisplay
board = esp32-4827S043R-smartdisplay
[env:esp32-8048S043C-SD]
board = esp32-8048S043C-smartdisplay
@@ -92,6 +92,9 @@ board = esp32-8048S043C-smartdisplay
[env:esp32-8048S043C-SD-alt]
board = esp32-8048S043C-smartdisplay-alt
[env:esp32-2432S022C-SD-V]
board = esp32-2432S022C-vertical
[env:esp32-CROWPANEL-28R]
board = esp32-CROWPANEL-28R
lib_deps =
@@ -113,3 +116,4 @@ lib_deps =
plageoj/UrlEncode@^1.0.1
knolleary/PubSubClient@^2.8
WiFiClientSecure

View File

@@ -375,6 +375,8 @@ void show_ip_entry()
lv_textarea_set_placeholder_text(host_entry, "Printer IP");
lv_textarea_set_placeholder_text(port_entry, "Access code");
lv_textarea_set_placeholder_text(auth_entry, "Printer serial number");
lv_obj_clear_flag(auth_entry, LV_OBJ_FLAG_USER_2);
lv_obj_add_flag(auth_entry, LV_OBJ_FLAG_USER_3);
break;
case PrinterType::PrinterTypeOctoprint:
lv_obj_clear_flag(auth_entry, LV_OBJ_FLAG_USER_2);

View File

@@ -2,9 +2,10 @@
[![Donations](https://img.shields.io/badge/Support%20on-Ko--Fi-red)](https://ko-fi.com/suchmememanyskill)
# CYD-Klipper
An implementation of a wireless Klipper status display on an ESP32 + screen. Uses Moonraker to fetch data.
An implementation of a wireless Klipper, Bambu and Octoprint status display on an ESP32 + screen. Uses Moonraker to fetch data.
A simple and cheap solution to use a dedicated screen with Klipper, a 3d printing Firmware.
Also now with Bambu Lab and Octoprint printers!
![showcase_image](readme/PXL_20231113_171629383.jpg)
@@ -24,6 +25,8 @@ A ESP32-2432S028R is required to run this project. You can find out where to buy
- Toggle Moonraker power devices
- OTA updates
- Serial console over USB (115200 8n1, echo off, LF/LF)
- Control Klipper, Octoprint and Bambu printers.
- Wired Serial/Usb Klipper connection
### Install
@@ -39,6 +42,21 @@ If you found this project helpful, please consider a donation [to my Ko-Fi](http
Thank you!
### Where to buy hardware
All links below are affiliate links. Please also check yourself if there is a cheaper version available than the ones below. I have only linked ones that i have personally bought.
*ESP32-2432S028R (2.8" Resistive, Cheapest)*
- [USB C + microB version](https://s.click.aliexpress.com/e/_omjsYBJ)
- [Another USB C + microB version](https://s.click.aliexpress.com/e/_olKBkmz)
- [microB version](https://s.click.aliexpress.com/e/_oCWhgmN)
*ESP32-2432S032C (3.2" Capacitive)*
- [Only the capacitive version is supported! USB-C](https://s.click.aliexpress.com/e/_okbSGmd)
- [IPS version (not that great of a screen), Only the capacitive version is supported! USB-C](https://s.click.aliexpress.com/e/_oFygVwt)
*ESP32-3248S035C (3.5" Capacitive)*
- [microB version](https://s.click.aliexpress.com/e/_oCqygE9)
### Screenshots
(Quite literally shots of the screen. I'm sorry)

View File

@@ -8,7 +8,7 @@
font-family: 'Roboto', sans-serif;
}
TT {
TT {
font-family: 'Terminal', monospace;
background-color: #080a0b;
}
@@ -24,7 +24,8 @@
max-width: 750px;
}
.main > section > :not(:first-child) {
.main > section > :not(:first-child),
.indent {
margin-left: 20px;
}
@@ -44,6 +45,17 @@
#changelog-body {
white-space: break-spaces;
}
.where-to-get details summary::marker
{
content: '';
}
.where-to-get details[open] summary p
{
display: none;
}
</style>
<script type="module" src="https://unpkg.com/esp-web-tools@9/dist/web/install-button.js?module"></script>
<script src="//code.iconify.design/1/1.0.6/iconify.min.js"></script>
@@ -71,7 +83,7 @@
<body>
<section class="main">
<h2>CYD-Klipper <span class="iconify" data-icon="mdi-printer-3d" style="color: #F44;"></span></h2>
<p>An implementation of a Klipper status display on an ESP32 + screen.<br>Uses Moonraker to fetch data.<br><a href="https://github.com/suchmememanyskill/CYD-Klipper">Source code is available on GitHub</a>.</p>
<p>An implementation of a wireless Klipper, Bambu and Octoprint status display on an ESP32 + screen.<br>Uses Moonraker to fetch data.<br><a href="https://github.com/suchmememanyskill/CYD-Klipper">Source code is available on GitHub</a>.</p>
<section class="changelog">
<h3 id="changelog-header"><span class="iconify" data-icon="mdi-hammer-wrench" style="color: lightgray;"></span> Changelog <span id="changelog-header-version"></span></h3>
@@ -83,6 +95,34 @@
<p>If you found this project helpful, please consider a donation to <a href="https://ko-fi.com/suchmememanyskill">my Ko-Fi</a>.<br>It would help out a lot in the development of this project, due to the need to buy the screens.<br>Thank you!</p>
</section>
<section class="where-to-get">
<details>
<summary>
<h3><span class="iconify" data-icon="mdi-shopping" style="color:orange; filter: drop-shadow(0 0 0.75rem orange);"></span> Where to buy hardware</h3>
<p class="indent">(Click to expand)</p>
</summary>
<section class="indent">
<p>All links below are affiliate links. Please also check yourself if there is a cheaper version available than the ones below. I have only linked ones that i have personally bought.</p>
<i>ESP32-2432S028R (2.8" Resistive, Cheapest)</i>
<ul>
<li><a href="https://s.click.aliexpress.com/e/_omjsYBJ">USB C + microB version</a></li>
<li><a href="https://s.click.aliexpress.com/e/_olKBkmz">Another USB C + microB version</a></li>
<li><a href="https://s.click.aliexpress.com/e/_oCWhgmN">microB version</a></li>
</ul>
<i>ESP32-2432S032C (3.2" Capacitive)</i>
<ul>
<li><a href="https://s.click.aliexpress.com/e/_okbSGmd">Only the capacitive version is supported! USB-C</a></li>
<li><a href="https://s.click.aliexpress.com/e/_oFygVwt">IPS version (not that great of a screen), Only the capacitive version is supported! USB-C</a></li>
</ul>
<i>ESP32-3248S035C (3.5" Capacitive)</i>
<ul>
<li><a href="https://s.click.aliexpress.com/e/_oCqygE9">microB version</a></li>
</ul>
</section>
</details>
</section>
<section class="issues">
<h3><span class="iconify" data-icon="mdi-github" style="color: white; filter: drop-shadow(0 0 0.75rem gray);"></span> Report Issues</h3>
<p>If you experience any issues with this project, or have any feature requests for the project, please report them on the <a href="https://github.com/suchmememanyskill/CYD-Klipper/issues">issues tab on Github</a>.</p>

10
ci.py
View File

@@ -34,19 +34,19 @@ def get_manifest(base_path : str, device_name : str):
"parts": [
{
"path": f"{base_path}/bootloader.bin",
"offset": 4096
"offset": 0 if device_name in ESP_S3_CHIPS else 0x1000
},
{
"path": f"{base_path}/partitions.bin",
"offset": 32768
"offset": 0x8000
},
{
"path": f"{base_path}/boot_app0.bin",
"offset": 57344
"offset": 0xe000
},
{
"path": f"{base_path}/firmware.bin",
"offset": 65536
"offset": 0x10000
}
]
}
@@ -83,7 +83,7 @@ for port in CYD_PORTS:
shutil.copy(os.path.join(os.path.expanduser("~"), ".platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin"), f"{port_path}/boot_app0.bin")
os.chdir(port_path)
if (port in ESP_S3_CHIPS):
subprocess.run(["python3", "-m", "esptool", "--chip", "esp32s3", "merge_bin", "-o", "merged_firmware.bin", "--flash_mode", "qio", "--flash_freq", "80m", "--flash_size", "16MB", "0x1000", "bootloader.bin", "0x8000", "partitions.bin", "0xe000", "boot_app0.bin", "0x10000", "firmware.bin"], check=True)
subprocess.run(["python3", "-m", "esptool", "--chip", "esp32s3", "merge_bin", "-o", "merged_firmware.bin", "--flash_mode", "dio", "--flash_freq", "80m", "--flash_size", "16MB", "0x0000", "bootloader.bin", "0x8000", "partitions.bin", "0xe000", "boot_app0.bin", "0x10000", "firmware.bin"], check=True)
else:
subprocess.run(["python3", "-m", "esptool", "--chip", "esp32", "merge_bin", "-o", "merged_firmware.bin", "--flash_mode", "dio", "--flash_freq", "40m", "--flash_size", "4MB", "0x1000", "bootloader.bin", "0x8000", "partitions.bin", "0xe000", "boot_app0.bin", "0x10000", "firmware.bin"], check=True)

View File

@@ -1,8 +1,21 @@
#!/bin/bash
if [ "$EUID" -eq 0 ]; then
echo "Please do not run as root"
exit
read -r -p "Are you sure you want to run this service as root? [y/N] " response
response=${response,,} # tolower
if ! [[ "$response" =~ ^(yes|y)$ ]]; then
exit
fi
SERVICE_PATH="/etc/systemd/system/cyd-klipper-serial.service"
else
echo "Are you sure you want to run this service as the current user?"
read -r -p "Make sure this user is logged in at boot! [y/N] " response
response=${response,,} # tolower
if ! [[ "$response" =~ ^(yes|y)$ ]]; then
exit
fi
mkdir -p ~/.config/systemd/user
SERVICE_PATH="$HOME/.config/systemd/user/cyd-klipper-serial.service"
fi
set -e
@@ -15,20 +28,26 @@ source ./env/bin/activate
pip3 install -r requirements.txt
# Create systemd unit file
mkdir -p ~/.config/systemd/user
echo "[Unit]" > ~/.config/systemd/user/cyd-klipper-serial.service
echo "Description=CYD Klipper serial server" >> ~/.config/systemd/user/cyd-klipper-serial.service
echo "After=network.target" >> ~/.config/systemd/user/cyd-klipper-serial.service
echo "" >> ~/.config/systemd/user/cyd-klipper-serial.service
echo "[Service]" >> ~/.config/systemd/user/cyd-klipper-serial.service
echo "ExecStart=$(pwd)/run.sh" >> ~/.config/systemd/user/cyd-klipper-serial.service
echo "WorkingDirectory=$(pwd)" >> ~/.config/systemd/user/cyd-klipper-serial.service
echo "Restart=always" >> ~/.config/systemd/user/cyd-klipper-serial.service
echo "" >> ~/.config/systemd/user/cyd-klipper-serial.service
echo "[Install]" >> ~/.config/systemd/user/cyd-klipper-serial.service
echo "WantedBy=multi-user.target" >> ~/.config/systemd/user/cyd-klipper-serial.service
echo "[Unit]" > $SERVICE_PATH
echo "Description=CYD Klipper serial server" >> $SERVICE_PATH
echo "After=network.target" >> $SERVICE_PATH
echo "" >> $SERVICE_PATH
echo "[Service]" >> $SERVICE_PATH
echo "ExecStart=$(pwd)/run.sh" >> $SERVICE_PATH
echo "WorkingDirectory=$(pwd)" >> $SERVICE_PATH
echo "Restart=always" >> $SERVICE_PATH
echo "" >> $SERVICE_PATH
echo "[Install]" >> $SERVICE_PATH
echo "WantedBy=multi-user.target" >> $SERVICE_PATH
# Start the service
systemctl --user daemon-reload
systemctl --user enable cyd-klipper-serial
systemctl --user start cyd-klipper-serial
if [ "$EUID" -eq 0 ]; then
systemctl daemon-reload
systemctl enable cyd-klipper-serial
systemctl start cyd-klipper-serial
else
systemctl --user daemon-reload
systemctl --user enable cyd-klipper-serial
systemctl --user start cyd-klipper-serial
fi

View File

@@ -1,13 +1,21 @@
#!/bin/bash
set -e
if [ "$EUID" -eq 0 ]; then
echo "Please do not run as root"
exit
systemctl stop cyd-klipper-serial
systemctl disable cyd-klipper-serial
rm /etc/systemd/system/cyd-klipper-serial.service
systemctl daemon-reload
else
systemctl --user stop cyd-klipper-serial
systemctl --user disable cyd-klipper-serial
rm ~/.config/systemd/user/cyd-klipper-serial.service
systemctl --user daemon-reload
fi
systemctl --user stop cyd-klipper-serial
systemctl --user disable cyd-klipper-serial
rm ~/.config/systemd/user/cyd-klipper-serial.service
systemctl --user daemon-reload
rm -rf ./env