diff --git a/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-1/canvas-toolhead.cfg b/meta-opencentauri/recipes-apps/klipper/files/centauri-carbon-1/canvas-toolhead.cfg similarity index 84% rename from meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-1/canvas-toolhead.cfg rename to meta-opencentauri/recipes-apps/klipper/files/centauri-carbon-1/canvas-toolhead.cfg index c6b21198..600f5c5c 100644 --- a/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-1/canvas-toolhead.cfg +++ b/meta-opencentauri/recipes-apps/klipper/files/centauri-carbon-1/canvas-toolhead.cfg @@ -48,17 +48,5 @@ press_gcode: release_gcode: RESPOND TYPE=echo MSG="Front cover has been re-attached" -[gcode_button toolhead_tangle_detection] -pin: !hotend:PB0 -debounce_delay: 0.5 -press_gcode: - RESPOND TYPE=echo MSG="Filament tangle detected!" - - {% if printer.print_stats.state == "printing" %} - PAUSE - {% endif %} -release_gcode: - RESPOND TYPE=echo MSG="Filament tangle has been resolved" - [extruder] pulldown_resistor: 100000 diff --git a/meta-opencentauri/recipes-apps/klipper/files/centauri-carbon-2/enclosed.cfg b/meta-opencentauri/recipes-apps/klipper/files/centauri-carbon-2/enclosed.cfg new file mode 100644 index 00000000..9a30dd16 --- /dev/null +++ b/meta-opencentauri/recipes-apps/klipper/files/centauri-carbon-2/enclosed.cfg @@ -0,0 +1,51 @@ +# ----- +# PLEASE DO NOT EDIT THIS FILE. EDIT printer.cfg INSTEAD. +# UPDATES WILL RESET THIS FILE. YOUR EDITS WILL BE LOST WHEN UPDATING. +# ----- + +[fan_generic aux_fan] +pin: PD15 +tachometer_pin: PD14 + +[fan_generic case_fan] +pin: PD17 +tachometer_pin: PD16 + +[servo case_servo] +pin: PB7 +initial_angle: 90.0 + +[led case] +white_pin: PB5 +initial_WHITE: 1 + +[gcode_button case_servo_sensor] +pin: PB2 +press_gcode: + UPDATE_DELAYED_GCODE ID=case_servo_timeout DURATION=0 + G4 P400 ; We have no way to detect when the fan cover is fully open. Instead, the sensor triggers somewhere halfway, and around 400ms is needed to fully open it. + SET_SERVO SERVO=case_servo ANGLE=90 +release_gcode: + UPDATE_DELAYED_GCODE ID=case_servo_timeout DURATION=0 + SET_SERVO SERVO=case_servo ANGLE=90 + +[delayed_gcode case_servo_timeout] +gcode: + SET_SERVO SERVO=case_servo ANGLE=90 + RESPOND TYPE=error MSG="Servo timeout: sensor did not change" + +[gcode_macro EXHAUST_OPEN] +gcode: + {% if printer["gcode_button case_servo_sensor"].state == "PRESSED" %} ; Currently closed + RESPOND MSG="Opening exhaust cover" + UPDATE_DELAYED_GCODE ID=case_servo_timeout DURATION=5 + SET_SERVO SERVO=case_servo ANGLE=135 + {% endif %} + +[gcode_macro EXHAUST_CLOSE] +gcode: + {% if printer["gcode_button case_servo_sensor"].state == "RELEASED" %} ; Currently open + RESPOND MSG="Closing exhaust cover" + UPDATE_DELAYED_GCODE ID=case_servo_timeout DURATION=5 + SET_SERVO SERVO=case_servo ANGLE=45 + {% endif %} \ No newline at end of file diff --git a/meta-opencentauri/recipes-apps/klipper/files/centauri-carbon-2/machine.cfg b/meta-opencentauri/recipes-apps/klipper/files/centauri-carbon-2/machine.cfg index c32b08fe..9eb06c0b 100644 --- a/meta-opencentauri/recipes-apps/klipper/files/centauri-carbon-2/machine.cfg +++ b/meta-opencentauri/recipes-apps/klipper/files/centauri-carbon-2/machine.cfg @@ -364,10 +364,6 @@ runout_gcode: PAUSE {% endif %} -[led case] -white_pin: PB5 -initial_WHITE: 1 - [temperature_sensor chamber] sensor_pin: PB13 sensor_type: NTC 100K beta 3950 @@ -375,49 +371,6 @@ pullup_resistor: 22000 min_temp: 0 max_temp: 125 -[fan_generic aux_fan] -pin: PD15 -tachometer_pin: PD14 - -[fan_generic case_fan] -pin: PD17 -tachometer_pin: PD16 - -[servo case_servo] -pin: PB7 -initial_angle: 90.0 - -[gcode_button case_servo_sensor] -pin: PB2 -press_gcode: - UPDATE_DELAYED_GCODE ID=case_servo_timeout DURATION=0 - G4 P400 ; We have no way to detect when the fan cover is fully open. Instead, the sensor triggers somewhere halfway, and around 400ms is needed to fully open it. - SET_SERVO SERVO=case_servo ANGLE=90 -release_gcode: - UPDATE_DELAYED_GCODE ID=case_servo_timeout DURATION=0 - SET_SERVO SERVO=case_servo ANGLE=90 - -[delayed_gcode case_servo_timeout] -gcode: - SET_SERVO SERVO=case_servo ANGLE=90 - RESPOND TYPE=error MSG="Servo timeout: sensor did not change" - -[gcode_macro EXHAUST_OPEN] -gcode: - {% if printer["gcode_button case_servo_sensor"].state == "PRESSED" %} ; Currently closed - RESPOND MSG="Opening exhaust cover" - UPDATE_DELAYED_GCODE ID=case_servo_timeout DURATION=5 - SET_SERVO SERVO=case_servo ANGLE=135 - {% endif %} - -[gcode_macro EXHAUST_CLOSE] -gcode: - {% if printer["gcode_button case_servo_sensor"].state == "RELEASED" %} ; Currently open - RESPOND MSG="Closing exhaust cover" - UPDATE_DELAYED_GCODE ID=case_servo_timeout DURATION=5 - SET_SERVO SERVO=case_servo ANGLE=45 - {% endif %} - [temperature_fan mainboard] pin: PG15 tachometer_pin: PB9 diff --git a/meta-opencentauri/recipes-apps/klipper/files/shared-macros.cfg b/meta-opencentauri/recipes-apps/klipper/files/shared-macros.cfg index 04638900..a72a64e8 100644 --- a/meta-opencentauri/recipes-apps/klipper/files/shared-macros.cfg +++ b/meta-opencentauri/recipes-apps/klipper/files/shared-macros.cfg @@ -782,9 +782,9 @@ gcode: {% if index == 1 %} # Part cooling fan M006 S{speed} - {% elif index == 2 %} # Aux cooling fan + {% elif index == 2 and 'fan_generic aux_fan' in printer %} # Aux cooling fan SET_FAN_SPEED FAN=aux_fan SPEED={nspeed} - {% elif index == 3 %} # Exhaust fan + {% elif index == 3 and 'fan_generic case_fan' in printer %} # Exhaust fan SET_FAN_SPEED FAN=case_fan SPEED={nspeed} {% else %} {action_raise_error("Invalid fan index")} diff --git a/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.bb b/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.bb index 66dda624..804ce9de 100644 --- a/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.bb +++ b/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.bb @@ -20,6 +20,9 @@ SRC_URI += " \ file://client.cfg \ " +SRC_URI:append:centauri-carbon-1 = " file://canvas-toolhead.cfg" +SRC_URI:append:centauri-carbon-2 = " file://enclosed.cfg" + inherit python3-dir update-rc.d RDEPENDS:${PN} = " \ @@ -113,6 +116,16 @@ do_install() { install -m 0755 ${UNPACKDIR}/klipper-init-d ${D}${sysconfdir}/init.d/klipper } +do_install:append:centauri-carbon-1() { + install -d ${D}${sysconfdir}/klipper/config/extras-readonly + install -m 0644 ${UNPACKDIR}/canvas-toolhead.cfg ${D}${sysconfdir}/klipper/config/extras-readonly/ +} + +do_install:append:centauri-carbon-2() { + install -d ${D}${sysconfdir}/klipper/config/extras-readonly + install -m 0644 ${UNPACKDIR}/enclosed.cfg ${D}${sysconfdir}/klipper/config/extras-readonly/ +} + FILES:${PN} = " \ ${datadir}/klipper \ ${sysconfdir}/init.d/klipper \ diff --git a/meta-opencentauri/recipes-apps/ustreamer/files/ustreamer-init-d b/meta-opencentauri/recipes-apps/ustreamer/files/ustreamer-init-d index b513fcfe..bb42913c 100644 --- a/meta-opencentauri/recipes-apps/ustreamer/files/ustreamer-init-d +++ b/meta-opencentauri/recipes-apps/ustreamer/files/ustreamer-init-d @@ -9,11 +9,17 @@ ### END INIT INFO USTREAMER_EXEC="/usr/bin/ustreamer" -USTREAMER_ARGS="-r 864x480 -c HW -m MJPEG -b 1 -w 1 -f 10 -l --host=0.0.0.0 --allow-origin=*" +CAMERA_DEVICE="/dev/video0" +USTREAMER_ARGS="-d $CAMERA_DEVICE -r 864x480 -c HW -m MJPEG -b 1 -w 1 -f 10 -l --host=0.0.0.0 --allow-origin=*" PIDFILE="/var/run/ustreamer.pid" case "$1" in start) + if [ ! -c "$CAMERA_DEVICE" ]; then + echo "Skipping ustreamer: camera device $CAMERA_DEVICE is unavailable." + exit 0 + fi + echo "Starting ustreamer..." start-stop-daemon -S -b -m -p $PIDFILE \ -x $USTREAMER_EXEC -- $USTREAMER_ARGS diff --git a/meta-opencentauri/recipes-data/config-manager/config-manager_0.1.bb b/meta-opencentauri/recipes-data/config-manager/config-manager_0.1.bb index 08dcd19e..5e5a202f 100644 --- a/meta-opencentauri/recipes-data/config-manager/config-manager_0.1.bb +++ b/meta-opencentauri/recipes-data/config-manager/config-manager_0.1.bb @@ -7,7 +7,7 @@ PACKAGE_ARCH = "${MACHINE_ARCH}" SRC_URI = " \ file://config_manager.py \ file://build-klipper-var-config.sh \ - file://default.conf \ + file://default-machine.conf \ " S = "${UNPACKDIR}" @@ -19,17 +19,17 @@ do_install() { sed -i -e 's,@MACHINE@,${MACHINE},g' ${D}${bindir}/build-klipper-var-config install -d ${D}${sysconfdir}/klipper/config - install -m 0644 ${S}/default.conf ${D}${sysconfdir}/klipper/config/cosmos.conf + install -m 0644 ${S}/default-machine.conf ${D}${sysconfdir}/klipper/config/cosmos.conf install -d ${D}${datadir}/config-manager - install -m 0644 ${S}/default.conf ${D}${datadir}/config-manager/default.conf + install -m 0644 ${S}/default-machine.conf ${D}${datadir}/config-manager/default-machine.conf } FILES:${PN} = " \ ${bindir}/config-manager \ ${bindir}/build-klipper-var-config \ ${sysconfdir}/klipper/config/cosmos.conf \ - ${datadir}/config-manager/default.conf \ + ${datadir}/config-manager/default-machine.conf \ " RDEPENDS:${PN} = "python3-core" diff --git a/meta-opencentauri/recipes-data/config-manager/files/build-klipper-var-config.sh b/meta-opencentauri/recipes-data/config-manager/files/build-klipper-var-config.sh index 1a387074..c9e29c98 100644 --- a/meta-opencentauri/recipes-data/config-manager/files/build-klipper-var-config.sh +++ b/meta-opencentauri/recipes-data/config-manager/files/build-klipper-var-config.sh @@ -28,6 +28,7 @@ toolhead_led=$(get_config_value toolhead_led) elegoo_canvas=$("${CONFIG_MANAGER}" extras elegoo_canvas) check_for_updates=$("${CONFIG_MANAGER}" update check_for_updates) release=$("${CONFIG_MANAGER}" update release) +enclosed=$("${CONFIG_MANAGER}" extras enclosed) mkdir -p "${OUTPUT_DIR}" @@ -63,6 +64,10 @@ if [ "${elegoo_canvas}" = "True" ]; then echo "[include ../extras-readonly/canvas.cfg]" >> "${EXTRAS_OUTPUT_FILE}" fi +if [ "${enclosed}" = "True" ]; then + echo "[include ../extras-readonly/enclosed.cfg]" >> "${EXTRAS_OUTPUT_FILE}" +fi + cat > "${MOONRAKER_UPDATE_FILE}" <> "${MOONRAKER_UPDATE_FILE}" fi -fi - +fi \ No newline at end of file diff --git a/meta-opencentauri/recipes-data/config-manager/files/default.conf b/meta-opencentauri/recipes-data/config-manager/files/centauri-carbon-1/default-machine.conf similarity index 100% rename from meta-opencentauri/recipes-data/config-manager/files/default.conf rename to meta-opencentauri/recipes-data/config-manager/files/centauri-carbon-1/default-machine.conf diff --git a/meta-opencentauri/recipes-data/config-manager/files/centauri-carbon-2/default-machine.conf b/meta-opencentauri/recipes-data/config-manager/files/centauri-carbon-2/default-machine.conf new file mode 100644 index 00000000..6aede88a --- /dev/null +++ b/meta-opencentauri/recipes-data/config-manager/files/centauri-carbon-2/default-machine.conf @@ -0,0 +1,45 @@ +# OpenCentauri COSMOS settings. +# Changes to these settings require rebooting the entire machine to apply. +# Power cycle the machine or run the REBOOT_MACHINE macro after editing. + +[ui] +# User interface used on the built in screen. Options: grumpyscreen, none. +screen_ui = grumpyscreen +# User interface used for the webui. Options: mainsail, fluidd. +web_ui = mainsail +# Brightness of the built in screen. Range: 0-100. +screen_brightness = 100 + +[update] +# Update channel used by the COSMOS updater. Options: stable, nightly. +release = stable +# Check for updates automatically on startup. Options: True, False. +check_for_updates = True + +[extras] +# Enable support for the CANVAS unit. Also enables the AFC subsystem. +elegoo_canvas = False +# Enable the aux fan, exhaust fan and chamber light. Available only on the CC2 and CC2C. +enclosed = False + +[klipper] +# Allow printing before the initial calibration checks have passed. +bypass_calibration = False +# Minutes to wait after the bed reaches temperature before starting a print. Range: 0-30 minutes. +heatsoak = 1 +# Probe only the print area for each print instead of loading the default bed mesh. Requires nozzle_z_homing to be enabled. +adaptive_mesh = False +# Prime the nozzle near the print area before each print. +adaptive_purge = True +# Unload filament automatically when a print finishes. +unload_after_print = False +# Clean the nozzle automatically when a print finishes. +clean_nozzle_after_print = True +# Use the nozzle and load cell probe for Z homing. +nozzle_z_homing = False +# Drop the bed to this height in millimeters when a print finishes. Range: 0-256. +z_ideal_lifting_distance = 170 +# Hotend temperature in Celsius used during the full calibration workflow. Range: 200-300. +full_calibrate_hotend_temperature = 250 +# Bed temperature in Celsius used during the full calibration workflow. Range: 40-100. +full_calibrate_bed_temperature = 60 diff --git a/meta-opencentauri/recipes-data/config-manager/files/config_manager.py b/meta-opencentauri/recipes-data/config-manager/files/config_manager.py index 4c4c961c..6b180850 100644 --- a/meta-opencentauri/recipes-data/config-manager/files/config_manager.py +++ b/meta-opencentauri/recipes-data/config-manager/files/config_manager.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import os, configparser, sys +# The first valid value is the fallback when no configured default is present. VALIDATORS = { 'ui': { 'screen_ui': ['grumpyscreen', 'guppyscreen', 'atomscreen', 'none'], @@ -12,27 +13,28 @@ 'check_for_updates': ['True', 'False'], }, 'extras': { - 'elegoo_canvas': ['True', 'False'], + 'elegoo_canvas': ['False', 'True'], + 'enclosed': ['False', 'True'], }, 'klipper': { - 'sync_camera_led_to_chamber_led': ['True', 'False'], + 'sync_camera_led_to_chamber_led': ['False', 'True'], 'camera_led_default_on': ['True', 'False'], - 'bypass_calibration': ['True', 'False'], + 'bypass_calibration': ['False', 'True'], 'heatsoak': [str(x / 10) for x in range(100)] + [str(x) for x in range(31)], - 'adaptive_mesh': ['True', 'False'], + 'adaptive_mesh': ['False', 'True'], 'adaptive_purge': ['True', 'False'], - 'unload_after_print': ['True', 'False'], + 'unload_after_print': ['False', 'True'], 'clean_nozzle_after_print': ['True', 'False'], - 'nozzle_z_homing': ['True', 'False'], + 'nozzle_z_homing': ['False', 'True'], 'z_ideal_lifting_distance': [str(i) for i in range(257)], 'full_calibrate_hotend_temperature': [str(i) for i in range(200, 301)], 'full_calibrate_bed_temperature': [str(i) for i in range(30, 111)], - 'toolhead_led': ['True', 'False'], + 'toolhead_led': ['False', 'True'], }, } VARIABLE_CONFIG_PATH = '/etc/klipper/config/cosmos.conf' -DEFAULT_CONFIG_PATH = '/usr/share/config-manager/default.conf' +MACHINE_CONFIG_PATH = '/usr/share/config-manager/default-machine.conf' def validate_config(config : dict): for section, options in VALIDATORS.items(): @@ -147,9 +149,9 @@ def merge_configs(default_config : dict, user_config : dict) -> dict: return default_config def main(section : str, option : str, value : str|None = None): - default_config = load_config(DEFAULT_CONFIG_PATH) - default_comments = load_config_comments(DEFAULT_CONFIG_PATH) - default_header = load_config_header(DEFAULT_CONFIG_PATH) + machine_config = load_config(MACHINE_CONFIG_PATH) + machine_comments = load_config_comments(MACHINE_CONFIG_PATH) + machine_header = load_config_header(MACHINE_CONFIG_PATH) user_config = load_config(VARIABLE_CONFIG_PATH) if value is not None: if section not in user_config: @@ -157,15 +159,19 @@ def main(section : str, option : str, value : str|None = None): user_config[section][option] = value normalize_boolean_values(user_config) validate_config(user_config) - merged_config = merge_configs(default_config, user_config) + merged_config = merge_configs(machine_config, user_config) - if user_config != merged_config or config_needs_update(VARIABLE_CONFIG_PATH, merged_config, default_comments, default_header): - save_config(VARIABLE_CONFIG_PATH, merged_config, default_comments, default_header) + if user_config != merged_config or config_needs_update(VARIABLE_CONFIG_PATH, merged_config, machine_comments, machine_header): + save_config(VARIABLE_CONFIG_PATH, merged_config, machine_comments, machine_header) - if section not in merged_config or option not in merged_config[section]: - raise ValueError(f"Option '{option}' not found in section '{section}'") + if section in merged_config and option in merged_config[section]: + result = merged_config[section][option] + else: + if section not in VALIDATORS or option not in VALIDATORS[section]: + raise ValueError(f"Option '{option}' not found in section '{section}'") + result = VALIDATORS[section][option][0] - print(merged_config[section][option], end='') + print(result, end='') if __name__ == "__main__": if len(sys.argv) not in (3, 4): diff --git a/meta-opencentauri/recipes-data/klipper-afc-addon/afc_0.1.bb b/meta-opencentauri/recipes-data/klipper-afc-addon/afc_0.1.bb index 6f0acbe5..d3046ef5 100644 --- a/meta-opencentauri/recipes-data/klipper-afc-addon/afc_0.1.bb +++ b/meta-opencentauri/recipes-data/klipper-afc-addon/afc_0.1.bb @@ -9,7 +9,6 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/files:" SRC_URI = " \ git://github.com/suchmememanyskill/AFC-Klipper-Add-On.git;protocol=https;branch=DEV \ file://afc.cfg \ - file://canvas-toolhead.cfg \ file://canvas.cfg \ file://boxturtle.cfg \ file://canvas_rfid.cfg \ @@ -36,7 +35,7 @@ do_install() { # Install config files install -d ${D}${sysconfdir}/klipper/config/extras-readonly - install -m 0644 ${UNPACKDIR}/canvas.cfg ${UNPACKDIR}/canvas_rfid.cfg ${UNPACKDIR}/afc.cfg ${UNPACKDIR}/canvas-toolhead.cfg ${UNPACKDIR}/boxturtle.cfg ${D}${sysconfdir}/klipper/config/extras-readonly + install -m 0644 ${UNPACKDIR}/canvas.cfg ${UNPACKDIR}/canvas_rfid.cfg ${UNPACKDIR}/afc.cfg ${UNPACKDIR}/boxturtle.cfg ${D}${sysconfdir}/klipper/config/extras-readonly } FILES:${PN} = " \ @@ -76,7 +75,6 @@ FILES:${PN} = " \ ${datadir}/klipper/klippy/extras/AFC_EMU.py \ ${sysconfdir}/klipper/config/extras-readonly/afc.cfg \ ${sysconfdir}/klipper/config/extras-readonly/canvas.cfg \ - ${sysconfdir}/klipper/config/extras-readonly/canvas-toolhead.cfg \ ${sysconfdir}/klipper/config/extras-readonly/boxturtle.cfg \ ${sysconfdir}/klipper/config/extras-readonly/canvas_rfid.cfg \ -" \ No newline at end of file +" diff --git a/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-1/canvas.cfg b/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-1/canvas.cfg index 92fd423d..b56232a1 100644 --- a/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-1/canvas.cfg +++ b/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-1/canvas.cfg @@ -192,3 +192,15 @@ sensor_mcu: canvas [pwm_cycle_time _buzzer_canvas] pin: canvas:PB13 + +[gcode_button toolhead_tangle_detection] +pin: !hotend:PB0 +debounce_delay: 0.5 +press_gcode: + RESPOND TYPE=echo MSG="Filament tangle detected!" + + {% if printer.print_stats.state == "printing" %} + PAUSE + {% endif %} +release_gcode: + RESPOND TYPE=echo MSG="Filament tangle has been resolved" \ No newline at end of file diff --git a/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-2/canvas-toolhead.cfg b/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-2/canvas-toolhead.cfg deleted file mode 100644 index 77829c0c..00000000 --- a/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-2/canvas-toolhead.cfg +++ /dev/null @@ -1,11 +0,0 @@ -[gcode_button toolhead_tangle_detection] -pin: !hotend:PA1 -debounce_delay: 0.5 -press_gcode: - RESPOND TYPE=echo MSG="Filament tangle detected!" - - {% if printer.print_stats.state == "printing" %} - PAUSE - {% endif %} -release_gcode: - RESPOND TYPE=echo MSG="Filament tangle has been resolved" diff --git a/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-2/canvas.cfg b/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-2/canvas.cfg index 2607ff10..c10c1a42 100644 --- a/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-2/canvas.cfg +++ b/meta-opencentauri/recipes-data/klipper-afc-addon/files/centauri-carbon-2/canvas.cfg @@ -4,7 +4,6 @@ # ----- [include afc.cfg] -[include canvas-toolhead.cfg] [gcode_macro _AFC_GLOBAL_VARS] variable_stepper_name: 'CANVAS_' @@ -189,3 +188,15 @@ clear_spool_after_eject: False [pwm_cycle_time _buzzer_canvas] pin: canvas:PC15 + +[gcode_button toolhead_tangle_detection] +pin: !hotend:PA1 +debounce_delay: 0.5 +press_gcode: + RESPOND TYPE=echo MSG="Filament tangle detected!" + + {% if printer.print_stats.state == "printing" %} + PAUSE + {% endif %} +release_gcode: + RESPOND TYPE=echo MSG="Filament tangle has been resolved" \ No newline at end of file