mirror of
https://github.com/suchmememanyskill/CYD-Klipper.git
synced 2026-03-21 05:33:24 +00:00
Serial installer
This commit is contained in:
39
serial/README.md
Normal file
39
serial/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Serial connection
|
||||
|
||||
This section elaborates on how the host side (linux) should be set up to handle incoming CYD-Klipper requests.
|
||||
|
||||
## Install (Linux)
|
||||
|
||||
1. Log into your host running klipper
|
||||
1. Cd into a folder where CYD-Klipper will live, for example your home folder
|
||||
- To enter your home folder, type `cd ~`
|
||||
1. `git clone https://github.com/suchmememanyskill/CYD-Klipper`
|
||||
1. `cd CYD-Klipper/serial`
|
||||
1. `chmod a+x install.sh`
|
||||
1. `./install.sh`
|
||||
|
||||
## Uninstall (Linux)
|
||||
1. Log into your host running klipper
|
||||
1. Cd into the folder where CYD-Klipper is installed
|
||||
1. `cd serial`
|
||||
1. `chmod a+x uninstall.sh`
|
||||
1. `./uninstall.sh`
|
||||
|
||||
## Configuration
|
||||
|
||||
The installer creates a systemd service in `~/.config/systemd/user/cyd-klipper-serial.service`. If manual configuration is needed (for example, if moonraker runs on another port or another host, or if the esp32 could not be found), you can edit this file to add environment variables.
|
||||
|
||||
To add an environemnt variable in a systemd unit file, inside the `[Service]` section, write `Environment="KEY=VALUE"`, where KEY is the environemnt variable's name, and VALUE is the environment variable's value.
|
||||
|
||||
After editing the cyd-klipper-serial.service file, run `systemctl --user daemon-reload` then `systemctl --user restart cyd-klipper-serial`
|
||||
|
||||
| Name | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
|KLIPPER_PROTOCOL|Protocol used. `http` or `https`|`http`|
|
||||
|KLIPPER_HOST|The IP or hostname the Moonraker server runs on|`localhost`|
|
||||
|KLIPPER_PORT|The port of the Moonraker webserver|[`80`, `7125`]|
|
||||
|ESP32_SERIAL|Path to the serial device, like `/dev/ttyUSB0`|None, see below
|
||||
|
||||
By default, a connection to moonraker is attempted to `http://localhost:80` and `http://localhost:7125`. If no connection can be made, the script refuses to continue. The script will indefinitely attempt to re-connect. See the logs generated for more information.
|
||||
|
||||
By default, ESP32 devices are attempted to be located on the system. If too many are located, or none are located, the script refuses to continue. If one is found, an attempt is made to connect to the device. See the logs generated for more information.
|
||||
30
serial/install.sh
Normal file
30
serial/install.sh
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
echo "Please do not run as root"
|
||||
exit
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
# Install dependencies
|
||||
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=python3 $(pwd)/serial_server.py" >> ~/.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
|
||||
|
||||
# Start the service
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable cyd-klipper-serial
|
||||
systemctl --user start cyd-klipper-serial
|
||||
2
serial/requirements.txt
Normal file
2
serial/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
pyserial
|
||||
requests
|
||||
183
serial/serial_server.py
Normal file
183
serial/serial_server.py
Normal file
@@ -0,0 +1,183 @@
|
||||
import serial
|
||||
import requests
|
||||
import os
|
||||
import serial.tools.list_ports
|
||||
import time
|
||||
|
||||
SERIAL_PORT = 'COM6'
|
||||
BAUD_RATE = 115200
|
||||
PROTOCOL = "http"
|
||||
HOSTNAME = 'localhost'
|
||||
PORT = 80
|
||||
|
||||
def test_request(protocol : str, hostname : str, port : int) -> bool:
|
||||
try:
|
||||
print(f"Sending test request to {hostname}:{port}")
|
||||
response = requests.get(f"{protocol}://{hostname}:{port}/printer/info")
|
||||
return response.status_code == 200
|
||||
except requests.exceptions.RequestException:
|
||||
return False
|
||||
|
||||
def find_klipper_host() -> bool:
|
||||
global PROTOCOL, HOSTNAME, PORT
|
||||
|
||||
protocol = PROTOCOL
|
||||
host = HOSTNAME
|
||||
port = PORT
|
||||
|
||||
if "KLIPPER_PROTOCOL" in os.environ:
|
||||
protocol = os.environ["KLIPPER_PROTOCOL"]
|
||||
|
||||
if "KLIPPER_HOST" in os.environ:
|
||||
host = os.environ["KLIPPER_HOST"]
|
||||
|
||||
if "KLIPPER_PORT" in os.environ:
|
||||
port = int(os.environ["KLIPPER_PORT"])
|
||||
|
||||
if test_request(protocol, host, port):
|
||||
HOSTNAME = host
|
||||
PORT = port
|
||||
return True
|
||||
|
||||
port = 80
|
||||
|
||||
if test_request(protocol, host, port):
|
||||
HOSTNAME = host
|
||||
PORT = port
|
||||
return True
|
||||
|
||||
port = 7125
|
||||
|
||||
if test_request(protocol, host, port):
|
||||
HOSTNAME = host
|
||||
PORT = port
|
||||
return True
|
||||
|
||||
print("Could not find Klipper host. Please specify the hostname and port using the KLIPPER_HOST and KLIPPER_PORT environment variables.")
|
||||
return False
|
||||
|
||||
def find_esp32() -> bool:
|
||||
global SERIAL_PORT
|
||||
|
||||
if "ESP32_SERIAL" in os.environ:
|
||||
SERIAL_PORT = os.environ["ESP32_SERIAL"]
|
||||
|
||||
if os.path.exists(SERIAL_PORT):
|
||||
return True
|
||||
else:
|
||||
print(f"Specified serial port {SERIAL_PORT} does not exist.")
|
||||
|
||||
possible_devices = []
|
||||
|
||||
for port in serial.tools.list_ports.comports():
|
||||
if port.vid == 0x10C4 and port.pid == 0xEA60:
|
||||
possible_devices.append(port)
|
||||
elif port.vid == 0x1A86 and port.pid == 0x7523:
|
||||
possible_devices.append(port)
|
||||
|
||||
if len(possible_devices) == 1:
|
||||
SERIAL_PORT = possible_devices[0].device
|
||||
return True
|
||||
elif len(possible_devices) > 1:
|
||||
print("Multiple ESP32 devices found. Please specify the serial port using the ESP32_SERIAL environment variable.")
|
||||
return False
|
||||
else:
|
||||
print("No ESP32 devices found. Please specify the serial port using the ESP32_SERIAL environment variable.")
|
||||
return False
|
||||
|
||||
# --------- #
|
||||
|
||||
ser : serial.Serial = None
|
||||
|
||||
def truncuate(text : str, length : int = 50):
|
||||
length = length - 3
|
||||
if len(text) > length:
|
||||
return text[:length] + "..."
|
||||
return text
|
||||
|
||||
def write(text : str, write : bool):
|
||||
if write:
|
||||
ser.write((text + "\n").encode('utf-8'))
|
||||
print(f"<<< {truncuate(text)}")
|
||||
else:
|
||||
print(f"(Ignored) <<< {truncuate(text)}")
|
||||
|
||||
def main():
|
||||
while True:
|
||||
# Read a line from the serial port
|
||||
if ser.in_waiting > 0:
|
||||
line = ser.readline().decode('utf-8').strip()
|
||||
if line.startswith("HTTP_REQUEST") or line.startswith("HTTP_BINARY"):
|
||||
print(f">>> {line}")
|
||||
# Parse the parameters
|
||||
try:
|
||||
parts = line.split(' ', 3)
|
||||
timeout_ms, request_type, url_path = int(parts[1]), parts[2], parts[3]
|
||||
|
||||
ignore_timeout = timeout_ms <= 0
|
||||
binary = line.startswith("HTTP_BINARY")
|
||||
|
||||
if ignore_timeout:
|
||||
timeout_ms = 1000;
|
||||
|
||||
# Construct the full URL
|
||||
full_url = f"{PROTOCOL}://{HOSTNAME}:{PORT}{url_path}"
|
||||
|
||||
# Make the HTTP request based on the type
|
||||
response = None
|
||||
if request_type.upper() == "GET":
|
||||
response = requests.get(full_url, timeout=timeout_ms / 1000)
|
||||
elif request_type.upper() == "POST":
|
||||
response = requests.post(full_url, timeout=timeout_ms / 1000)
|
||||
else:
|
||||
write("400 Unsupported request type", not ignore_timeout)
|
||||
continue
|
||||
|
||||
# Send response back over serial
|
||||
if response != None:
|
||||
if binary:
|
||||
if response.status_code != 200:
|
||||
write("00000000", not ignore_timeout)
|
||||
else:
|
||||
length = len(response.content)
|
||||
ser.write(f"{length:>08}".encode('utf-8'))
|
||||
ser.write(response.content)
|
||||
print(f"<<< (Binary data of {length} bytes)")
|
||||
else:
|
||||
status_code = response.status_code
|
||||
body = response.text.replace('\n', ' ') # Trim and sanitize body for serial
|
||||
message = f"{status_code} {body}"
|
||||
write(message, not ignore_timeout)
|
||||
except (IndexError, ValueError) as e:
|
||||
write(f"400 Malformed request {str(e)}", not ignore_timeout)
|
||||
except requests.exceptions.ReadTimeout as e:
|
||||
print("Request timed out.")
|
||||
pass
|
||||
except requests.exceptions.RequestException as e:
|
||||
write("500 Request failed", not ignore_timeout)
|
||||
else:
|
||||
print(f"[LOG] {line}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
while True:
|
||||
try:
|
||||
if not find_klipper_host():
|
||||
time.sleep(5);
|
||||
continue
|
||||
|
||||
if not find_esp32():
|
||||
time.sleep(5);
|
||||
continue
|
||||
|
||||
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\nExiting script.")
|
||||
ser.close()
|
||||
break;
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {str(e)}")
|
||||
ser.close()
|
||||
print("Retrying in 5 seconds...")
|
||||
time.sleep(5)
|
||||
|
||||
13
serial/uninstall.sh
Normal file
13
serial/uninstall.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
echo "Please do not run as root"
|
||||
exit
|
||||
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
|
||||
Reference in New Issue
Block a user