From a629b87ae07fdea3c8aeaec3c01fc3a95e49adff Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 30 Oct 2023 14:04:08 -0400 Subject: [PATCH 01/53] config: Split generic-bigtreetech-octopus.cfg into separate files Use two different files for the Octopus Pro and Octopus (non-Pro). The configs are the same in practice, but the difference in version and naming can lead to confusion. Signed-off-by: Kevin O'Connor --- .../generic-bigtreetech-octopus-pro-v1.0.cfg | 289 ++++++++++++++++++ ...g => generic-bigtreetech-octopus-v1.1.cfg} | 20 +- test/klippy/printers.test | 6 +- 3 files changed, 306 insertions(+), 9 deletions(-) create mode 100644 config/generic-bigtreetech-octopus-pro-v1.0.cfg rename config/{generic-bigtreetech-octopus.cfg => generic-bigtreetech-octopus-v1.1.cfg} (89%) diff --git a/config/generic-bigtreetech-octopus-pro-v1.0.cfg b/config/generic-bigtreetech-octopus-pro-v1.0.cfg new file mode 100644 index 000000000..7a5eeafb9 --- /dev/null +++ b/config/generic-bigtreetech-octopus-pro-v1.0.cfg @@ -0,0 +1,289 @@ +# This file contains common pin mappings for the BigTreeTech Octopus +# Pro v1.0 board. + +# Important! Do not use this config with an Octopus Pro v1.1 board as +# doing so could result in a heater being inadvertently enabled. + +# To use this config, start by identifying the micro-controller on the +# board - it may be an STM32F446, STM32F429, or an STM32H723. Select +# the appropriate micro-controller in "make menuconfig" and select +# "Enable low-level configuration options". For STM32F446 boards the +# firmware should be compiled with a "32KiB bootloader" and a "12MHz +# crystal" clock reference. For STM32F429 boards use a "32KiB +# bootloader" and an "8MHz crystal". For STM32H723 boards use a +# "128KiB bootloader" and a "25Mhz crystal". + +# See docs/Config_Reference.md for a description of parameters. + +# Driver0 +[stepper_x] +step_pin: PF13 +dir_pin: PF12 +enable_pin: !PF14 +microsteps: 16 +rotation_distance: 40 +endstop_pin: PG6 +position_endstop: 0 +position_max: 200 +homing_speed: 50 + +# Driver1 +[stepper_y] +step_pin: PG0 +dir_pin: PG1 +enable_pin: !PF15 +microsteps: 16 +rotation_distance: 40 +endstop_pin: PG9 +position_endstop: 0 +position_max: 200 +homing_speed: 50 + +# Driver2 +[stepper_z] +step_pin: PF11 +dir_pin: PG3 +enable_pin: !PG5 +microsteps: 16 +rotation_distance: 8 +endstop_pin: PG10 +position_endstop: 0.5 +position_max: 200 + +# Driver3 +# The Octopus only has 4 heater outputs which leaves an extra stepper +# This can be used for a second Z stepper, dual_carriage, extruder co-stepper, +# or other accesory such as an MMU +#[stepper_] +#step_pin: PG4 +#dir_pin: PC1 +#enable_pin: PA0 +#endstop_pin: PG11 +#... + +# Driver4 +[extruder] +step_pin: PF9 +dir_pin: PF10 +enable_pin: !PG2 +microsteps: 16 +rotation_distance: 33.500 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +heater_pin: PA2 # HE0 +sensor_pin: PF4 # T0 +sensor_type: EPCOS 100K B57560G104F +control: pid +pid_Kp: 22.2 +pid_Ki: 1.08 +pid_Kd: 114 +min_temp: 0 +max_temp: 250 + +#[filament_switch_sensor material_0] +#switch_pin: PG12 + +# Driver5 +#[extruder1] +#step_pin: PC13 +#dir_pin: PF0 +#enable_pin: !PF1 +#heater_pin: PA3 # HE1 +#sensor_pin: PF5 # T1 +#... + +#[filament_switch_sensor material_1] +#switch_pin: PG13 + +# Driver6 +#[extruder2] +#step_pin: PE2 +#dir_pin: PE3 +#enable_pin: !PD4 +#heater_pin: PB10 # HE2 +#sensor_pin: PF6 # T2 +#... + +#[filament_switch_sensor material_2] +#switch_pin: PG14 + +# Driver7 +#[extruder3] +#step_pin: PE6 +#dir_pin: PA14 +#enable_pin: !PE0 +#heater_pin: PB11 # HE3 +#sensor_pin: PF7 # T3 +#... + +#[filament_switch_sensor material_3] +#switch_pin: PG15 + +[heater_bed] +heater_pin: PA1 +sensor_pin: PF3 # TB +sensor_type: ATC Semitec 104GT-2 +control: watermark +min_temp: 0 +max_temp: 130 + +[fan] +pin: PA8 + +#[heater_fan fan1] +#pin: PE5 + +#[heater_fan fan2] +#pin: PD12 + +#[heater_fan fan3] +#pin: PD13 + +#[heater_fan fan4] +#pin: PD14 + +#[controller_fan fan5] +#pin: PD15 + +[mcu] +serial: /dev/serial/by-id/usb-Klipper_Klipper_firmware_12345-if00 +# CAN bus is also available on this board + +[printer] +kinematics: cartesian +max_velocity: 300 +max_accel: 3000 +max_z_velocity: 5 +max_z_accel: 100 + +######################################## +# TMC2209 configuration +######################################## + +#[tmc2209 stepper_x] +#uart_pin: PC4 +##diag_pin: PG6 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2209 stepper_y] +#uart_pin: PD11 +##diag_pin: PG9 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2209 stepper_z] +#uart_pin: PC6 +##diag_pin: PG10 +#run_current: 0.650 +#stealthchop_threshold: 999999 + +#[tmc2209 stepper_] +#uart_pin: PC7 +##diag_pin: PG11 +#run_current: 0.650 +#stealthchop_threshold: 999999 + +#[tmc2209 extruder] +#uart_pin: PF2 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2209 extruder1] +#uart_pin: PE4 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2209 extruder2] +#uart_pin: PE1 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2209 extruder3] +#uart_pin: PD3 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +######################################## +# TMC2130 configuration +######################################## + +#[tmc2130 stepper_x] +#cs_pin: PC4 +#spi_bus: spi1 +##diag1_pin: PG6 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 stepper_y] +#cs_pin: PD11 +#spi_bus: spi1 +##diag1_pin: PG9 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 stepper_z] +#cs_pin: PC6 +#spi_bus: spi1 +##diag1_pin: PG10 +#run_current: 0.650 +#stealthchop_threshold: 999999 + +#[tmc2130 stepper_] +#cs_pin: PC7 +#spi_bus: spi1 +##diag1_pin: PG11 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 extruder] +#cs_pin: PF2 +#spi_bus: spi1 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 extruder1] +#cs_pin: PE4 +#spi_bus: spi1 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 extruder2] +#cs_pin: PE1 +#spi_bus: spi1 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 extruder3] +#cs_pin: PD3 +#spi_bus: spi1 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +[board_pins] +aliases: + # EXP1 header + EXP1_1=PE8, EXP1_2=PE7, + EXP1_3=PE9, EXP1_4=PE10, + EXP1_5=PE12, EXP1_6=PE13, # Slot in the socket on this side + EXP1_7=PE14, EXP1_8=PE15, + EXP1_9=, EXP1_10=<5V>, + + # EXP2 header + EXP2_1=PA6, EXP2_2=PA5, + EXP2_3=PB1, EXP2_4=PA4, + EXP2_5=PB2, EXP2_6=PA7, # Slot in the socket on this side + EXP2_7=PC15, EXP2_8=, + EXP2_9=, EXP2_10=PC5 + +# See the sample-lcd.cfg file for definitions of common LCD displays. + +# A [probe] section can be defined instead with a pin: setting identical +# to the sensor_pin: for a bltouch +#[bltouch] +#sensor_pin: PB7 +#control_pin: PB6 +#z_offset: 0 + +#[neopixel my_neopixel] +#pin: PB0 diff --git a/config/generic-bigtreetech-octopus.cfg b/config/generic-bigtreetech-octopus-v1.1.cfg similarity index 89% rename from config/generic-bigtreetech-octopus.cfg rename to config/generic-bigtreetech-octopus-v1.1.cfg index 73bd584ca..d90f6f661 100644 --- a/config/generic-bigtreetech-octopus.cfg +++ b/config/generic-bigtreetech-octopus-v1.1.cfg @@ -1,12 +1,16 @@ # This file contains common pin mappings for the BigTreeTech Octopus -# and Octopus Pro boards. To use this config, start by identifying the -# micro-controller on the board - it may be an STM32F446, STM32F429, -# or an STM32H723. Select the appropriate micro-controller in "make -# menuconfig" and select "Enable low-level configuration options". For -# STM32F446 boards the firmware should be compiled with a "32KiB -# bootloader" and a "12MHz crystal" clock reference. For STM32F429 -# boards use a "32KiB bootloader" and an "8MHz crystal". For STM32H723 -# boards use a "128KiB bootloader" and a "25Mhz crystal". +# (non-Pro) boards. + +# Important! Do not use this config with an Octopus Pro v1.1 board as +# doing so could result in a heater being inadvertently enabled. + +# To use this config, start by identifying the micro-controller on the +# board - it may be an STM32F446, or STM32F429. Select the +# appropriate micro-controller in "make menuconfig" and select "Enable +# low-level configuration options". For STM32F446 boards the firmware +# should be compiled with a "32KiB bootloader" and a "12MHz crystal" +# clock reference. For STM32F429 boards use a "32KiB bootloader" and +# an "8MHz crystal". # See docs/Config_Reference.md for a description of parameters. diff --git a/test/klippy/printers.test b/test/klippy/printers.test index 8038ad4c0..ddb94ed2b 100644 --- a/test/klippy/printers.test +++ b/test/klippy/printers.test @@ -227,9 +227,13 @@ CONFIG ../../config/generic-th3d-ezboard-v2.0.cfg CONFIG ../../config/printer-biqu-b1-se-plus-2022.cfg CONFIG ../../config/printer-prusa-mini-plus-2020.cfg +# Printers using the stm32f429 +DICTIONARY stm32f429.dict +CONFIG ../../config/generic-bigtreetech-octopus-v1.1.cfg + # Printers using the stm32f446 DICTIONARY stm32f446.dict -CONFIG ../../config/generic-bigtreetech-octopus.cfg +CONFIG ../../config/generic-bigtreetech-octopus-pro-v1.0.cfg CONFIG ../../config/generic-fysetc-s6.cfg CONFIG ../../config/generic-fysetc-s6-v2.cfg CONFIG ../../config/generic-fysetc-spider.cfg From 9b1529354799c01912cd48d2c905e2afa8489fde Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 30 Oct 2023 14:09:00 -0400 Subject: [PATCH 02/53] config: Clarify stepper enable comment in Octopus boards Make sure the stepper enable_pin comment shows an inverted pin. Signed-off-by: Kevin O'Connor --- config/generic-bigtreetech-octopus-pro-v1.0.cfg | 2 +- config/generic-bigtreetech-octopus-v1.1.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/generic-bigtreetech-octopus-pro-v1.0.cfg b/config/generic-bigtreetech-octopus-pro-v1.0.cfg index 7a5eeafb9..109257f65 100644 --- a/config/generic-bigtreetech-octopus-pro-v1.0.cfg +++ b/config/generic-bigtreetech-octopus-pro-v1.0.cfg @@ -57,7 +57,7 @@ position_max: 200 #[stepper_] #step_pin: PG4 #dir_pin: PC1 -#enable_pin: PA0 +#enable_pin: !PA0 #endstop_pin: PG11 #... diff --git a/config/generic-bigtreetech-octopus-v1.1.cfg b/config/generic-bigtreetech-octopus-v1.1.cfg index d90f6f661..f3a2b87c3 100644 --- a/config/generic-bigtreetech-octopus-v1.1.cfg +++ b/config/generic-bigtreetech-octopus-v1.1.cfg @@ -56,7 +56,7 @@ position_max: 200 #[stepper_] #step_pin: PG4 #dir_pin: PC1 -#enable_pin: PA0 +#enable_pin: !PA0 #endstop_pin: PG11 #... From bee7ec720b1405e8a14ca1fc81e565f0e5ce7aa9 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 30 Oct 2023 14:14:20 -0400 Subject: [PATCH 03/53] config: Add new generic-bigtreetech-octopus-pro-v1.1.cfg config Signed-off-by: Kevin O'Connor --- .../generic-bigtreetech-octopus-pro-v1.1.cfg | 285 ++++++++++++++++++ test/klippy/printers.test | 1 + 2 files changed, 286 insertions(+) create mode 100644 config/generic-bigtreetech-octopus-pro-v1.1.cfg diff --git a/config/generic-bigtreetech-octopus-pro-v1.1.cfg b/config/generic-bigtreetech-octopus-pro-v1.1.cfg new file mode 100644 index 000000000..920ca9f38 --- /dev/null +++ b/config/generic-bigtreetech-octopus-pro-v1.1.cfg @@ -0,0 +1,285 @@ +# This file contains common pin mappings for the BigTreeTech Octopus +# Pro v1.1 board. + +# Important! Do not use this config with an Octopus Pro v1.0 board nor +# non-Pro board. + +# To use this config, during "make menuconfig", select "Enable +# low-level configuration options", select the STM32H723 +# micro-controller, select a "128KiB bootloader", and select a "25Mhz +# crystal". + +# See docs/Config_Reference.md for a description of parameters. + +# Driver0 +[stepper_x] +step_pin: PF13 +dir_pin: PF12 +enable_pin: !PF14 +microsteps: 16 +rotation_distance: 40 +endstop_pin: PG6 +position_endstop: 0 +position_max: 200 +homing_speed: 50 + +# Driver1 +[stepper_y] +step_pin: PG0 +dir_pin: PG1 +enable_pin: !PF15 +microsteps: 16 +rotation_distance: 40 +endstop_pin: PG9 +position_endstop: 0 +position_max: 200 +homing_speed: 50 + +# Driver2 +[stepper_z] +step_pin: PF11 +dir_pin: PG3 +enable_pin: !PG5 +microsteps: 16 +rotation_distance: 8 +endstop_pin: PG10 +position_endstop: 0.5 +position_max: 200 + +# Driver3 +# The Octopus only has 4 heater outputs which leaves an extra stepper +# This can be used for a second Z stepper, dual_carriage, extruder co-stepper, +# or other accesory such as an MMU +#[stepper_] +#step_pin: PG4 +#dir_pin: PC1 +#enable_pin: !PA2 +#endstop_pin: PG11 +#... + +# Driver4 +[extruder] +step_pin: PF9 +dir_pin: PF10 +enable_pin: !PG2 +microsteps: 16 +rotation_distance: 33.500 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +heater_pin: PA0 # HE0 +sensor_pin: PF4 # T0 +sensor_type: EPCOS 100K B57560G104F +control: pid +pid_Kp: 22.2 +pid_Ki: 1.08 +pid_Kd: 114 +min_temp: 0 +max_temp: 250 + +#[filament_switch_sensor material_0] +#switch_pin: PG12 + +# Driver5 +#[extruder1] +#step_pin: PC13 +#dir_pin: PF0 +#enable_pin: !PF1 +#heater_pin: PA3 # HE1 +#sensor_pin: PF5 # T1 +#... + +#[filament_switch_sensor material_1] +#switch_pin: PG13 + +# Driver6 +#[extruder2] +#step_pin: PE2 +#dir_pin: PE3 +#enable_pin: !PD4 +#heater_pin: PB0 # HE2 +#sensor_pin: PF6 # T2 +#... + +#[filament_switch_sensor material_2] +#switch_pin: PG14 + +# Driver7 +#[extruder3] +#step_pin: PE6 +#dir_pin: PA14 +#enable_pin: !PE0 +#heater_pin: PB11 # HE3 +#sensor_pin: PF7 # T3 +#... + +#[filament_switch_sensor material_3] +#switch_pin: PG15 + +[heater_bed] +heater_pin: PA1 +sensor_pin: PF3 # TB +sensor_type: ATC Semitec 104GT-2 +control: watermark +min_temp: 0 +max_temp: 130 + +[fan] +pin: PA8 + +#[heater_fan fan1] +#pin: PE5 + +#[heater_fan fan2] +#pin: PD12 + +#[heater_fan fan3] +#pin: PD13 + +#[heater_fan fan4] +#pin: PD14 + +#[controller_fan fan5] +#pin: PD15 + +[mcu] +serial: /dev/serial/by-id/usb-Klipper_Klipper_firmware_12345-if00 +# CAN bus is also available on this board + +[printer] +kinematics: cartesian +max_velocity: 300 +max_accel: 3000 +max_z_velocity: 5 +max_z_accel: 100 + +######################################## +# TMC2209 configuration +######################################## + +#[tmc2209 stepper_x] +#uart_pin: PC4 +##diag_pin: PG6 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2209 stepper_y] +#uart_pin: PD11 +##diag_pin: PG9 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2209 stepper_z] +#uart_pin: PC6 +##diag_pin: PG10 +#run_current: 0.650 +#stealthchop_threshold: 999999 + +#[tmc2209 stepper_] +#uart_pin: PC7 +##diag_pin: PG11 +#run_current: 0.650 +#stealthchop_threshold: 999999 + +#[tmc2209 extruder] +#uart_pin: PF2 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2209 extruder1] +#uart_pin: PE4 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2209 extruder2] +#uart_pin: PE1 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2209 extruder3] +#uart_pin: PD3 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +######################################## +# TMC2130 configuration +######################################## + +#[tmc2130 stepper_x] +#cs_pin: PC4 +#spi_bus: spi1 +##diag1_pin: PG6 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 stepper_y] +#cs_pin: PD11 +#spi_bus: spi1 +##diag1_pin: PG9 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 stepper_z] +#cs_pin: PC6 +#spi_bus: spi1 +##diag1_pin: PG10 +#run_current: 0.650 +#stealthchop_threshold: 999999 + +#[tmc2130 stepper_] +#cs_pin: PC7 +#spi_bus: spi1 +##diag1_pin: PG11 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 extruder] +#cs_pin: PF2 +#spi_bus: spi1 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 extruder1] +#cs_pin: PE4 +#spi_bus: spi1 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 extruder2] +#cs_pin: PE1 +#spi_bus: spi1 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +#[tmc2130 extruder3] +#cs_pin: PD3 +#spi_bus: spi1 +#run_current: 0.800 +#stealthchop_threshold: 999999 + +[board_pins] +aliases: + # EXP1 header + EXP1_1=PE8, EXP1_2=PE7, + EXP1_3=PE9, EXP1_4=PE10, + EXP1_5=PE12, EXP1_6=PE13, # Slot in the socket on this side + EXP1_7=PE14, EXP1_8=PE15, + EXP1_9=, EXP1_10=<5V>, + + # EXP2 header + EXP2_1=PA6, EXP2_2=PA5, + EXP2_3=PB1, EXP2_4=PA4, + EXP2_5=PB2, EXP2_6=PA7, # Slot in the socket on this side + EXP2_7=PC15, EXP2_8=, + EXP2_9=, EXP2_10=PC5 + +# See the sample-lcd.cfg file for definitions of common LCD displays. + +# A [probe] section can be defined instead with a pin: setting identical +# to the sensor_pin: for a bltouch +#[bltouch] +#sensor_pin: PB7 +#control_pin: PB6 +#z_offset: 0 + +#[neopixel my_neopixel] +#pin: PB10 diff --git a/test/klippy/printers.test b/test/klippy/printers.test index ddb94ed2b..d9168adc5 100644 --- a/test/klippy/printers.test +++ b/test/klippy/printers.test @@ -243,6 +243,7 @@ CONFIG ../../config/printer-ratrig-v-minion-2021.cfg # Printers using the stm32h723 DICTIONARY stm32h723.dict CONFIG ../../config/generic-bigtreetech-octopus-max-ez.cfg +CONFIG ../../config/generic-bigtreetech-octopus-pro-v1.1.cfg # Printers using the stm32h743 DICTIONARY stm32h743.dict From 03982f2c7ecae3fee2e187f1d235b28f373d8885 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 30 Oct 2023 16:32:49 -0400 Subject: [PATCH 04/53] docs: Update the Contact document Try to make it more clear that Discord is not Discourse, as the two similarly sounding services are often confused by users unfamiliar with them. The Klipper Discourse and Klipper Discord are available for many topics related to Klipper - reword some sections to make their use more open. Avoid referring to the Klipper github repo in this Contact document as we no longer use Klipper github issues at all. Remove the table of contents as it is largely redundant for the document. Signed-off-by: Kevin O'Connor --- .github/workflows/reviewer-needed-label.yaml | 4 +- .github/workflows/stale-issue-bot.yaml | 6 +- docs/Contact.md | 68 ++++++-------------- docs/SDCard_Updates.md | 2 +- 4 files changed, 26 insertions(+), 54 deletions(-) diff --git a/.github/workflows/reviewer-needed-label.yaml b/.github/workflows/reviewer-needed-label.yaml index c4603b300..61b72dc98 100644 --- a/.github/workflows/reviewer-needed-label.yaml +++ b/.github/workflows/reviewer-needed-label.yaml @@ -34,7 +34,7 @@ jobs: + " bulk of a review has already been completed." + "\n" + "2. Consider opening a topic on the [Klipper Discourse]" - + "(https://www.klipper3d.org/Contact.html#community-forum)" + + "(https://www.klipper3d.org/Contact.html#discourse-forum)" + " server to discuss this work. The Discourse server is a good" + " place to discuss development ideas and to engage users" + " interested in testing. Reviewers are more likely to" @@ -50,7 +50,7 @@ jobs: + " this GitHub Pull Request then it will be automatically" + " closed. If this happens, then it is a good idea to move" + " further discussion to the [Klipper Discourse]" - + "(https://www.klipper3d.org/Contact.html#community-forum)" + + "(https://www.klipper3d.org/Contact.html#discourse-forum)" + " server. Reviewers can reach out on that forum to let you" + " know if they are interested and when they are available." + "\n\n" diff --git a/.github/workflows/stale-issue-bot.yaml b/.github/workflows/stale-issue-bot.yaml index 3ada94d61..2f05abc1f 100644 --- a/.github/workflows/stale-issue-bot.yaml +++ b/.github/workflows/stale-issue-bot.yaml @@ -83,7 +83,7 @@ jobs: + " this GitHub Pull Request and it is therefore being" + " closed. It is a good idea to move" + " further discussion to the [Klipper Discourse]" - + "(https://www.klipper3d.org/Contact.html#community-forum)" + + "(https://www.klipper3d.org/Contact.html#discourse-forum)" + " server. Reviewers can reach out on that forum to let you" + " know if they are interested and when they are available." + "\n\n" @@ -137,7 +137,7 @@ jobs: + " bulk of a review has already been completed." + "\n" + "2. Consider opening a topic on the [Klipper Discourse]" - + "(https://www.klipper3d.org/Contact.html#community-forum)" + + "(https://www.klipper3d.org/Contact.html#discourse-forum)" + " server to discuss this work. The Discourse server is a good" + " place to discuss development ideas and to engage users" + " interested in testing. Reviewers are more likely to" @@ -153,7 +153,7 @@ jobs: + " this GitHub Pull Request then it will be automatically" + " closed. If this happens, then it is a good idea to move" + " further discussion to the [Klipper Discourse]" - + "(https://www.klipper3d.org/Contact.html#community-forum)" + + "(https://www.klipper3d.org/Contact.html#discourse-forum)" + " server. Reviewers can reach out on that forum to let you" + " know if they are interested and when they are available." + "\n\n" diff --git a/docs/Contact.md b/docs/Contact.md index d6ffc08dc..26894eb11 100644 --- a/docs/Contact.md +++ b/docs/Contact.md @@ -2,25 +2,18 @@ This document provides contact information for Klipper. -1. [Community Forum](#community-forum) -2. [Discord Chat](#discord-chat) -3. [I have a question about Klipper](#i-have-a-question-about-klipper) -4. [I have a feature request](#i-have-a-feature-request) -5. [Help! It doesn't work!](#help-it-doesnt-work) -6. [I found a bug in the Klipper software](#i-found-a-bug-in-the-klipper-software) -7. [I am making changes that I'd like to include in Klipper](#i-am-making-changes-that-id-like-to-include-in-klipper) -8. [Klipper github](#klipper-github) - -## Community Forum +## Discourse Forum There is a [Klipper Community Discourse server](https://community.klipper3d.org) -for discussions on Klipper. +for "forum" style discussions on Klipper. Note that Discourse is not +Discord. ## Discord Chat There is a Discord server dedicated to Klipper at: -[https://discord.klipper3d.org](https://discord.klipper3d.org). +[https://discord.klipper3d.org](https://discord.klipper3d.org). Note +that Discord is not Discourse. This server is run by a community of Klipper enthusiasts dedicated to discussions on Klipper. It allows users to chat with other users in @@ -33,37 +26,29 @@ Many questions we receive are already answered in the documentation and follow the directions provided there. It is also possible to search for similar questions in the -[Klipper Community Forum](#community-forum). +[Klipper Discourse Forum](#discourse-forum). If you are interested in sharing your knowledge and experience with other Klipper users then you can join the -[Klipper Community Forum](#community-forum) or +[Klipper Discourse Forum](#discourse-forum) or [Klipper Discord Chat](#discord-chat). Both are communities where Klipper users can discuss Klipper with other users. -Many questions we receive are general 3d-printing questions that are -not specific to Klipper. If you have a general question or are -experiencing general printing problems, then you will likely get a -better response by asking in a general 3d-printing forum or a forum -dedicated to your printer hardware. +If you have a general question or are experiencing general printing +problems, then also consider a general 3d-printing forum or a forum +dedicated to the printer hardware. ## I have a feature request All new features require someone interested and able to implement that feature. If you are interested in helping to implement or test a new feature, you can search for ongoing developments in the -[Klipper Community Forum](#community-forum). There is also +[Klipper Discourse Forum](#discourse-forum). There is also [Klipper Discord Chat](#discord-chat) for discussions between collaborators. ## Help! It doesn't work! -Unfortunately, we receive many more requests for help than we could -possibly answer. Most problem reports we see are eventually tracked -down to: -1. Subtle errors in the hardware, or -2. Not following all the steps described in the Klipper documentation. - If you are experiencing problems we recommend you carefully read the [Klipper documentation](Overview.md) and double check that all steps were followed. @@ -72,16 +57,15 @@ If you are experiencing a printing problem, then we recommend carefully inspecting the printer hardware (all joints, wires, screws, etc.) and verify nothing is abnormal. We find most printing problems are not related to the Klipper software. If you do find a problem with -the printer hardware then you will likely get a better response by -searching in a general 3d-printing forum or in a forum dedicated to -your printer hardware. +the printer hardware then consider searching general 3d-printing +forums or forums dedicated to the printer hardware. It is also possible to search for similar issues in the -[Klipper Community Forum](#community-forum). +[Klipper Discourse Forum](#discourse-forum). If you are interested in sharing your knowledge and experience with other Klipper users then you can join the -[Klipper Community Forum](#community-forum) or +[Klipper Discourse Forum](#discourse-forum) or [Klipper Discord Chat](#discord-chat). Both are communities where Klipper users can discuss Klipper with other users. @@ -91,7 +75,7 @@ Klipper is an open-source project and we appreciate when collaborators diagnose errors in the software. Problems should be reported in the -[Klipper Community Forum](#community-forum). +[Klipper Discourse Forum](#discourse-forum). There is important information that will be needed in order to fix a bug. Please follow these steps: @@ -126,7 +110,7 @@ bug. Please follow these steps: of the log. Only the full unmodified log file provides the necessary information. 5. It is a good idea to compress the log file with zip or gzip. -5. Open a new topic on the [Klipper Community Forum](#community-forum) +5. Open a new topic on the [Klipper Discourse Forum](#discourse-forum) and provide a clear description of the problem. Other Klipper contributors will need to understand what steps were taken, what the desired outcome was, and what outcome actually occurred. The @@ -136,22 +120,10 @@ bug. Please follow these steps: Klipper is open-source software and we appreciate new contributions. -New contributions (for both code and documentation) are submitted via -Github Pull Requests. See the [CONTRIBUTING document](CONTRIBUTING.md) -for important information. +See the [CONTRIBUTING document](CONTRIBUTING.md) for information. There are several [documents for developers](Overview.md#developer-documentation). If you have questions on the code then you can also ask in the -[Klipper Community Forum](#community-forum) or on the -[Klipper Community Discord](#discord-chat). - -## Klipper github - -Klipper github may be used by contributors to share the status of -their work to improve Klipper. It is expected that the person opening -a github ticket is actively working on the given task and will be the -one performing all the work necessary to accomplish it. The Klipper -github is not used for requests, nor to report bugs, nor to ask -questions. Use the [Klipper Community Forum](#community-forum) or the -[Klipper Community Discord](#discord-chat) instead. +[Klipper Discourse Forum](#discourse-forum) or on the +[Klipper Discord Chat](#discord-chat). diff --git a/docs/SDCard_Updates.md b/docs/SDCard_Updates.md index 77a586157..432446de7 100644 --- a/docs/SDCard_Updates.md +++ b/docs/SDCard_Updates.md @@ -170,7 +170,7 @@ BOARD_ALIASES = { If you need a new board definition and you are uncomfortable with the procedure outlined above it is recommended that you request one in -the [Klipper Community Discord](Contact.md#discord). +the [Klipper Discord](Contact.md). ## Flashing Boards that use SDIO From 64a97431b5091d97b06cb125c30213519495d84b Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 30 Oct 2023 18:18:17 -0400 Subject: [PATCH 05/53] docs: Update Contact.md to note other common ways to obtain Klipper log file Signed-off-by: Kevin O'Connor --- docs/Contact.md | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/docs/Contact.md b/docs/Contact.md index 26894eb11..714c36bd4 100644 --- a/docs/Contact.md +++ b/docs/Contact.md @@ -94,22 +94,27 @@ bug. Please follow these steps: about the software and its environment (software version, hardware type, configuration, event timing, and hundreds of other questions). - 1. The Klipper log file is located in `/tmp/klippy.log` on the - Klipper "host" computer (the Raspberry Pi). - 2. An "scp" or "sftp" utility is needed to copy this log file to - your desktop computer. The "scp" utility comes standard with - Linux and MacOS desktops. There are freely available scp - utilities for other desktops (eg, WinSCP). If using a graphical - scp utility that can not directly copy `/tmp/klippy.log` then - repeatedly click on `..` or `parent folder` until you get to the - root directory, click on the `tmp` folder, and then select the - `klippy.log` file. - 3. Copy the log file to your desktop so that it can be attached to + 1. Dedicated Klipper web interfaces have the ability to directly + obtain the Klipper log file. It's the easiest way to obtain the + log when using one of these interfaces. Otherwise, an "scp" or + "sftp" utility is needed to copy the log file to your desktop + computer. The "scp" utility comes standard with Linux and MacOS + desktops. There are freely available scp utilities for other + desktops (eg, WinSCP). The log file may be located in the + `~/printer_data/logs/klippy.log` file (if using a graphical scp + utility, look for the "printer_data" folder, then the "logs" + folder under that, then the `klippy.log` file). The log file may + alternatively be located in the `/tmp/klippy.log` file (if using + a graphical scp utility that can not directly copy + `/tmp/klippy.log` then repeatedly click on `..` or + "parent folder" until reaching the root directory, click on + the `tmp` folder, and then select the `klippy.log` file). + 2. Copy the log file to your desktop so that it can be attached to an issue report. - 4. Do not modify the log file in any way; do not provide a snippet + 3. Do not modify the log file in any way; do not provide a snippet of the log. Only the full unmodified log file provides the necessary information. - 5. It is a good idea to compress the log file with zip or gzip. + 4. It is a good idea to compress the log file with zip or gzip. 5. Open a new topic on the [Klipper Discourse Forum](#discourse-forum) and provide a clear description of the problem. Other Klipper contributors will need to understand what steps were taken, what From 233f50424cc2523814edaa2ce3521e86876b4ba7 Mon Sep 17 00:00:00 2001 From: Wayne Manion Date: Thu, 2 Nov 2023 12:03:01 -0500 Subject: [PATCH 06/53] config: Update generic-bigtreetech-skr-pico-v1.0.cfg (#6376) Add RP2040 internal temperature sensor Signed-off-by: Wayne Manion --- config/generic-bigtreetech-skr-pico-v1.0.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/generic-bigtreetech-skr-pico-v1.0.cfg b/config/generic-bigtreetech-skr-pico-v1.0.cfg index 06a0c9f7a..4dd750f5c 100644 --- a/config/generic-bigtreetech-skr-pico-v1.0.cfg +++ b/config/generic-bigtreetech-skr-pico-v1.0.cfg @@ -106,6 +106,9 @@ pin: gpio18 [heater_fan controller_fan] pin: gpio20 +[temperature_sensor pico] +sensor_type: temperature_mcu + [mcu] serial: /dev/serial/by-id/usb-Klipper_Klipper_firmware_12345-if00 From 9e765daeedb2adf7641b96882326b80aeeb70c93 Mon Sep 17 00:00:00 2001 From: Pedro Lamas Date: Wed, 1 Nov 2023 01:14:44 +0000 Subject: [PATCH 07/53] bed_screws: Reset on move error Signed-off-by: Pedro Lamas --- klippy/extras/bed_screws.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/klippy/extras/bed_screws.py b/klippy/extras/bed_screws.py index 4749d4e10..c34461919 100644 --- a/klippy/extras/bed_screws.py +++ b/klippy/extras/bed_screws.py @@ -42,7 +42,12 @@ def reset(self): self.current_screw = 0 self.accepted_screws = 0 def move(self, coord, speed): - self.printer.lookup_object('toolhead').manual_move(coord, speed) + try: + self.printer.lookup_object('toolhead').manual_move(coord, speed) + except self.printer.command_error as e: + self.unregister_commands() + self.reset() + raise def move_to_screw(self, state, screw): # Move up, over, and then down self.move((None, None, self.horizontal_move_z), self.lift_speed) From 0acf3035e8193d69ebe1bccb9906852500987b35 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Fri, 10 Nov 2023 18:23:35 -0500 Subject: [PATCH 08/53] docs: Update Features.md Note additional features that have been added recently. Signed-off-by: Kevin O'Connor --- docs/Features.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/docs/Features.md b/docs/Features.md index 96b17a756..9c61ccf72 100644 --- a/docs/Features.md +++ b/docs/Features.md @@ -60,7 +60,7 @@ Klipper has several compelling features: if it is unable to). This makes it easier to use available hardware, to upgrade to new hardware, and to have confidence in the hardware. -* Portable code. Klipper works on ARM, AVR, and PRU based +* Portable code. Klipper works on ARM, AVR, PRU, and other micro-controllers. Existing "reprap" style printers can run Klipper without hardware modification - just add a Raspberry Pi. Klipper's internal code layout makes it easier to support other @@ -105,7 +105,8 @@ Klipper supports many standard 3d printer features: bed tilt detection or full mesh bed leveling. If the bed uses multiple Z steppers then Klipper can also level by independently manipulating the Z steppers. Most Z height probes are supported, - including BL-Touch probes and servo activated probes. + including BL-Touch probes and servo activated probes. Probes may be + calibrated for axis twist compensation. * Automatic delta calibration support. The calibration tool can perform basic height calibration as well as an enhanced X and Y @@ -117,10 +118,11 @@ Klipper supports many standard 3d printer features: * Support for common temperature sensors (eg, common thermistors, AD595, AD597, AD849x, PT100, PT1000, MAX6675, MAX31855, MAX31856, - MAX31865, BME280, HTU21D, DS18B20, and LM75). Custom thermistors and - custom analog temperature sensors can also be configured. One can - monitor the internal micro-controller temperature sensor and the - internal temperature sensor of a Raspberry Pi. + MAX31865, BME280, HTU21D, DS18B20, AHT10, and LM75). Custom + thermistors and custom analog temperature sensors can also be + configured. One can monitor the internal micro-controller + temperature sensor and the internal temperature sensor of a + Raspberry Pi. * Basic thermal heater protection enabled by default. @@ -129,9 +131,9 @@ Klipper supports many standard 3d printer features: speed can be monitored on fans that have a tachometer. * Support for run-time configuration of TMC2130, TMC2208/TMC2224, - TMC2209, TMC2660, and TMC5160 stepper motor drivers. There is also - support for current control of traditional stepper drivers via - AD5206, DAC084S085, MCP4451, MCP4728, MCP4018, and PWM pins. + TMC2209, TMC2240, TMC2660, and TMC5160 stepper motor drivers. There + is also support for current control of traditional stepper drivers + via AD5206, DAC084S085, MCP4451, MCP4728, MCP4018, and PWM pins. * Support for common LCD displays attached directly to the printer. A default menu is also available. The contents of the display and menu @@ -151,8 +153,8 @@ Klipper supports many standard 3d printer features: * Support for filament presence sensors, filament motion sensors, and filament width sensors. -* Support for measuring and recording acceleration using an adxl345, - mpu9250, and mpu6050 accelerometers. +* Support for measuring and recording acceleration using adxl345, + mpu9250, mpu6050, and lis2dw12 accelerometers. * Support for limiting the top speed of short "zigzag" moves to reduce printer vibration and noise. See the [kinematics](Kinematics.md) From 0d67d9c45d2dc39f8b4be7d1bb54b94b2698a2b6 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Thu, 12 Oct 2023 20:54:45 -0400 Subject: [PATCH 09/53] docs: Note the release of v0.12.0 Signed-off-by: Kevin O'Connor --- docs/Releases.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/Releases.md b/docs/Releases.md index 7054a0b30..e564a9d24 100644 --- a/docs/Releases.md +++ b/docs/Releases.md @@ -3,6 +3,24 @@ History of Klipper releases. Please see [installation](Installation.md) for information on installing Klipper. +## Klipper 0.12.0 + +Available on 20231110. Major changes in this release: +* Support for COPY and MIRROR modes on IDEX printers. +* Several micro-controller improvements: + * Support for new ar100 and hc32f460 architectures. + * Support for stm32f7, stm32g0b0, stm32g07x, stm32g4, stm32h723, + n32g45x, samc21, and samd21j18 chip variants. + * Improved DFU and Katapult reboot handling. + * Improved performance on USB to CANbus bridge mode. + * Improved performance on "linux mcu". + * New support for software based i2c. +* New hardware support for tmc2240 stepper motor drivers, lis2dw12 + accelerometers, and aht10 temperature sensors. +* New axis_twist_compensation and temperature_combined modules added. +* New support for gcode arcs in XY, XZ, and YZ planes. +* Several bug fixes and code cleanups. + ## Klipper 0.11.0 Available on 20221128. Major changes in this release: From 74473322e550e8ca1d7a9c513ea3a909b4845126 Mon Sep 17 00:00:00 2001 From: Herb McNew Date: Tue, 14 Nov 2023 13:15:46 -0500 Subject: [PATCH 10/53] config: Add Sovol SV06 Plus (#6397) New configuration for the Sovol SV06 Plus Initial pass at adding a printer configuration for the Sovol SV06 Plus based on the existing Sovol SV06 config. Updated for the larger build volume, added filament runout sensor, and removed any references to the LCD screen since the stock screen doesn't work with Klipper. Signed-off-by: Herb McNew --- config/printer-sovol-sv06-plus-2023.cfg | 147 ++++++++++++++++++++++++ test/klippy/printers.test | 1 + 2 files changed, 148 insertions(+) create mode 100644 config/printer-sovol-sv06-plus-2023.cfg diff --git a/config/printer-sovol-sv06-plus-2023.cfg b/config/printer-sovol-sv06-plus-2023.cfg new file mode 100644 index 000000000..cb6fbd873 --- /dev/null +++ b/config/printer-sovol-sv06-plus-2023.cfg @@ -0,0 +1,147 @@ +# This file contains pin mappings for the stock Sovol SV06 Plus +# To use this config, during "make menuconfig" select the +# STM32F103 with a "28KiB bootloader" and serial (on USART1 PA10/PA9) communication. +# Also, since it is using the GD32F103, please select Disable SWD at startup +# +# Flash this firmware by copying "out/klipper.bin" to a SD card and +# turning on the printer with the card inserted. The firmware +# filename must end in ".bin" and must not match the last filename +# that was flashed. +# +# Note: The stock LCD display does not currently work with Klipper +# +# See docs/Config_Reference.md for a description of parameters. +[mcu] +serial: /dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0 +restart_method: command + +[printer] +kinematics: cartesian +max_velocity: 500 +max_accel: 2000 +max_z_velocity: 10 +max_z_accel: 100 + +[stepper_x] +step_pin: PC2 +dir_pin: !PB9 +enable_pin: !PC3 +microsteps: 16 +rotation_distance: 40 +endstop_pin: tmc2209_stepper_x:virtual_endstop +position_endstop: 0 +position_max: 305 +homing_speed: 40 +homing_retract_dist: 0 + +[tmc2209 stepper_x] +uart_pin: PC1 +run_current: 0.860 +sense_resistor: 0.150 +uart_address: 3 +driver_SGTHRS: 81 +diag_pin: PA5 + +[stepper_y] +step_pin: PB8 +dir_pin: PB7 +enable_pin: !PC3 +microsteps: 16 +rotation_distance: 40 +endstop_pin: tmc2209_stepper_y:virtual_endstop +position_endstop: 0 +position_max: 305 +homing_speed: 40 +homing_retract_dist: 0 + +[tmc2209 stepper_y] +uart_pin: PC0 +run_current: 0.900 +sense_resistor: 0.150 +uart_address: 3 +driver_SGTHRS: 82 +diag_pin: PA6 + +[stepper_z] +step_pin: PB6 +dir_pin: !PB5 +enable_pin: !PC3 +microsteps: 16 +rotation_distance: 4 +endstop_pin: probe:z_virtual_endstop +position_min: -4 +position_max: 350 +homing_speed: 4 + +[tmc2209 stepper_z] +uart_pin: PA15 +run_current: 1.000 +interpolate: False +sense_resistor: 0.150 +uart_address: 3 +diag_pin: PA7 + +[extruder] +max_extrude_only_distance: 100.0 +step_pin: PB4 +dir_pin: !PB3 +enable_pin: !PC3 +microsteps: 16 +rotation_distance: 4.56 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +heater_pin: PA1 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PC5 +control: pid +pid_kd: 41.96 +pid_kp: 15.66 +pid_ki: 1.49 +min_temp: 0 +max_temp: 300 + +[tmc2209 extruder] +uart_pin: PC14 +run_current: 0.550 +stealthchop_threshold: 0 +interpolate: False +sense_resistor: 0.150 +uart_address: 3 + +[heater_bed] +heater_pin: PA2 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PC4 +control: pid +pid_kp: 186.38 +pid_ki: 36.12 +pid_kd: 637.30 +min_temp: 0 +max_temp: 130 + +[fan] +pin: PA0 + +[probe] +pin: PB1 +x_offset: 28 +y_offset: -20 +z_offset: 0 + +[safe_z_home] +home_xy_position: 123,170 +z_hop: 10 +z_hop_speed: 5 + +[bed_mesh] +speed: 120 +mesh_min: 28, 20 +mesh_max: 270, 270 +probe_count: 5 +algorithm: bicubic +fade_end: 10 +fade_target: 0 + +[filament_switch_sensor filament_runout_sensor] +switch_pin: PA4 +pause_on_runout: True diff --git a/test/klippy/printers.test b/test/klippy/printers.test index d9168adc5..2ab96acc8 100644 --- a/test/klippy/printers.test +++ b/test/klippy/printers.test @@ -157,6 +157,7 @@ CONFIG ../../config/printer-anycubic-vyper-2021.cfg CONFIG ../../config/printer-monoprice-select-mini-v1-2016.cfg CONFIG ../../config/printer-sovol-sv05-2022.cfg CONFIG ../../config/printer-sovol-sv06-2022.cfg +CONFIG ../../config/printer-sovol-sv06-plus-2023.cfg CONFIG ../../config/printer-sunlu-t3-2022.cfg # Printers using the stm32f103 via serial From 3f8f30d6123c647c05ba3f4f58477d9dda36f03f Mon Sep 17 00:00:00 2001 From: Herb McNew Date: Thu, 16 Nov 2023 21:57:45 -0500 Subject: [PATCH 11/53] config: Update printer-sovol-sv06-plus-2023.cfg (#6401) In testing with a user on Discord we discovered the sensorless homing thresholds were out of line with what Sovol ships with Marlin. This aligns with their settings. Signed-off-by: Herb McNew --- config/printer-sovol-sv06-plus-2023.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/printer-sovol-sv06-plus-2023.cfg b/config/printer-sovol-sv06-plus-2023.cfg index cb6fbd873..8c72192f3 100644 --- a/config/printer-sovol-sv06-plus-2023.cfg +++ b/config/printer-sovol-sv06-plus-2023.cfg @@ -39,7 +39,7 @@ uart_pin: PC1 run_current: 0.860 sense_resistor: 0.150 uart_address: 3 -driver_SGTHRS: 81 +driver_SGTHRS: 86 diag_pin: PA5 [stepper_y] @@ -59,7 +59,7 @@ uart_pin: PC0 run_current: 0.900 sense_resistor: 0.150 uart_address: 3 -driver_SGTHRS: 82 +driver_SGTHRS: 110 diag_pin: PA6 [stepper_z] From 4688c21c54dfb89ef9902b607521a8cc0e99c94a Mon Sep 17 00:00:00 2001 From: Thijs Triemstra Date: Fri, 17 Nov 2023 04:06:13 +0100 Subject: [PATCH 12/53] klippy: Replace deprecated logger.warn with logger.warning (#6385) Replace deprecated logger.warn with logger.warning logger.warn will be removed in Python 3.13 Signed-off-by: Thijs Triemstra --- klippy/configfile.py | 8 ++++---- klippy/extras/adc_temperature.py | 4 ++-- klippy/extras/htu21d.py | 14 ++++++++------ klippy/extras/spi_temperature.py | 2 +- klippy/extras/thermistor.py | 3 ++- klippy/serialhdl.py | 4 ++-- 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/klippy/configfile.py b/klippy/configfile.py index dd32d47fd..e5c0fb20b 100644 --- a/klippy/configfile.py +++ b/klippy/configfile.py @@ -172,16 +172,16 @@ def _find_autosave_data(self, data): autosave_data = data[pos + len(AUTOSAVE_HEADER):].strip() # Check for errors and strip line prefixes if "\n#*# " in regular_data: - logging.warn("Can't read autosave from config file" - " - autosave state corrupted") + logging.warning("Can't read autosave from config file" + " - autosave state corrupted") return data, "" out = [""] for line in autosave_data.split('\n'): if ((not line.startswith("#*#") or (len(line) >= 4 and not line.startswith("#*# "))) and autosave_data): - logging.warn("Can't read autosave from config file" - " - modifications after header") + logging.warning("Can't read autosave from config file" + " - modifications after header") return data, "" out.append(line[4:]) out.append("") diff --git a/klippy/extras/adc_temperature.py b/klippy/extras/adc_temperature.py index d456a653d..b76e8c66f 100644 --- a/klippy/extras/adc_temperature.py +++ b/klippy/extras/adc_temperature.py @@ -95,8 +95,8 @@ def __init__(self, config, params): for temp, volt in params: adc = (volt - voltage_offset) / adc_voltage if adc < 0. or adc > 1.: - logging.warn("Ignoring adc sample %.3f/%.3f in heater %s", - temp, volt, config.get_name()) + logging.warning("Ignoring adc sample %.3f/%.3f in heater %s", + temp, volt, config.get_name()) continue samples.append((adc, temp)) try: diff --git a/klippy/extras/htu21d.py b/klippy/extras/htu21d.py index 16da7f8eb..ec44da633 100644 --- a/klippy/extras/htu21d.py +++ b/klippy/extras/htu21d.py @@ -126,7 +126,7 @@ def _init_htu21d(self): rdevId |= response[1] checksum = response[2] if self._chekCRC8(rdevId) != checksum: - logging.warn("htu21d: Reading deviceId !Checksum error!") + logging.warning("htu21d: Reading deviceId !Checksum error!") rdevId = rdevId >> 8 deviceId_list = list( filter( @@ -135,10 +135,10 @@ def _init_htu21d(self): if len(deviceId_list) != 0: logging.info("htu21d: Found Device Type %s" % deviceId_list[0]) else: - logging.warn("htu21d: Unknown Device ID %#x " % rdevId) + logging.warning("htu21d: Unknown Device ID %#x " % rdevId) - if(self.deviceId != deviceId_list[0]): - logging.warn( + if self.deviceId != deviceId_list[0]: + logging.warning( "htu21d: Found device %s. Forcing to type %s as config.", deviceId_list[0],self.deviceId) @@ -169,7 +169,9 @@ def _sample_htu21d(self, eventtime): rtemp = response[0] << 8 rtemp |= response[1] if self._chekCRC8(rtemp) != response[2]: - logging.warn("htu21d: Checksum error on Temperature reading!") + logging.warning( + "htu21d: Checksum error on Temperature reading!" + ) else: self.temp = (0.002681 * float(rtemp) - 46.85) logging.debug("htu21d: Temperature %.2f " % self.temp) @@ -190,7 +192,7 @@ def _sample_htu21d(self, eventtime): rhumid = response[0] << 8 rhumid|= response[1] if self._chekCRC8(rhumid) != response[2]: - logging.warn("htu21d: Checksum error on Humidity reading!") + logging.warning("htu21d: Checksum error on Humidity reading!") else: #clear status bits, # humidity always returns xxxxxx10 in the LSB field diff --git a/klippy/extras/spi_temperature.py b/klippy/extras/spi_temperature.py index 77033b1b1..92d1b385f 100644 --- a/klippy/extras/spi_temperature.py +++ b/klippy/extras/spi_temperature.py @@ -62,7 +62,7 @@ def _handle_spi_response(self, params): last_read_time = self.mcu.clock_to_print_time(last_read_clock) self._callback(last_read_time, temp) def report_fault(self, msg): - logging.warn(msg) + logging.warning(msg) ###################################################################### diff --git a/klippy/extras/thermistor.py b/klippy/extras/thermistor.py index d04225b7b..16d6ca61b 100644 --- a/klippy/extras/thermistor.py +++ b/klippy/extras/thermistor.py @@ -33,7 +33,8 @@ def setup_coefficients(self, t1, r1, t2, r2, t3, r3, name=""): / (ln3_r12 - ln3_r13 * ln_r12 / ln_r13)) if self.c3 <= 0.: beta = ln_r13 / inv_t13 - logging.warn("Using thermistor beta %.3f in heater %s", beta, name) + logging.warning("Using thermistor beta %.3f in heater %s", + beta, name) self.setup_coefficients_beta(t1, r1, beta) return self.c2 = (inv_t12 - self.c3 * ln3_r12) / ln_r12 diff --git a/klippy/serialhdl.py b/klippy/serialhdl.py index f3c4ed990..f9e542d35 100644 --- a/klippy/serialhdl.py +++ b/klippy/serialhdl.py @@ -137,8 +137,8 @@ def connect_canbus(self, canbus_uuid, canbus_nodeid, canbus_iface="can0"): bustype='socketcan') bus.send(set_id_msg) except (can.CanError, os.error) as e: - logging.warn("%sUnable to open CAN port: %s", - self.warn_prefix, e) + logging.warning("%sUnable to open CAN port: %s", + self.warn_prefix, e) self.reactor.pause(self.reactor.monotonic() + 5.) continue bus.close = bus.shutdown # XXX From 48a05eaa542bfd4a1099c2224bda443049a6fa64 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Fri, 13 Oct 2023 20:43:27 -0400 Subject: [PATCH 13/53] stepcompress: Add support for queuing messages that consume move queue space Signed-off-by: Kevin O'Connor --- klippy/chelper/__init__.py | 2 ++ klippy/chelper/stepcompress.c | 15 +++++++++++++++ klippy/chelper/stepcompress.h | 2 ++ 3 files changed, 19 insertions(+) diff --git a/klippy/chelper/__init__.py b/klippy/chelper/__init__.py index c94e9677e..290234c57 100644 --- a/klippy/chelper/__init__.py +++ b/klippy/chelper/__init__.py @@ -49,6 +49,8 @@ , uint64_t clock); int stepcompress_queue_msg(struct stepcompress *sc , uint32_t *data, int len); + int stepcompress_queue_mq_msg(struct stepcompress *sc, uint64_t req_clock + , uint32_t *data, int len); int stepcompress_extract_old(struct stepcompress *sc , struct pull_history_steps *p, int max , uint64_t start_clock, uint64_t end_clock); diff --git a/klippy/chelper/stepcompress.c b/klippy/chelper/stepcompress.c index e261f1d30..e5514b952 100644 --- a/klippy/chelper/stepcompress.c +++ b/klippy/chelper/stepcompress.c @@ -623,6 +623,21 @@ stepcompress_queue_msg(struct stepcompress *sc, uint32_t *data, int len) return 0; } +// Queue an mcu command that will consume space in the mcu move queue +int __visible +stepcompress_queue_mq_msg(struct stepcompress *sc, uint64_t req_clock + , uint32_t *data, int len) +{ + int ret = stepcompress_flush(sc, UINT64_MAX); + if (ret) + return ret; + + struct queue_message *qm = message_alloc_and_encode(data, len); + qm->min_clock = qm->req_clock = req_clock; + list_add_tail(&qm->node, &sc->msg_queue); + return 0; +} + // Return history of queue_step commands int __visible stepcompress_extract_old(struct stepcompress *sc, struct pull_history_steps *p diff --git a/klippy/chelper/stepcompress.h b/klippy/chelper/stepcompress.h index b8a950575..bfc0dfcde 100644 --- a/klippy/chelper/stepcompress.h +++ b/klippy/chelper/stepcompress.h @@ -29,6 +29,8 @@ int stepcompress_set_last_position(struct stepcompress *sc, uint64_t clock int64_t stepcompress_find_past_position(struct stepcompress *sc , uint64_t clock); int stepcompress_queue_msg(struct stepcompress *sc, uint32_t *data, int len); +int stepcompress_queue_mq_msg(struct stepcompress *sc, uint64_t req_clock + , uint32_t *data, int len); int stepcompress_extract_old(struct stepcompress *sc , struct pull_history_steps *p, int max , uint64_t start_clock, uint64_t end_clock); From 29b7550ce5330da8e5ca4f22c1420cbeadf01eaf Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Fri, 13 Oct 2023 21:10:11 -0400 Subject: [PATCH 14/53] pwm_tool: Add support for high-speed PWM pin updates The output_pin module is only capable of updating an output pin at most once every 100ms. Add a new pwm_tool module that is capable of queuing updates in the micro-controller and thus allowing for much higher update rates. Signed-off-by: Kevin O'Connor --- config/sample-pwm-tool.cfg | 11 ++- docs/Config_Reference.md | 20 +++++ docs/G-Codes.md | 3 +- docs/Status_Reference.md | 3 +- docs/Using_PWM_Tools.md | 10 +-- klippy/extras/pwm_tool.py | 157 +++++++++++++++++++++++++++++++++++++ test/klippy/pwm.cfg | 6 ++ test/klippy/pwm.test | 8 ++ 8 files changed, 201 insertions(+), 17 deletions(-) create mode 100644 klippy/extras/pwm_tool.py diff --git a/config/sample-pwm-tool.cfg b/config/sample-pwm-tool.cfg index 4adcea6f1..489860617 100644 --- a/config/sample-pwm-tool.cfg +++ b/config/sample-pwm-tool.cfg @@ -2,9 +2,8 @@ # such as a laser or spindle. # See docs/Using_PWM_Tools.md for a more detailed description. -[output_pin TOOL] +[pwm_tool TOOL] pin: !ar9 # use your fan's pin number -pwm: True hardware_pwm: True cycle_time: 0.001 shutdown_value: 0 @@ -36,9 +35,9 @@ gcode: [menu __main __control __toolonoff] type: input -enable: {'output_pin TOOL' in printer} +enable: {'pwm_tool TOOL' in printer} name: Fan: {'ON ' if menu.input else 'OFF'} -input: {printer['output_pin TOOL'].value} +input: {printer['pwm_tool TOOL'].value} input_min: 0 input_max: 1 input_step: 1 @@ -47,9 +46,9 @@ gcode: [menu __main __control __toolspeed] type: input -enable: {'output_pin TOOL' in printer} +enable: {'pwm_tool TOOL' in printer} name: Tool speed: {'%3d' % (menu.input*100)}% -input: {printer['output_pin TOOL'].value} +input: {printer['pwm_tool TOOL'].value} input_min: 0 input_max: 1 input_step: 0.01 diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 241391834..c7a2a3dbf 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -3127,6 +3127,26 @@ pin: # parameter. ``` +### [pwm_tool] + +Pulse width modulation digital output pins capable of high speed +updates (one may define any number of sections with an "output_pin" +prefix). Pins configured here will be setup as output pins and one may +modify them at run-time using "SET_PIN PIN=my_pin VALUE=.1" type +extended [g-code commands](G-Codes.md#output_pin). + +``` +[pwm_tool my_tool] +pin: +# The pin to configure as an output. This parameter must be provided. +#value: +#shutdown_value: +#cycle_time: 0.100 +#hardware_pwm: False +#scale: +# See the "output_pin" section for the definition of these parameters. +``` + ### [static_digital_output] Statically configured digital output pins (one may define any number diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 1f466dcd3..6d492c251 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -834,7 +834,8 @@ commands to manage the LED's color settings). ### [output_pin] The following command is available when an -[output_pin config section](Config_Reference.md#output_pin) is +[output_pin config section](Config_Reference.md#output_pin) or +[pwm_tool config section](Config_Reference.md#pwm_tool) is enabled. #### SET_PIN diff --git a/docs/Status_Reference.md b/docs/Status_Reference.md index 13139dd02..5477049a1 100644 --- a/docs/Status_Reference.md +++ b/docs/Status_Reference.md @@ -318,7 +318,8 @@ is defined): ## output_pin The following information is available in -[output_pin some_name](Config_Reference.md#output_pin) objects: +[output_pin some_name](Config_Reference.md#output_pin) and +[pwm_tool some_name](Config_Reference.md#pwm_tool) objects: - `value`: The "value" of the pin, as set by a `SET_PIN` command. ## palette2 diff --git a/docs/Using_PWM_Tools.md b/docs/Using_PWM_Tools.md index a67df84d3..108ae37ad 100644 --- a/docs/Using_PWM_Tools.md +++ b/docs/Using_PWM_Tools.md @@ -1,7 +1,7 @@ # Using PWM tools This document describes how to setup a PWM-controlled laser or spindle -using `output_pin` and some macros. +using `pwm_tool` and some macros. ## How does it work? @@ -26,14 +26,6 @@ so that when your host or MCU encounters an error, the tool will stop. For an example configuration, see [config/sample-pwm-tool.cfg](/config/sample-pwm-tool.cfg). -## Current Limitations - -There is a limitation of how frequent PWM updates may occur. -While being very precise, a PWM update may only occur every 0.1 seconds, -rendering it almost useless for raster engraving. -However, there exists an [experimental branch](https://github.com/Cirromulus/klipper/tree/laser_tool) with its own tradeoffs. -In long term, it is planned to add this functionality to main-line klipper. - ## Commands `M3/M4 S` : Set PWM duty-cycle. Values between 0 and 255. diff --git a/klippy/extras/pwm_tool.py b/klippy/extras/pwm_tool.py new file mode 100644 index 000000000..c012bb317 --- /dev/null +++ b/klippy/extras/pwm_tool.py @@ -0,0 +1,157 @@ +# Queued PWM gpio output +# +# Copyright (C) 2017-2023 Kevin O'Connor +# +# This file may be distributed under the terms of the GNU GPLv3 license. +import chelper + +PIN_MIN_TIME = 0.100 +MAX_SCHEDULE_TIME = 5.0 + +class error(Exception): + pass + +class MCU_queued_pwm: + def __init__(self, pin_params): + self._mcu = pin_params['chip'] + self._hardware_pwm = False + self._cycle_time = 0.100 + self._max_duration = 2. + self._oid = self._mcu.create_oid() + ffi_main, ffi_lib = chelper.get_ffi() + self._stepqueue = ffi_main.gc(ffi_lib.stepcompress_alloc(self._oid), + ffi_lib.stepcompress_free) + self._mcu.register_stepqueue(self._stepqueue) + self._stepcompress_queue_mq_msg = ffi_lib.stepcompress_queue_mq_msg + self._mcu.register_config_callback(self._build_config) + self._pin = pin_params['pin'] + self._invert = pin_params['invert'] + self._start_value = self._shutdown_value = float(self._invert) + self._last_clock = self._cycle_ticks = 0 + self._pwm_max = 0. + self._set_cmd_tag = None + def get_mcu(self): + return self._mcu + def setup_max_duration(self, max_duration): + self._max_duration = max_duration + def setup_cycle_time(self, cycle_time, hardware_pwm=False): + self._cycle_time = cycle_time + self._hardware_pwm = hardware_pwm + def setup_start_value(self, start_value, shutdown_value): + if self._invert: + start_value = 1. - start_value + shutdown_value = 1. - shutdown_value + self._start_value = max(0., min(1., start_value)) + self._shutdown_value = max(0., min(1., shutdown_value)) + def _build_config(self): + if self._max_duration and self._start_value != self._shutdown_value: + raise pins.error("Pin with max duration must have start" + " value equal to shutdown value") + cmd_queue = self._mcu.alloc_command_queue() + curtime = self._mcu.get_printer().get_reactor().monotonic() + printtime = self._mcu.estimated_print_time(curtime) + self._last_clock = self._mcu.print_time_to_clock(printtime + 0.200) + cycle_ticks = self._mcu.seconds_to_clock(self._cycle_time) + mdur_ticks = self._mcu.seconds_to_clock(self._max_duration) + if mdur_ticks >= 1<<31: + raise pins.error("PWM pin max duration too large") + if self._hardware_pwm: + self._pwm_max = self._mcu.get_constant_float("PWM_MAX") + self._mcu.add_config_cmd( + "config_pwm_out oid=%d pin=%s cycle_ticks=%d value=%d" + " default_value=%d max_duration=%d" + % (self._oid, self._pin, cycle_ticks, + self._start_value * self._pwm_max, + self._shutdown_value * self._pwm_max, mdur_ticks)) + svalue = int(self._start_value * self._pwm_max + 0.5) + self._mcu.add_config_cmd("queue_pwm_out oid=%d clock=%d value=%d" + % (self._oid, self._last_clock, svalue), + on_restart=True) + self._set_cmd_tag = self._mcu.lookup_command( + "queue_pwm_out oid=%c clock=%u value=%hu", + cq=cmd_queue).get_command_tag() + return + # Software PWM + if self._shutdown_value not in [0., 1.]: + raise pins.error("shutdown value must be 0.0 or 1.0 on soft pwm") + if cycle_ticks >= 1<<31: + raise pins.error("PWM pin cycle time too large") + self._mcu.add_config_cmd( + "config_digital_out oid=%d pin=%s value=%d" + " default_value=%d max_duration=%d" + % (self._oid, self._pin, self._start_value >= 1.0, + self._shutdown_value >= 0.5, mdur_ticks)) + self._mcu.add_config_cmd( + "set_digital_out_pwm_cycle oid=%d cycle_ticks=%d" + % (self._oid, cycle_ticks)) + self._cycle_ticks = cycle_ticks + svalue = int(self._start_value * cycle_ticks + 0.5) + self._mcu.add_config_cmd( + "queue_digital_out oid=%d clock=%d on_ticks=%d" + % (self._oid, self._last_clock, svalue), is_init=True) + self._set_cmd_tag = self._mcu.lookup_command( + "queue_digital_out oid=%c clock=%u on_ticks=%u", + cq=cmd_queue).get_command_tag() + def set_pwm(self, print_time, value): + clock = self._mcu.print_time_to_clock(print_time) + minclock = self._last_clock + self._last_clock = clock + if self._invert: + value = 1. - value + max_count = self._cycle_ticks + if self._hardware_pwm: + max_count = self._pwm_max + v = int(max(0., min(1., value)) * max_count + 0.5) + data = (self._set_cmd_tag, self._oid, clock & 0xffffffff, v) + ret = self._stepcompress_queue_mq_msg(self._stepqueue, clock, + data, len(data)) + if ret: + raise error("Internal error in stepcompress") + +class PrinterOutputPin: + def __init__(self, config): + self.printer = config.get_printer() + ppins = self.printer.lookup_object('pins') + # Determine pin type + pin_params = ppins.lookup_pin(config.get('pin'), can_invert=True) + self.mcu_pin = MCU_queued_pwm(pin_params) + cycle_time = config.getfloat('cycle_time', 0.100, above=0., + maxval=MAX_SCHEDULE_TIME) + hardware_pwm = config.getboolean('hardware_pwm', False) + self.mcu_pin.setup_cycle_time(cycle_time, hardware_pwm) + self.scale = config.getfloat('scale', 1., above=0.) + self.last_print_time = 0. + self.mcu_pin.setup_max_duration(0.) + # Determine start and shutdown values + self.last_value = config.getfloat( + 'value', 0., minval=0., maxval=self.scale) / self.scale + self.shutdown_value = config.getfloat( + 'shutdown_value', 0., minval=0., maxval=self.scale) / self.scale + self.mcu_pin.setup_start_value(self.last_value, self.shutdown_value) + # Register commands + pin_name = config.get_name().split()[1] + gcode = self.printer.lookup_object('gcode') + gcode.register_mux_command("SET_PIN", "PIN", pin_name, + self.cmd_SET_PIN, + desc=self.cmd_SET_PIN_help) + def get_status(self, eventtime): + return {'value': self.last_value} + def _set_pin(self, print_time, value): + if value == self.last_value: + return + print_time = max(print_time, self.last_print_time) + self.mcu_pin.set_pwm(print_time, value) + self.last_value = value + self.last_print_time = print_time + cmd_SET_PIN_help = "Set the value of an output pin" + def cmd_SET_PIN(self, gcmd): + # Read requested value + value = gcmd.get_float('VALUE', minval=0., maxval=self.scale) + value /= self.scale + # Obtain print_time and apply requested settings + toolhead = self.printer.lookup_object('toolhead') + toolhead.register_lookahead_callback( + lambda print_time: self._set_pin(print_time, value)) + +def load_config_prefix(config): + return PrinterOutputPin(config) diff --git a/test/klippy/pwm.cfg b/test/klippy/pwm.cfg index 45555ca87..fbda91269 100644 --- a/test/klippy/pwm.cfg +++ b/test/klippy/pwm.cfg @@ -13,6 +13,12 @@ value: 0 shutdown_value: 0 cycle_time: 0.01 +[pwm_tool test_pwm_tool] +pin: PH4 +value: 0 +shutdown_value: 0 +cycle_time: 0.01 + [mcu] serial: /dev/ttyACM0 diff --git a/test/klippy/pwm.test b/test/klippy/pwm.test index d204cbc6d..5e74a3e05 100644 --- a/test/klippy/pwm.test +++ b/test/klippy/pwm.test @@ -28,3 +28,11 @@ SET_PIN PIN=soft_pwm_pin VALUE=0.5 CYCLE_TIME=0.5 SET_PIN PIN=soft_pwm_pin VALUE=0.5 CYCLE_TIME=0.5 SET_PIN PIN=soft_pwm_pin VALUE=0.75 CYCLE_TIME=0.5 SET_PIN PIN=soft_pwm_pin VALUE=0.75 CYCLE_TIME=0.75 + +# PWM tool +# Basic test +SET_PIN PIN=test_pwm_tool VALUE=0 +SET_PIN PIN=test_pwm_tool VALUE=0.5 +SET_PIN PIN=test_pwm_tool VALUE=0.5 +SET_PIN PIN=test_pwm_tool VALUE=0.25 +SET_PIN PIN=test_pwm_tool VALUE=1 From 83df4a8627ce98316e9515425f01d443caa52d52 Mon Sep 17 00:00:00 2001 From: docgalaxyblock Date: Wed, 1 Nov 2023 17:26:12 +0100 Subject: [PATCH 15/53] stm32: enable 64KiB bootloader offset for all F4 Signed-off-by: Joshua Schlicker --- src/stm32/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stm32/Kconfig b/src/stm32/Kconfig index e39611764..7f50ea3ca 100644 --- a/src/stm32/Kconfig +++ b/src/stm32/Kconfig @@ -288,7 +288,7 @@ choice config STM32_FLASH_START_C000 bool "48KiB bootloader (MKS Robin Nano V3)" if MACH_STM32F4x5 config STM32_FLASH_START_10000 - bool "64KiB bootloader" if MACH_STM32F103 || MACH_STM32F446 || MACH_STM32F401 + bool "64KiB bootloader" if MACH_STM32F103 || MACH_STM32F4 config STM32_FLASH_START_800 bool "2KiB bootloader (HID Bootloader)" if MACH_STM32F103 From 3c8f202dfa4a581dd52ad0cc98f3b7e41b23559b Mon Sep 17 00:00:00 2001 From: vaxxi Date: Fri, 17 Nov 2023 05:27:44 +0200 Subject: [PATCH 16/53] bmp280: Add support for BMP180 sensor (#6370) Extends the BMxx80 category with support for the older BMP180 sensor, providing temperature and humidity output. Signed-off-by: VAXXi Popescu --- docs/Config_Reference.md | 6 +- klippy/extras/bme280.py | 127 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 9 deletions(-) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index c7a2a3dbf..cc78ddbe3 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -2440,9 +2440,9 @@ sensor_pin: # name in the above list. ``` -### BMP280/BME280/BME680 temperature sensor +### BMP180/BMP280/BME280/BME680 temperature sensor -BMP280/BME280/BME680 two wire interface (I2C) environmental sensors. +BMP180/BMP280/BME280/BME680 two wire interface (I2C) environmental sensors. Note that these sensors are not intended for use with extruders and heater beds, but rather for monitoring ambient temperature (C), pressure (hPa), relative humidity and in case of the BME680 gas level. @@ -2453,7 +2453,7 @@ temperature. ``` sensor_type: BME280 #i2c_address: -# Default is 118 (0x76). Some BME280 sensors have an address of 119 +# Default is 118 (0x76). The BMP180 and some BME280 sensors have an address of 119 # (0x77). #i2c_mcu: #i2c_bus: diff --git a/klippy/extras/bme280.py b/klippy/extras/bme280.py index e27bfb479..3bc3c471c 100644 --- a/klippy/extras/bme280.py +++ b/klippy/extras/bme280.py @@ -8,6 +8,7 @@ REPORT_TIME = .8 BME280_CHIP_ADDR = 0x76 + BME280_REGS = { 'RESET': 0xE0, 'CTRL_HUM': 0xF2, 'STATUS': 0xF3, 'CTRL_MEAS': 0xF4, 'CONFIG': 0xF5, @@ -46,6 +47,16 @@ 15: (1., 244.140625) } +BMP180_REGS = { + 'RESET': 0xE0, + 'CAL_1': 0xAA, + 'CTRL_MEAS': 0xF4, + 'REG_MSB': 0xF6, + 'REG_LSB': 0xF7, + 'CRV_TEMP': 0x2E, + 'CRV_PRES': 0x34 +} + STATUS_MEASURING = 1 << 3 STATUS_IM_UPDATE = 1 MODE = 1 @@ -57,7 +68,7 @@ RESET_CHIP_VALUE = 0xB6 BME_CHIPS = { - 0x58: 'BMP280', 0x60: 'BME280', 0x61: 'BME680' + 0x58: 'BMP280', 0x60: 'BME280', 0x61: 'BME680', 0x55: 'BMP180' } BME_CHIP_ID_REG = 0xD0 @@ -81,6 +92,14 @@ def get_signed_byte(bits): return get_twos_complement(bits, 8) +def get_unsigned_short_msb(bits): + return bits[0] << 8 | bits[1] + + +def get_signed_short_msb(bits): + val = get_unsigned_short_msb(bits) + return get_twos_complement(val, 16) + class BME280: def __init__(self, config): self.printer = config.get_printer() @@ -188,6 +207,23 @@ def read_calibration_data_bme680(calib_data_1, calib_data_2): dig['G3'] = get_signed_byte(calib_data_2[13]) return dig + def read_calibration_data_bmp180(calib_data_1): + dig = {} + dig['AC1'] = get_signed_short_msb(calib_data_1[0:2]) + dig['AC2'] = get_signed_short_msb(calib_data_1[2:4]) + dig['AC3'] = get_signed_short_msb(calib_data_1[4:6]) + dig['AC4'] = get_unsigned_short_msb(calib_data_1[6:8]) + dig['AC5'] = get_unsigned_short_msb(calib_data_1[8:10]) + dig['AC6'] = get_unsigned_short_msb(calib_data_1[10:12]) + + dig['B1'] = get_signed_short_msb(calib_data_1[12:14]) + dig['B2'] = get_signed_short_msb(calib_data_1[14:16]) + + dig['MB'] = get_signed_short_msb(calib_data_1[16:18]) + dig['MC'] = get_signed_short_msb(calib_data_1[18:20]) + dig['MD'] = get_signed_short_msb(calib_data_1[20:22]) + return dig + chip_id = self.read_id() if chip_id not in BME_CHIPS.keys(): logging.info("bme280: Unknown Chip ID received %#x" % chip_id) @@ -201,15 +237,21 @@ def read_calibration_data_bme680(calib_data_1, calib_data_2): self.reactor.pause(self.reactor.monotonic() + .5) # Make sure non-volatile memory has been copied to registers - status = self.read_register('STATUS', 1)[0] - while status & STATUS_IM_UPDATE: - self.reactor.pause(self.reactor.monotonic() + .01) + if self.chip_type != 'BMP180': + # BMP180 has no status register available status = self.read_register('STATUS', 1)[0] + while status & STATUS_IM_UPDATE: + self.reactor.pause(self.reactor.monotonic() + .01) + status = self.read_register('STATUS', 1)[0] if self.chip_type == 'BME680': self.max_sample_time = 0.5 self.sample_timer = self.reactor.register_timer(self._sample_bme680) self.chip_registers = BME680_REGS + elif self.chip_type == 'BMP180': + self.max_sample_time = (1.25 + ((2.3 * self.os_pres) + .575)) / 1000 + self.sample_timer = self.reactor.register_timer(self._sample_bmp180) + self.chip_registers = BMP180_REGS else: self.max_sample_time = \ (1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + .575) @@ -221,14 +263,19 @@ def read_calibration_data_bme680(calib_data_1, calib_data_2): self.write_register('CONFIG', (self.iir_filter & 0x07) << 2) # Read out and calculate the trimming parameters - cal_1 = self.read_register('CAL_1', 26) - cal_2 = self.read_register('CAL_2', 16) + if self.chip_type == 'BMP180': + cal_1 = self.read_register('CAL_1', 22) + else: + cal_1 = self.read_register('CAL_1', 26) + cal_2 = self.read_register('CAL_2', 16) if self.chip_type == 'BME280': self.dig = read_calibration_data_bme280(cal_1, cal_2) elif self.chip_type == 'BMP280': self.dig = read_calibration_data_bmp280(cal_1) elif self.chip_type == 'BME680': self.dig = read_calibration_data_bme680(cal_1, cal_2) + elif self.chip_type == 'BMP180': + self.dig = read_calibration_data_bmp180(cal_1) def _sample_bme280(self, eventtime): # Enter forced mode @@ -334,6 +381,43 @@ def data_ready(stat): self._callback(self.mcu.estimated_print_time(measured_time), self.temp) return measured_time + REPORT_TIME + def _sample_bmp180(self, eventtime): + meas = self.chip_registers['CRV_TEMP'] + self.write_register('CTRL_MEAS', meas) + + try: + self.reactor.pause(self.reactor.monotonic() + .01) + data = self.read_register('REG_MSB', 2) + temp_raw = (data[0] << 8) | data[1] + except Exception: + logging.exception("BMP180: Error reading temperature") + self.temp = self.pressure = .0 + return self.reactor.NEVER + + meas = self.chip_registers['CRV_PRES'] | (self.os_pres << 6) + self.write_register('CTRL_MEAS', meas) + + try: + self.reactor.pause(self.reactor.monotonic() + .01) + data = self.read_register('REG_MSB', 3) + pressure_raw = \ + ((data[0] << 16)|(data[1] << 8)|data[2]) >> (8 - self.os_pres) + except Exception: + logging.exception("BMP180: Error reading pressure") + self.temp = self.pressure = .0 + return self.reactor.NEVER + + self.temp = self._compensate_temp_bmp180(temp_raw) + self.pressure = self._compensate_pressure_bmp180(pressure_raw) / 100. + if self.temp < self.min_temp or self.temp > self.max_temp: + self.printer.invoke_shutdown( + "BMP180 temperature %0.1f outside range of %0.1f:%.01f" + % (self.temp, self.min_temp, self.max_temp)) + measured_time = self.reactor.monotonic() + self._callback(self.mcu.estimated_print_time(measured_time), self.temp) + return measured_time + REPORT_TIME + + def _compensate_temp(self, raw_temp): dig = self.dig var1 = ((raw_temp / 16384. - (dig['T1'] / 1024.)) * dig['T2']) @@ -443,6 +527,37 @@ def _calculate_gas_heater_duration(self, duration_ms): return duration_reg + def _compensate_temp_bmp180(self, raw_temp): + dig = self.dig + x1 = (raw_temp - dig['AC6']) * dig['AC5'] / 32768. + x2 = dig['MC'] * 2048 / (x1 + dig['MD']) + b5 = x1 + x2 + self.t_fine = b5 + return (b5 + 8)/16./10. + + def _compensate_pressure_bmp180(self, raw_pressure): + dig = self.dig + b5 = self.t_fine + b6 = b5 - 4000 + x1 = (dig['B2'] * (b6 * b6 / 4096)) / 2048 + x2 = dig['AC2'] * b6 / 2048 + x3 = x1 + x2 + b3 = ((int(dig['AC1'] * 4 + x3) << self.os_pres) + 2) / 4 + x1 = dig['AC3'] * b6 / 8192 + x2 = (dig['B1'] * (b6 * b6 / 4096)) / 65536 + x3 = ((x1 + x2) + 2) / 4 + b4 = dig['AC4'] * (x3 + 32768) / 32768 + b7 = (raw_pressure - b3) * (50000 >> self.os_pres) + if (b7 < 0x80000000): + p = (b7 * 2) / b4 + else: + p = (b7 / b4) * 2 + x1 = (p / 256) * (p / 256) + x1 = (x1 * 3038) / 65536 + x2 = (-7357 * p) / 65536 + p = p + (x1 + x2 + 3791) / 16. + return p + def read_id(self): # read chip id register regs = [BME_CHIP_ID_REG] From 187cc2f1b89e3870d694f8db6a64b116992106b7 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Wed, 1 Nov 2023 20:38:15 -0400 Subject: [PATCH 17/53] configfile: Improve support for python3.12 It seems python3.12 has removed support for readfp() - use read_file() instead. Signed-off-by: Kevin O'Connor --- klippy/configfile.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/klippy/configfile.py b/klippy/configfile.py index e5c0fb20b..f099b5634 100644 --- a/klippy/configfile.py +++ b/klippy/configfile.py @@ -217,7 +217,10 @@ def _parse_config_buffer(self, buffer, filename, fileconfig): data = '\n'.join(buffer) del buffer[:] sbuffer = io.StringIO(data) - fileconfig.readfp(sbuffer, filename) + if sys.version_info.major >= 3: + fileconfig.read_file(sbuffer, filename) + else: + fileconfig.readfp(sbuffer, filename) def _resolve_include(self, source_filename, include_spec, fileconfig, visited): dirname = os.path.dirname(source_filename) From bb4711c5d31e8159945f945c662e6668059a174f Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Wed, 8 Nov 2023 13:46:13 -0500 Subject: [PATCH 18/53] tmc5160: Increase maximum current error check It's possible to build and configure tmc5160 drivers with external mosfets that support more than 3 amps. The actual maximum for tmc5160 drivers is dependent on how the board is wired and the mosfets used. Increase the error check to 10 amps. This error checking is primarily intended to catch "obvious misconfigurations" (eg, specifying milli-amps instead of amps), and the new value of 10 amps should suffice for this task. Signed-off-by: Kevin O'Connor --- klippy/extras/tmc5160.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/tmc5160.py b/klippy/extras/tmc5160.py index b82b5fecd..7ff47abf9 100644 --- a/klippy/extras/tmc5160.py +++ b/klippy/extras/tmc5160.py @@ -257,7 +257,7 @@ ###################################################################### VREF = 0.325 -MAX_CURRENT = 3.000 +MAX_CURRENT = 10.000 # Maximum dependent on board, but 10 is safe sanity check class TMC5160CurrentHelper: def __init__(self, config, mcu_tmc): From ea2f6bc0f544132738c7f052ffcc586fa884a19a Mon Sep 17 00:00:00 2001 From: Andrei Ignat Date: Wed, 22 Nov 2023 17:53:38 +0100 Subject: [PATCH 19/53] exclude_object: Don't use gcmd.respond_error() (#6407) gcmd.respond_error() has been deprecated: 61524542d20e50c4866836d6ed23ca03521ffb15 Signed-off-by: Andrei Ignat --- klippy/extras/exclude_object.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/exclude_object.py b/klippy/extras/exclude_object.py index 0a68d9b5e..194012796 100644 --- a/klippy/extras/exclude_object.py +++ b/klippy/extras/exclude_object.py @@ -234,7 +234,7 @@ def cmd_EXCLUDE_OBJECT(self, gcmd): elif current: if not self.current_object: - gcmd.respond_error('There is no current object to cancel') + raise self.gcode.error('There is no current object to cancel') else: self._exclude_object(self.current_object) From 03f69cd81a129456a66e2deb03bc8b137504c179 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 20 Nov 2023 19:43:36 -0500 Subject: [PATCH 20/53] tmc: Query latest value during _init_registers() The set_register() code may block, and it therefore may be possible that the loop in _init_registers() could occur in parallel with other updates. That could result in a "OrderedDict mutated during iteration" error. Avoid the error by querying the latest value during each iteration of the loop. Signed-off-by: Kevin O'Connor --- klippy/extras/tmc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index c3ece070d..8143882ab 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -262,7 +262,8 @@ def __init__(self, config, mcu_tmc, current_helper): desc=self.cmd_SET_TMC_CURRENT_help) def _init_registers(self, print_time=None): # Send registers - for reg_name, val in self.fields.registers.items(): + for reg_name in list(self.fields.registers.keys()): + val = self.fields.registers[reg_name] # Val may change during loop self.mcu_tmc.set_register(reg_name, val, print_time) cmd_INIT_TMC_help = "Initialize TMC stepper driver registers" def cmd_INIT_TMC(self, gcmd): From 38221df83ada0a1a9731b18b0226a02726b1c236 Mon Sep 17 00:00:00 2001 From: CODeRUS Date: Wed, 29 Nov 2023 17:31:49 +0100 Subject: [PATCH 21/53] avr: enable small code size options for 328 and 328p (#6411) Signed-off-by: Andrei Kozhevnikov --- src/avr/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/avr/Kconfig b/src/avr/Kconfig index ac3ffa2ab..f1ad54395 100644 --- a/src/avr/Kconfig +++ b/src/avr/Kconfig @@ -11,7 +11,7 @@ config AVR_SELECT select HAVE_GPIO_I2C select HAVE_GPIO_HARD_PWM select HAVE_STRICT_TIMING - select HAVE_LIMITED_CODE_SIZE if MACH_atmega168 + select HAVE_LIMITED_CODE_SIZE if MACH_atmega168 || MACH_atmega328 || MACH_atmega328p config BOARD_DIRECTORY string From 795ce490a05b2ebae724cff634317f38bc38d721 Mon Sep 17 00:00:00 2001 From: Thijs Triemstra Date: Mon, 4 Dec 2023 03:10:59 +0100 Subject: [PATCH 22/53] doc: updates to Installation.md (#6398) omit rpi device version nrs rpi2 or newer Signed-off-by: Thijs Triemstra --- docs/Installation.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 5e97436fe..004d963a0 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -2,7 +2,7 @@ These instructions assume the software will run on a Raspberry Pi computer in conjunction with OctoPrint. It is recommended that a -Raspberry Pi 2, 3, or 4 computer be used as the host machine (see the +Raspberry Pi 2 (or later) be used as the host machine (see the [FAQ](FAQ.md#can-i-run-klipper-on-something-other-than-a-raspberry-pi-3) for other machines). @@ -50,7 +50,7 @@ using a Linux or MacOS desktop, then the "ssh" software should already be installed on the desktop. There are free ssh clients available for other desktops (eg, [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/)). Use the -ssh utility to connect to the Raspberry Pi (ssh pi@octopi -- password +ssh utility to connect to the Raspberry Pi (`ssh pi@octopi` -- password is "raspberry") and run the following commands: ``` @@ -135,18 +135,18 @@ web page and then configure the following items: Navigate to the Settings tab (the wrench icon at the top of the page). Under "Serial Connection" in "Additional serial ports" add -"/tmp/printer". Then click "Save". +`/tmp/printer`. Then click "Save". Enter the Settings tab again and under "Serial Connection" change the -"Serial Port" setting to "/tmp/printer". +"Serial Port" setting to `/tmp/printer`. In the Settings tab, navigate to the "Behavior" sub-tab and select the "Cancel any ongoing prints but stay connected to the printer" option. Click "Save". From the main page, under the "Connection" section (at the top left of -the page) make sure the "Serial Port" is set to "/tmp/printer" and -click "Connect". (If "/tmp/printer" is not an available selection then +the page) make sure the "Serial Port" is set to `/tmp/printer` and +click "Connect". (If `/tmp/printer` is not an available selection then try reloading the page.) Once connected, navigate to the "Terminal" tab and type "status" @@ -165,8 +165,8 @@ Arguably the easiest way to set the Klipper configuration file is to use a desktop editor that supports editing files over the "scp" and/or "sftp" protocols. There are freely available tools that support this (eg, Notepad++, WinSCP, and Cyberduck). Load the printer config file -in the editor and then save it as a file named "printer.cfg" in the -home directory of the pi user (ie, /home/pi/printer.cfg). +in the editor and then save it as a file named `printer.cfg` in the +home directory of the pi user (ie, `/home/pi/printer.cfg`). Alternatively, one can also copy and edit the file directly on the Raspberry Pi via ssh. That may look something like the following (be From 05d5451347e665e211662009837ee861a62dc372 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Sun, 3 Dec 2023 17:56:22 -0500 Subject: [PATCH 23/53] docs: Improve max_accel documentation in Config_Reference.md Note that the max_accel parameter is the actual acceleration used in most movements. Note that the accel/velocity limits can be changed using the SET_VELOCITY_LIMIT command. Signed-off-by: Kevin O'Connor --- docs/Config_Reference.md | 18 +++++++++++++----- docs/G-Codes.md | 7 +++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index cc78ddbe3..bcccebe06 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -88,16 +88,22 @@ kinematics: # deltesian, polar, winch, or none. This parameter must be specified. max_velocity: # Maximum velocity (in mm/s) of the toolhead (relative to the -# print). This parameter must be specified. +# print). This value may be changed at runtime using the +# SET_VELOCITY_LIMIT command. This parameter must be specified. max_accel: # Maximum acceleration (in mm/s^2) of the toolhead (relative to the -# print). This parameter must be specified. +# print). Although this parameter is described as a "maximum" +# acceleration, in practice most moves that accelerate or decelerate +# will do so at the rate specified here. The value specified here +# may be changed at runtime using the SET_VELOCITY_LIMIT command. +# This parameter must be specified. #max_accel_to_decel: # A pseudo acceleration (in mm/s^2) controlling how fast the # toolhead may go from acceleration to deceleration. It is used to # reduce the top speed of short zig-zag moves (and thus reduce -# printer vibration from these moves). The default is half of -# max_accel. +# printer vibration from these moves). The value specified here may +# be changed at runtime using the SET_VELOCITY_LIMIT command. The +# default is half of max_accel. #square_corner_velocity: 5.0 # The maximum velocity (in mm/s) that the toolhead may travel a 90 # degree corner at. A non-zero value can reduce changes in extruder @@ -107,7 +113,9 @@ max_accel: # larger than 90 degrees will have a higher cornering velocity while # corners with angles less than 90 degrees will have a lower # cornering velocity. If this is set to zero then the toolhead will -# decelerate to zero at each corner. The default is 5mm/s. +# decelerate to zero at each corner. The value specified here may be +# changed at runtime using the SET_VELOCITY_LIMIT command. The +# default is 5mm/s. ``` ### [stepper] diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 6d492c251..8c70609f1 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -1281,8 +1281,11 @@ The toolhead module is automatically loaded. #### SET_VELOCITY_LIMIT `SET_VELOCITY_LIMIT [VELOCITY=] [ACCEL=] -[ACCEL_TO_DECEL=] [SQUARE_CORNER_VELOCITY=]`: Modify the -printer's velocity limits. +[ACCEL_TO_DECEL=] [SQUARE_CORNER_VELOCITY=]`: This +command can alter the velocity limits that were specified in the +printer config file. See the +[printer config section](Config_Reference.md#printer) for a +description of each parameter. ### [tuning_tower] From fc102edc242a7c5e9b8c6415f447bf1a6a022ea7 Mon Sep 17 00:00:00 2001 From: Will Puckett Date: Tue, 5 Dec 2023 14:44:51 -0800 Subject: [PATCH 24/53] hard_pwm: Add pin defs for STM32F070 and STM32F072 (#6409) Define hard_pwm pins for STM32F070 and STM32F072, and update KConfig accordingly. Signed-off-by: Will Puckett --- src/stm32/Kconfig | 2 +- src/stm32/hard_pwm.c | 65 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/stm32/Kconfig b/src/stm32/Kconfig index 7f50ea3ca..c06bb6ffb 100644 --- a/src/stm32/Kconfig +++ b/src/stm32/Kconfig @@ -10,7 +10,7 @@ config STM32_SELECT select HAVE_GPIO_I2C if !MACH_STM32F031 select HAVE_GPIO_SPI if !MACH_STM32F031 select HAVE_GPIO_SDIO if MACH_STM32F4 - select HAVE_GPIO_HARD_PWM if MACH_STM32F1 || MACH_STM32F4 || MACH_STM32F7 || MACH_STM32G0 || MACH_STM32H7 + select HAVE_GPIO_HARD_PWM if MACH_STM32F070 || MACH_STM32F072 || MACH_STM32F1 || MACH_STM32F4 || MACH_STM32F7 || MACH_STM32G0 || MACH_STM32H7 select HAVE_STRICT_TIMING select HAVE_CHIPID select HAVE_STEPPER_BOTH_EDGE diff --git a/src/stm32/hard_pwm.c b/src/stm32/hard_pwm.c index ad30051f8..bbdd18b21 100644 --- a/src/stm32/hard_pwm.c +++ b/src/stm32/hard_pwm.c @@ -20,7 +20,70 @@ struct gpio_pwm_info { }; static const struct gpio_pwm_info pwm_regs[] = { -#if CONFIG_MACH_STM32F1 +#if CONFIG_MACH_STM32F0 + #if CONFIG_MACH_STM32F070 + {TIM15, GPIO('A', 2), 1, GPIO_FUNCTION(0)}, + {TIM15, GPIO('A', 3), 2, GPIO_FUNCTION(0)}, + {TIM14, GPIO('A', 4), 1, GPIO_FUNCTION(4)}, + {TIM3, GPIO('A', 6), 1, GPIO_FUNCTION(1)}, + {TIM3, GPIO('A', 7), 2, GPIO_FUNCTION(1)}, + {TIM1, GPIO('A', 8), 1, GPIO_FUNCTION(2)}, + {TIM1, GPIO('A', 9), 2, GPIO_FUNCTION(2)}, + {TIM1, GPIO('A', 10), 3, GPIO_FUNCTION(2)}, + {TIM1, GPIO('A', 11), 4, GPIO_FUNCTION(2)}, + {TIM3, GPIO('B', 0), 3, GPIO_FUNCTION(1)}, + {TIM3, GPIO('B', 1), 4, GPIO_FUNCTION(1)}, + {TIM3, GPIO('B', 4), 1, GPIO_FUNCTION(1)}, + {TIM3, GPIO('B', 5), 2, GPIO_FUNCTION(1)}, + {TIM16, GPIO('B', 8), 1, GPIO_FUNCTION(2)}, + {TIM17, GPIO('B', 9), 1, GPIO_FUNCTION(2)}, + {TIM15, GPIO('B', 14), 1, GPIO_FUNCTION(1)}, + {TIM15, GPIO('B', 15), 2, GPIO_FUNCTION(1)}, + {TIM3, GPIO('C', 6), 1, GPIO_FUNCTION(0)}, + {TIM3, GPIO('C', 7), 2, GPIO_FUNCTION(0)}, + {TIM3, GPIO('C', 8), 3, GPIO_FUNCTION(0)}, + {TIM3, GPIO('C', 9), 4, GPIO_FUNCTION(0)} + #endif + #if CONFIG_MACH_STM32F072 + {TIM2, GPIO('A', 1), 2, GPIO_FUNCTION(2)}, + {TIM2, GPIO('A', 2), 3, GPIO_FUNCTION(2)}, + {TIM2, GPIO('A', 3), 4, GPIO_FUNCTION(2)}, + {TIM14, GPIO('A', 4), 1, GPIO_FUNCTION(4)}, + {TIM3, GPIO('A', 6), 1, GPIO_FUNCTION(1)}, + {TIM3, GPIO('A', 7), 2, GPIO_FUNCTION(1)}, + {TIM1, GPIO('A', 8), 1, GPIO_FUNCTION(2)}, + {TIM1, GPIO('A', 9), 2, GPIO_FUNCTION(2)}, + {TIM1, GPIO('A', 10), 3, GPIO_FUNCTION(2)}, + {TIM1, GPIO('A', 11), 4, GPIO_FUNCTION(2)}, + {TIM3, GPIO('B', 0), 3, GPIO_FUNCTION(1)}, + {TIM3, GPIO('B', 1), 4, GPIO_FUNCTION(1)}, + {TIM2, GPIO('B', 3), 2, GPIO_FUNCTION(2)}, + {TIM3, GPIO('B', 4), 1, GPIO_FUNCTION(1)}, + {TIM3, GPIO('B', 5), 2, GPIO_FUNCTION(1)}, + {TIM16, GPIO('B', 8), 1, GPIO_FUNCTION(2)}, + {TIM17, GPIO('B', 9), 1, GPIO_FUNCTION(2)}, + {TIM2, GPIO('B', 10), 3, GPIO_FUNCTION(2)}, + {TIM2, GPIO('B', 11), 4, GPIO_FUNCTION(2)}, + {TIM15, GPIO('B', 14), 1, GPIO_FUNCTION(1)}, + {TIM15, GPIO('B', 15), 2, GPIO_FUNCTION(1)}, + {TIM3, GPIO('C', 6), 1, GPIO_FUNCTION(0)}, + {TIM3, GPIO('C', 7), 2, GPIO_FUNCTION(0)}, + {TIM3, GPIO('C', 8), 3, GPIO_FUNCTION(0)}, + {TIM3, GPIO('C', 9), 4, GPIO_FUNCTION(0)}, + {TIM16, GPIO('E', 0), 1, GPIO_FUNCTION(0)}, + {TIM17, GPIO('E', 1), 1, GPIO_FUNCTION(0)}, + {TIM3, GPIO('E', 3), 1, GPIO_FUNCTION(0)}, + {TIM3, GPIO('E', 4), 2, GPIO_FUNCTION(0)}, + {TIM3, GPIO('E', 5), 3, GPIO_FUNCTION(0)}, + {TIM3, GPIO('E', 6), 4, GPIO_FUNCTION(0)}, + {TIM1, GPIO('E', 9), 1, GPIO_FUNCTION(0)}, + {TIM1, GPIO('E', 11), 2, GPIO_FUNCTION(0)}, + {TIM1, GPIO('E', 13), 3, GPIO_FUNCTION(0)}, + {TIM1, GPIO('E', 14), 4, GPIO_FUNCTION(0)}, + {TIM15, GPIO('F', 9), 1, GPIO_FUNCTION(0)}, + {TIM15, GPIO('F', 10), 2, GPIO_FUNCTION(0)} + #endif +#elif CONFIG_MACH_STM32F1 {TIM2, GPIO('A', 0), 1, GPIO_FUNCTION(2)}, {TIM2, GPIO('A', 1), 2, GPIO_FUNCTION(2)}, {TIM2, GPIO('A', 2), 3, GPIO_FUNCTION(2)}, From 0ccf5f8e479a9801b065034e07d974e694918772 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Tue, 5 Dec 2023 17:55:11 -0500 Subject: [PATCH 25/53] github: Temporarily disable close_reviewer_needed automation Temporarily disable the closing of PRs marked as "reviewer needed". Signed-off-by: Kevin O'Connor --- .github/workflows/stale-issue-bot.yaml | 96 +++++++++++++------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/.github/workflows/stale-issue-bot.yaml b/.github/workflows/stale-issue-bot.yaml index 2f05abc1f..b23511615 100644 --- a/.github/workflows/stale-issue-bot.yaml +++ b/.github/workflows/stale-issue-bot.yaml @@ -62,54 +62,54 @@ jobs: state: 'closed' }); } - # Close tickets marked with "reviewer needed" label for 2+ weeks - close_reviewer_needed: - if: github.repository == 'Klipper3d/klipper' - runs-on: ubuntu-latest - steps: - - uses: actions/github-script@v6 - with: - script: | - const issues = await github.rest.issues.listForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - labels: 'reviewer needed', - assignee: 'none', - per_page: 100, - page: 1 - }); - msg = "Unfortunately a reviewer has not assigned themselves to" - + " this GitHub Pull Request and it is therefore being" - + " closed. It is a good idea to move" - + " further discussion to the [Klipper Discourse]" - + "(https://www.klipper3d.org/Contact.html#discourse-forum)" - + " server. Reviewers can reach out on that forum to let you" - + " know if they are interested and when they are available." - + "\n\n" - + "Best regards,\n" - + "~ Your friendly GitIssueBot" - + "\n\n" - + "PS: I'm just an automated script, not a human being."; - const expireMillis = 1000 * 60 * 60 * 24 * 14; - const curtime = new Date().getTime(); - for (const issue of issues.data.values()) { - const updatetime = new Date(issue.updated_at).getTime(); - if (curtime < updatetime + expireMillis) - continue; - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - body: msg - }); - await github.rest.issues.update({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - state: 'closed' - }); - } +# # Close tickets marked with "reviewer needed" label for 2+ weeks +# close_reviewer_needed: +# if: github.repository == 'Klipper3d/klipper' +# runs-on: ubuntu-latest +# steps: +# - uses: actions/github-script@v6 +# with: +# script: | +# const issues = await github.rest.issues.listForRepo({ +# owner: context.repo.owner, +# repo: context.repo.repo, +# state: 'open', +# labels: 'reviewer needed', +# assignee: 'none', +# per_page: 100, +# page: 1 +# }); +# msg = "Unfortunately a reviewer has not assigned themselves to" +# + " this GitHub Pull Request and it is therefore being" +# + " closed. It is a good idea to move" +# + " further discussion to the [Klipper Discourse]" +# + "(https://www.klipper3d.org/Contact.html#discourse-forum)" +# + " server. Reviewers can reach out on that forum to let you" +# + " know if they are interested and when they are available." +# + "\n\n" +# + "Best regards,\n" +# + "~ Your friendly GitIssueBot" +# + "\n\n" +# + "PS: I'm just an automated script, not a human being."; +# const expireMillis = 1000 * 60 * 60 * 24 * 14; +# const curtime = new Date().getTime(); +# for (const issue of issues.data.values()) { +# const updatetime = new Date(issue.updated_at).getTime(); +# if (curtime < updatetime + expireMillis) +# continue; +# await github.rest.issues.createComment({ +# owner: context.repo.owner, +# repo: context.repo.repo, +# issue_number: issue.number, +# body: msg +# }); +# await github.rest.issues.update({ +# owner: context.repo.owner, +# repo: context.repo.repo, +# issue_number: issue.number, +# state: 'closed' +# }); +# } # Mark unassigned PRs that are idle for 2 weeks mark_reviewer_needed: if: github.repository == 'Klipper3d/klipper' From 7ed3c679dc3cadb8cc853dbc30573774ee797597 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Tue, 5 Dec 2023 21:53:41 +0100 Subject: [PATCH 26/53] config: Add LDO Leviathan v1.2 generic config Signed-off-by: Stefan Dej --- config/generic-ldo-leviathan-v1.2.cfg | 241 ++++++++++++++++++++++++++ test/klippy/printers.test | 1 + 2 files changed, 242 insertions(+) create mode 100644 config/generic-ldo-leviathan-v1.2.cfg diff --git a/config/generic-ldo-leviathan-v1.2.cfg b/config/generic-ldo-leviathan-v1.2.cfg new file mode 100644 index 000000000..61fc1cd71 --- /dev/null +++ b/config/generic-ldo-leviathan-v1.2.cfg @@ -0,0 +1,241 @@ +# This file contains common pin mappings for the LDO Leviathan v1.2. + +# To use this config, during "make menuconfig", select "Enable +# low-level configuration options", select the STM32F446 micro-controller, +# select a "32KiB bootloader", and select a "12Mhz crystal". + +# See docs/Config_Reference.md for a description of parameters. + +# HV-STEPPER-0 +[stepper_x] +step_pin: PB10 +dir_pin: PB11 +enable_pin: !PG0 +microsteps: 32 +rotation_distance: 40 +endstop_pin: PC1 # X-ENDSTOP +position_endstop: 0 +position_max: 200 +homing_speed: 50 + +[tmc5160 stepper_x] +spi_bus: spi4 +cs_pin: PE15 +#diag0_pin: PG1 +interpolate: False +sense_resistor: 0.075 +run_current: 0.8 +stealthchop_threshold: 0 + +# HV-STEPPER-1 +[stepper_y] +step_pin: PF15 +dir_pin: PF14 +enable_pin: !PE9 +microsteps: 32 +rotation_distance: 40 +endstop_pin: PC2 # Y-ENDSTOP +position_endstop: 0 +position_max: 200 +homing_speed: 50 + +[tmc5160 stepper_y] +spi_bus: spi4 +cs_pin: PE11 +#diag0_pin: PE10 +interpolate: False +sense_resistor: 0.075 +run_current: 0.8 +stealthchop_threshold: 0 + +# STEPPER-0 +[stepper_z] +step_pin: PD4 +dir_pin: PD3 +enable_pin: !PD7 +microsteps: 32 +rotation_distance: 8 +endstop_pin: PC3 # Z-ENDSTOP +position_endstop: 0 +position_max: 200 + +[tmc2209 stepper_z] +uart_pin: PD5 +#diag_pin: PD6 +interpolate: False +run_current: 0.6 +stealthchop_threshold: 999999 + +# The Leviathan was developed for Voron printers. It therefore has several +# steppers for the z-axes, but only one heater for one extruder. + +# STEPPER-1 +#[stepper_z1] +#step_pin: PC12 +#dir_pin: PC11 +#enable_pin: !PD2 +#microsteps: 32 +#rotation_distance: 8 +# +#[tmc2209 stepper_z1] +#uart_pin: PD5 +##diag_pin: PD6 +#interpolate: False +#run_current: 0.6 +#stealthchop_threshold: 999999 + +# STEPPER-2 +#[stepper_z2] +#step_pin: PC9 +#dir_pin: PC8 +#enable_pin: !PC10 +#microsteps: 32 +#rotation_distance: 8 +# +#[tmc2209 stepper_z2] +#uart_pin: PA8 +##diag_pin: PA15 +#interpolate: False +#run_current: 0.6 +#stealthchop_threshold: 999999 + +# STEPPER-3 +#[stepper_z3] +#step_pin: PG7 +#dir_pin: PG6 +#enable_pin: !PC7 +#microsteps: 32 +#rotation_distance: 8 +# +#[tmc2209 stepper_z2] +#uart_pin: PG8 +##diag_pin: PC6 +#interpolate: False +#run_current: 0.6 +#stealthchop_threshold: 999999 + +# STEPPER-4 +[extruder] +step_pin: PD10 +dir_pin: PD9 +enable_pin: !PD13 +microsteps: 32 +rotation_distance: 22.67 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +heater_pin: PG10 # HEATER +sensor_pin: PA2 # TH1 +sensor_type: ATC Semitec 104NT-4-R025H42G +pullup_resistor: 2200 +control: pid +pid_Kp: 36.787 +pid_Ki: 4.716 +pid_Kd: 71.735 +min_temp: 0 +max_temp: 250 + +[tmc2209 stepper_z] +uart_pin: PD11 +#diag_pin: PD12 +interpolate: False +run_current: 0.5 +stealthchop_threshold: 0 + +#[filament_switch_sensor material_0] +#switch_pin: PC0 # FILAMENT-SENSOR + +[heater_bed] +heater_pin: PG11 # HEATBED +sensor_pin: PA1 # TH0 +sensor_type: ATC Semitec 104GT-2 +pullup_resistor: 2200 +control: pid +pid_kp: 56.723 +pid_ki: 5.561 +pid_kd: 144.642 +min_temp: 0 +max_temp: 130 + +[fan] +pin: PB7 # FAN0 +#tachometer_pin: PB0 + +#[heater_fan fan1] +#pin: PB3 +#tachometer_pin: PB4 + +#[heater_fan fan2] +#pin: PF7 +#tachometer_pin: PF6 + +#[controller_fan fan3] +#pin: PF9 +#tachometer_pin: PF8 + +[mcu] +serial: /dev/serial/by-id/usb-Klipper_Klipper_firmware_12345-if00 +# CAN bus is also available on this board + +[printer] +kinematics: cartesian +max_velocity: 300 +max_accel: 3000 +max_z_velocity: 5 +max_z_accel: 100 + +[board_pins] +aliases: + # EXP1 header + EXP1_1=PG9, EXP1_2=PG12, + EXP1_3=PG13, EXP1_4=PG14, + EXP1_5=PC13, EXP1_6=PC14, + EXP1_7=PC15, EXP1_8=PF0, + EXP1_9=, EXP1_10=<5V>, + + # EXP2 header + EXP2_1=PA6, EXP2_2=PA5, + EXP2_3=PE2, EXP2_4=PE4, + EXP2_5=PE3, EXP2_6=PA7, + EXP2_7=PE5, EXP2_8=, + EXP2_9=, EXP2_10=PE4, + + # See the sample-lcd.cfg file for definitions of common LCD displays. + + # EXTENSION PORT + EXP3_1=<5V>, EXP3_2=<5V>, # max. 0.5A + EXP3_3=, EXP3_4=, + EXP3_5=<3.3V>, EXP3_6=<3.3V>, # max. 0.5A + EXP3_7=PF5, EXP3_8=PF4, + EXP3_9=PF3, EXP3_10=PF2, + EXP3_11=PC4, EXP3_12=PC5, # EXP3_11 and EXP3_12 are ADC inputs + EXP3_13=PB0, EXP3_14=PB1, # EXP3_13 and EXP3_14 are ADC inputs + EXP3_15=PE8, EXP3_16=PE7, # EXP3_15 is UART5_TX, EXP3_16 is UART5_RX + EXP3_17=PG5, EXP3_18=PG4, + EXP3_19=PG3, EXP3_20=PG2, + EXP3_21=PD15, EXP3_22=PD14, + EXP3_23=PB15, EXP3_24=PB14, # EXP3_23 is SPI2_MOSI + # EXP3_24 is SPI2_MISO + EXP3_25=PB13, EXP3_26=PB12, # EXP3_25 is SPI2_SCK + CAN2_TX + # EXP3_26 is SPI2_CS + CAN2_RX + EXP3_27=, EXP3_28=, + EXP3_29=<24V>, EXP3_30=<24V>, # max. 0.5A + +#[probe] +#sensor_pin: PF1 # Z-PROBE +#z_offset: 0 + +#[led my_led] +#white_pin: PE6 # LED-Strip + +#[neopixel my_neopixel] +#pin: PF10 # NEOPIXEL + +#[temperature_sensor TH2] +#sensor_type: ATC Semitec 104GT-2 +#sensor_pin: PA0 # TH2 +#pullup_resistor: 2200 + +#[temperature_sensor TH3] +#sensor_type: ATC Semitec 104GT-2 +#sensor_pin: PA3 # TH3 +#pullup_resistor: 2200 diff --git a/test/klippy/printers.test b/test/klippy/printers.test index 2ab96acc8..9edc57506 100644 --- a/test/klippy/printers.test +++ b/test/klippy/printers.test @@ -238,6 +238,7 @@ CONFIG ../../config/generic-bigtreetech-octopus-pro-v1.0.cfg CONFIG ../../config/generic-fysetc-s6.cfg CONFIG ../../config/generic-fysetc-s6-v2.cfg CONFIG ../../config/generic-fysetc-spider.cfg +CONFIG ../../config/generic-ldo-leviathan-v1.2.cfg CONFIG ../../config/generic-mks-rumba32-v1.0.cfg CONFIG ../../config/printer-ratrig-v-minion-2021.cfg From d929be487b169111e78e62c9bec1b2fb2d065393 Mon Sep 17 00:00:00 2001 From: Bassam <61985779+bassamanator@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:56:12 -0500 Subject: [PATCH 27/53] config: Update printer-sovol-sv06-2022.cfg to stock (#6416) This printer is advertised as having a `220*220*250mm build volume`. Signed-off-by: Bassam Husain --- config/printer-sovol-sv06-2022.cfg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/printer-sovol-sv06-2022.cfg b/config/printer-sovol-sv06-2022.cfg index 78a627c7d..9b476894b 100644 --- a/config/printer-sovol-sv06-2022.cfg +++ b/config/printer-sovol-sv06-2022.cfg @@ -28,7 +28,7 @@ microsteps: 16 rotation_distance: 40 endstop_pin: tmc2209_stepper_x:virtual_endstop position_endstop: 0 -position_max: 225 +position_max: 220 homing_speed: 40 homing_retract_dist: 0 @@ -50,7 +50,7 @@ microsteps: 16 rotation_distance: 40 endstop_pin: tmc2209_stepper_y:virtual_endstop position_endstop: 0 -position_max: 225 +position_max: 220 homing_speed: 40 homing_retract_dist: 0 @@ -72,7 +72,7 @@ microsteps: 16 rotation_distance: 4 endstop_pin: probe:z_virtual_endstop position_min: -4 -position_max: 261 +position_max: 250 homing_speed: 4 [tmc2209 stepper_z] From 5bd32e2984b5d34ab54267aed3f078bd990d33b5 Mon Sep 17 00:00:00 2001 From: Bassam <61985779+bassamanator@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:57:10 -0500 Subject: [PATCH 28/53] config: Update printer-sovol-sv06-2022.cfg x_offset (#6417) Signed-off-by: Bassam Husain --- config/printer-sovol-sv06-2022.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/printer-sovol-sv06-2022.cfg b/config/printer-sovol-sv06-2022.cfg index 9b476894b..e9f179eea 100644 --- a/config/printer-sovol-sv06-2022.cfg +++ b/config/printer-sovol-sv06-2022.cfg @@ -127,7 +127,7 @@ pin: PA0 [probe] pin: PB1 -x_offset: 28 +x_offset: 27 y_offset: -20 z_offset: 0 samples: 2 From bafb126abd77edd0cb2e5ae3b5d99ff83272594c Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Sun, 26 Nov 2023 19:01:24 -0500 Subject: [PATCH 29/53] toolhead: Remove undocumented buffer management config parameters These internal low-level config parameters were never documented. Going forward, developers may modify them by altering the internal settings in toolhead.py. Signed-off-by: Kevin O'Connor --- docs/Config_Changes.md | 6 ++++++ klippy/toolhead.py | 33 ++++++++++++++------------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/docs/Config_Changes.md b/docs/Config_Changes.md index 6d83238d0..3606c6d00 100644 --- a/docs/Config_Changes.md +++ b/docs/Config_Changes.md @@ -8,6 +8,12 @@ All dates in this document are approximate. ## Changes +20231207: Several undocumented config parameters in the `[printer]` +config section have been removed (the buffer_time_low, +buffer_time_high, buffer_time_start, and move_flush_time parameters). + +20231110: Klipper v0.12.0 released. + 20230826: If `safe_distance` is set or calculated to be 0 in `[dual_carriage]`, the carriages proximity checks will be disabled as per documentation. A user may wish to configure `safe_distance` explicitly to prevent accidental crashes diff --git a/klippy/toolhead.py b/klippy/toolhead.py index d8b938257..58fa429f2 100644 --- a/klippy/toolhead.py +++ b/klippy/toolhead.py @@ -186,8 +186,12 @@ def add_move(self, move): # Enough moves have been queued to reach the target flush time. self.flush(lazy=True) +BUFFER_TIME_LOW = 1.0 +BUFFER_TIME_HIGH = 2.0 +BUFFER_TIME_START = 0.250 MIN_KIN_TIME = 0.100 MOVE_BATCH_TIME = 0.500 +STEPCOMPRESS_FLUSH_TIME = 0.050 SDS_CHECK_TIME = 0.001 # step+dir+step filter in stepcompress.c DRIP_SEGMENT_TIME = 0.050 @@ -221,19 +225,11 @@ def __init__(self, config): self.junction_deviation = 0. self._calc_junction_deviation() # Print time tracking - self.buffer_time_low = config.getfloat( - 'buffer_time_low', 1.000, above=0.) - self.buffer_time_high = config.getfloat( - 'buffer_time_high', 2.000, above=self.buffer_time_low) - self.buffer_time_start = config.getfloat( - 'buffer_time_start', 0.250, above=0.) - self.move_flush_time = config.getfloat( - 'move_flush_time', 0.050, above=0.) self.print_time = 0. self.special_queuing_state = "Flushed" self.need_check_stall = -1. self.flush_timer = self.reactor.register_timer(self._flush_handler) - self.move_queue.set_flush_time(self.buffer_time_high) + self.move_queue.set_flush_time(BUFFER_TIME_HIGH) self.idle_flush_print_time = 0. self.print_stall = 0 self.drip_completion = None @@ -288,7 +284,7 @@ def _update_move_time(self, next_print_time): free_time = max(fft, sg_flush_time - kin_flush_delay) self.trapq_finalize_moves(self.trapq, free_time) self.extruder.update_move_time(free_time) - mcu_flush_time = max(fft, sg_flush_time - self.move_flush_time) + mcu_flush_time = max(fft, sg_flush_time - STEPCOMPRESS_FLUSH_TIME) for m in self.all_mcus: m.flush_moves(mcu_flush_time) if self.print_time >= next_print_time: @@ -298,7 +294,7 @@ def _calc_print_time(self): est_print_time = self.mcu.estimated_print_time(curtime) kin_time = max(est_print_time + MIN_KIN_TIME, self.force_flush_time) kin_time += self.kin_flush_delay - min_print_time = max(est_print_time + self.buffer_time_start, kin_time) + min_print_time = max(est_print_time + BUFFER_TIME_START, kin_time) if min_print_time > self.print_time: self.print_time = min_print_time self.printer.send_event("toolhead:sync_print_time", @@ -339,7 +335,7 @@ def flush_step_generation(self): self.special_queuing_state = "Flushed" self.need_check_stall = -1. self.reactor.update_timer(self.flush_timer, self.reactor.NEVER) - self.move_queue.set_flush_time(self.buffer_time_high) + self.move_queue.set_flush_time(BUFFER_TIME_HIGH) self.idle_flush_print_time = 0. # Determine actual last "itersolve" flush time lastf = self.print_time - self.kin_flush_delay @@ -377,7 +373,7 @@ def _check_stall(self): while 1: est_print_time = self.mcu.estimated_print_time(eventtime) buffer_time = self.print_time - est_print_time - stall_time = buffer_time - self.buffer_time_high + stall_time = buffer_time - BUFFER_TIME_HIGH if stall_time <= 0.: break if not self.can_pause: @@ -386,15 +382,14 @@ def _check_stall(self): eventtime = self.reactor.pause(eventtime + min(1., stall_time)) if not self.special_queuing_state: # In main state - defer stall checking until needed - self.need_check_stall = (est_print_time + self.buffer_time_high - + 0.100) + self.need_check_stall = est_print_time + BUFFER_TIME_HIGH + 0.100 def _flush_handler(self, eventtime): try: print_time = self.print_time buffer_time = print_time - self.mcu.estimated_print_time(eventtime) - if buffer_time > self.buffer_time_low: + if buffer_time > BUFFER_TIME_LOW: # Running normally - reschedule check - return eventtime + buffer_time - self.buffer_time_low + return eventtime + buffer_time - BUFFER_TIME_LOW # Under ran low buffer mark - flush lookahead queue self.flush_step_generation() if print_time != self.print_time: @@ -452,7 +447,7 @@ def get_extruder(self): return self.extruder # Homing "drip move" handling def _update_drip_move_time(self, next_print_time): - flush_delay = DRIP_TIME + self.move_flush_time + self.kin_flush_delay + flush_delay = DRIP_TIME + STEPCOMPRESS_FLUSH_TIME + self.kin_flush_delay while self.print_time < next_print_time: if self.drip_completion.test(): raise DripModeEndSignal() @@ -472,7 +467,7 @@ def drip_move(self, newpos, speed, drip_completion): self.special_queuing_state = "Drip" self.need_check_stall = self.reactor.NEVER self.reactor.update_timer(self.flush_timer, self.reactor.NEVER) - self.move_queue.set_flush_time(self.buffer_time_high) + self.move_queue.set_flush_time(BUFFER_TIME_HIGH) self.idle_flush_print_time = 0. self.drip_completion = drip_completion # Submit move From 93cd8834f373a4a6fc0ad679575137f8b2e41da7 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Sun, 26 Nov 2023 19:05:36 -0500 Subject: [PATCH 30/53] toolhead: Clarify internal toolhead "stall" and "pause" naming Clarify the internal naming to make a more clear distinction between "stalling" (input not coming fast enough) and "pausing" (the need to hold up reading of input to avoid buffering too far into the future). Signed-off-by: Kevin O'Connor --- klippy/toolhead.py | 62 ++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/klippy/toolhead.py b/klippy/toolhead.py index 58fa429f2..0f7da6b90 100644 --- a/klippy/toolhead.py +++ b/klippy/toolhead.py @@ -207,13 +207,9 @@ def __init__(self, config): self.all_mcus = [ m for n, m in self.printer.lookup_objects(module='mcu')] self.mcu = self.all_mcus[0] - self.can_pause = True - if self.mcu.is_fileoutput(): - self.can_pause = False self.move_queue = MoveQueue(self) + self.move_queue.set_flush_time(BUFFER_TIME_HIGH) self.commanded_pos = [0., 0., 0., 0.] - self.printer.register_event_handler("klippy:shutdown", - self._handle_shutdown) # Velocity and acceleration control self.max_velocity = config.getfloat('max_velocity', above=0.) self.max_accel = config.getfloat('max_accel', above=0.) @@ -224,14 +220,18 @@ def __init__(self, config): 'square_corner_velocity', 5., minval=0.) self.junction_deviation = 0. self._calc_junction_deviation() + # Input stall detection + self.check_stall_time = 0. + self.print_stall = 0 + # Input pause tracking + self.can_pause = True + if self.mcu.is_fileoutput(): + self.can_pause = False + self.need_check_pause = -1. # Print time tracking self.print_time = 0. self.special_queuing_state = "Flushed" - self.need_check_stall = -1. self.flush_timer = self.reactor.register_timer(self._flush_handler) - self.move_queue.set_flush_time(BUFFER_TIME_HIGH) - self.idle_flush_print_time = 0. - self.print_stall = 0 self.drip_completion = None # Kinematic step generation scan window time tracking self.kin_flush_delay = SDS_CHECK_TIME @@ -266,6 +266,8 @@ def __init__(self, config): self.cmd_SET_VELOCITY_LIMIT, desc=self.cmd_SET_VELOCITY_LIMIT_help) gcode.register_command('M204', self.cmd_M204) + self.printer.register_event_handler("klippy:shutdown", + self._handle_shutdown) # Load some default modules modules = ["gcode_move", "homing", "idle_timeout", "statistics", "manual_probe", "tuning_tower"] @@ -305,7 +307,7 @@ def _process_moves(self, moves): if self.special_queuing_state != "Drip": # Transition from "Flushed"/"Priming" state to main state self.special_queuing_state = "" - self.need_check_stall = -1. + self.need_check_pause = -1. self.reactor.update_timer(self.flush_timer, self.reactor.NOW) self._calc_print_time() # Queue moves into trapezoid motion queue (trapq) @@ -333,10 +335,10 @@ def flush_step_generation(self): # Transition from "Flushed"/"Priming"/main state to "Flushed" state self.move_queue.flush() self.special_queuing_state = "Flushed" - self.need_check_stall = -1. + self.need_check_pause = -1. self.reactor.update_timer(self.flush_timer, self.reactor.NEVER) self.move_queue.set_flush_time(BUFFER_TIME_HIGH) - self.idle_flush_print_time = 0. + self.check_stall_time = 0. # Determine actual last "itersolve" flush time lastf = self.print_time - self.kin_flush_delay # Calculate flush time that includes kinematic scan windows @@ -356,33 +358,33 @@ def get_last_move_time(self): if self.special_queuing_state: self._calc_print_time() return self.print_time - def _check_stall(self): + def _check_pause(self): eventtime = self.reactor.monotonic() if self.special_queuing_state: - if self.idle_flush_print_time: + if self.check_stall_time: # Was in "Flushed" state and got there from idle input est_print_time = self.mcu.estimated_print_time(eventtime) - if est_print_time < self.idle_flush_print_time: + if est_print_time < self.check_stall_time: self.print_stall += 1 - self.idle_flush_print_time = 0. + self.check_stall_time = 0. # Transition from "Flushed"/"Priming" state to "Priming" state self.special_queuing_state = "Priming" - self.need_check_stall = -1. + self.need_check_pause = -1. self.reactor.update_timer(self.flush_timer, eventtime + 0.100) - # Check if there are lots of queued moves and stall if so + # Check if there are lots of queued moves and pause if so while 1: est_print_time = self.mcu.estimated_print_time(eventtime) buffer_time = self.print_time - est_print_time - stall_time = buffer_time - BUFFER_TIME_HIGH - if stall_time <= 0.: + pause_time = buffer_time - BUFFER_TIME_HIGH + if pause_time <= 0.: break if not self.can_pause: - self.need_check_stall = self.reactor.NEVER + self.need_check_pause = self.reactor.NEVER return - eventtime = self.reactor.pause(eventtime + min(1., stall_time)) + eventtime = self.reactor.pause(eventtime + min(1., pause_time)) if not self.special_queuing_state: - # In main state - defer stall checking until needed - self.need_check_stall = est_print_time + BUFFER_TIME_HIGH + 0.100 + # In main state - defer pause checking until needed + self.need_check_pause = est_print_time + BUFFER_TIME_HIGH + 0.100 def _flush_handler(self, eventtime): try: print_time = self.print_time @@ -393,7 +395,7 @@ def _flush_handler(self, eventtime): # Under ran low buffer mark - flush lookahead queue self.flush_step_generation() if print_time != self.print_time: - self.idle_flush_print_time = self.print_time + self.check_stall_time = self.print_time except: logging.exception("Exception in flush_handler") self.printer.invoke_shutdown("Exception in flush_handler") @@ -419,8 +421,8 @@ def move(self, newpos, speed): self.extruder.check_move(move) self.commanded_pos[:] = move.end_pos self.move_queue.add_move(move) - if self.print_time > self.need_check_stall: - self._check_stall() + if self.print_time > self.need_check_pause: + self._check_pause() def manual_move(self, coord, speed): curpos = list(self.commanded_pos) for i in range(len(coord)): @@ -431,7 +433,7 @@ def manual_move(self, coord, speed): def dwell(self, delay): next_print_time = self.get_last_move_time() + max(0., delay) self._update_move_time(next_print_time) - self._check_stall() + self._check_pause() def wait_moves(self): self._flush_lookahead() eventtime = self.reactor.monotonic() @@ -465,10 +467,10 @@ def drip_move(self, newpos, speed, drip_completion): # Transition from "Flushed"/"Priming"/main state to "Drip" state self.move_queue.flush() self.special_queuing_state = "Drip" - self.need_check_stall = self.reactor.NEVER + self.need_check_pause = self.reactor.NEVER self.reactor.update_timer(self.flush_timer, self.reactor.NEVER) self.move_queue.set_flush_time(BUFFER_TIME_HIGH) - self.idle_flush_print_time = 0. + self.check_stall_time = 0. self.drip_completion = drip_completion # Submit move try: From 9e574c34970fe4c68c5ad44d4f1f5faac4682e1e Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 27 Nov 2023 18:02:47 -0500 Subject: [PATCH 31/53] toolhead: Separate out priming flush notification to its own timer Simplify the code by introducing a separate lookahead priming flush timer. After this change, the flush_timer is not active in any of the special queuing states. Signed-off-by: Kevin O'Connor --- klippy/toolhead.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/klippy/toolhead.py b/klippy/toolhead.py index 0f7da6b90..95131c80b 100644 --- a/klippy/toolhead.py +++ b/klippy/toolhead.py @@ -232,6 +232,7 @@ def __init__(self, config): self.print_time = 0. self.special_queuing_state = "Flushed" self.flush_timer = self.reactor.register_timer(self._flush_handler) + self.priming_timer = None self.drip_completion = None # Kinematic step generation scan window time tracking self.kin_flush_delay = SDS_CHECK_TIME @@ -360,21 +361,24 @@ def get_last_move_time(self): return self.print_time def _check_pause(self): eventtime = self.reactor.monotonic() + est_print_time = self.mcu.estimated_print_time(eventtime) + buffer_time = self.print_time - est_print_time if self.special_queuing_state: if self.check_stall_time: # Was in "Flushed" state and got there from idle input - est_print_time = self.mcu.estimated_print_time(eventtime) if est_print_time < self.check_stall_time: self.print_stall += 1 self.check_stall_time = 0. # Transition from "Flushed"/"Priming" state to "Priming" state self.special_queuing_state = "Priming" self.need_check_pause = -1. - self.reactor.update_timer(self.flush_timer, eventtime + 0.100) + if self.priming_timer is None: + self.priming_timer = self.reactor.register_timer( + self._priming_handler) + wtime = eventtime + max(0.100, buffer_time - BUFFER_TIME_LOW) + self.reactor.update_timer(self.priming_timer, wtime) # Check if there are lots of queued moves and pause if so while 1: - est_print_time = self.mcu.estimated_print_time(eventtime) - buffer_time = self.print_time - est_print_time pause_time = buffer_time - BUFFER_TIME_HIGH if pause_time <= 0.: break @@ -382,9 +386,22 @@ def _check_pause(self): self.need_check_pause = self.reactor.NEVER return eventtime = self.reactor.pause(eventtime + min(1., pause_time)) + est_print_time = self.mcu.estimated_print_time(eventtime) + buffer_time = self.print_time - est_print_time if not self.special_queuing_state: # In main state - defer pause checking until needed self.need_check_pause = est_print_time + BUFFER_TIME_HIGH + 0.100 + def _priming_handler(self, eventtime): + self.reactor.unregister_timer(self.priming_timer) + self.priming_timer = None + try: + if self.special_queuing_state == "Priming": + self.flush_step_generation() + self.check_stall_time = self.print_time + except: + logging.exception("Exception in priming_handler") + self.printer.invoke_shutdown("Exception in priming_handler") + return self.reactor.NEVER def _flush_handler(self, eventtime): try: print_time = self.print_time From b7b13588c7bf8925e739954860eedc3a5eea42e7 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 27 Nov 2023 19:54:39 -0500 Subject: [PATCH 32/53] toolhead: Rework flushing to be based on mcu flush time Rename last_kin_move_time to need_flush_time and rename force_flush_time to last_flush_time to improve variable name clarity. Move low-level flushing to new _advance_flush_time() so that it is possible to flush the queues without needing to advance print_time. Signed-off-by: Kevin O'Connor --- klippy/toolhead.py | 73 ++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/klippy/toolhead.py b/klippy/toolhead.py index 95131c80b..b605a5694 100644 --- a/klippy/toolhead.py +++ b/klippy/toolhead.py @@ -231,13 +231,14 @@ def __init__(self, config): # Print time tracking self.print_time = 0. self.special_queuing_state = "Flushed" - self.flush_timer = self.reactor.register_timer(self._flush_handler) self.priming_timer = None self.drip_completion = None + # Flush tracking + self.flush_timer = self.reactor.register_timer(self._flush_handler) + self.last_flush_time = self.need_flush_time = 0. # Kinematic step generation scan window time tracking self.kin_flush_delay = SDS_CHECK_TIME self.kin_flush_times = [] - self.force_flush_time = self.last_kin_move_time = 0. # Setup iterative solver ffi_main, ffi_lib = chelper.get_ffi() self.trapq = ffi_main.gc(ffi_lib.trapq_alloc(), ffi_lib.trapq_free) @@ -274,28 +275,36 @@ def __init__(self, config): "manual_probe", "tuning_tower"] for module_name in modules: self.printer.load_object(config, module_name) - # Print time tracking - def _update_move_time(self, next_print_time): - batch_time = MOVE_BATCH_TIME - kin_flush_delay = self.kin_flush_delay - fft = self.force_flush_time + # Print time and flush tracking + def _advance_flush_time(self, flush_time): + flush_time = max(flush_time, self.last_flush_time) + # Generate steps via itersolve + sg_flush_ceil = max(flush_time, self.print_time - self.kin_flush_delay) + sg_flush_time = min(flush_time + STEPCOMPRESS_FLUSH_TIME, sg_flush_ceil) + for sg in self.step_generators: + sg(sg_flush_time) + # Free trapq entries that are no longer needed + free_time = sg_flush_time - self.kin_flush_delay + self.trapq_finalize_moves(self.trapq, free_time) + self.extruder.update_move_time(free_time) + # Flush stepcompress and mcu steppersync + for m in self.all_mcus: + m.flush_moves(flush_time) + self.last_flush_time = flush_time + def _advance_move_time(self, next_print_time): + pt_delay = self.kin_flush_delay + STEPCOMPRESS_FLUSH_TIME + flush_time = max(self.last_flush_time, self.print_time - pt_delay) + self.print_time = max(self.print_time, next_print_time) + want_flush_time = max(flush_time, self.print_time - pt_delay) while 1: - self.print_time = min(self.print_time + batch_time, next_print_time) - sg_flush_time = max(fft, self.print_time - kin_flush_delay) - for sg in self.step_generators: - sg(sg_flush_time) - free_time = max(fft, sg_flush_time - kin_flush_delay) - self.trapq_finalize_moves(self.trapq, free_time) - self.extruder.update_move_time(free_time) - mcu_flush_time = max(fft, sg_flush_time - STEPCOMPRESS_FLUSH_TIME) - for m in self.all_mcus: - m.flush_moves(mcu_flush_time) - if self.print_time >= next_print_time: + flush_time = min(flush_time + MOVE_BATCH_TIME, want_flush_time) + self._advance_flush_time(flush_time) + if flush_time >= want_flush_time: break def _calc_print_time(self): curtime = self.reactor.monotonic() est_print_time = self.mcu.estimated_print_time(curtime) - kin_time = max(est_print_time + MIN_KIN_TIME, self.force_flush_time) + kin_time = max(est_print_time + MIN_KIN_TIME, self.last_flush_time) kin_time += self.kin_flush_delay min_print_time = max(est_print_time + BUFFER_TIME_START, kin_time) if min_print_time > self.print_time: @@ -330,8 +339,8 @@ def _process_moves(self, moves): # Generate steps for moves if self.special_queuing_state: self._update_drip_move_time(next_move_time) - self._update_move_time(next_move_time) - self.last_kin_move_time = max(self.last_kin_move_time, next_move_time) + self.note_kinematic_activity(next_move_time + self.kin_flush_delay) + self._advance_move_time(next_move_time) def flush_step_generation(self): # Transition from "Flushed"/"Priming"/main state to "Flushed" state self.move_queue.flush() @@ -340,16 +349,8 @@ def flush_step_generation(self): self.reactor.update_timer(self.flush_timer, self.reactor.NEVER) self.move_queue.set_flush_time(BUFFER_TIME_HIGH) self.check_stall_time = 0. - # Determine actual last "itersolve" flush time - lastf = self.print_time - self.kin_flush_delay - # Calculate flush time that includes kinematic scan windows - flush_time = max(lastf, self.last_kin_move_time + self.kin_flush_delay) - if flush_time > self.print_time: - # Flush in small time chunks - self._update_move_time(flush_time) - # Flush kinematic scan windows and step buffers - self.force_flush_time = max(self.force_flush_time, flush_time) - self._update_move_time(max(self.print_time, self.force_flush_time)) + # Flush all queues + self._advance_flush_time(self.need_flush_time) def _flush_lookahead(self): if self.special_queuing_state: return self.flush_step_generation() @@ -449,7 +450,7 @@ def manual_move(self, coord, speed): self.printer.send_event("toolhead:manual_move") def dwell(self, delay): next_print_time = self.get_last_move_time() + max(0., delay) - self._update_move_time(next_print_time) + self._advance_move_time(next_print_time) self._check_pause() def wait_moves(self): self._flush_lookahead() @@ -478,7 +479,8 @@ def _update_drip_move_time(self, next_print_time): self.drip_completion.wait(curtime + wait_time) continue npt = min(self.print_time + DRIP_SEGMENT_TIME, next_print_time) - self._update_move_time(npt) + self.note_kinematic_activity(npt + self.kin_flush_delay) + self._advance_move_time(npt) def drip_move(self, newpos, speed, drip_completion): self.dwell(self.kin_flush_delay) # Transition from "Flushed"/"Priming"/main state to "Drip" state @@ -505,8 +507,9 @@ def drip_move(self, newpos, speed, drip_completion): self.flush_step_generation() # Misc commands def stats(self, eventtime): + max_queue_time = max(self.print_time, self.last_flush_time) for m in self.all_mcus: - m.check_active(self.print_time, eventtime) + m.check_active(max_queue_time, eventtime) buffer_time = self.print_time - self.mcu.estimated_print_time(eventtime) is_active = buffer_time > -60. or not self.special_queuing_state if self.special_queuing_state == "Drip": @@ -556,7 +559,7 @@ def register_lookahead_callback(self, callback): return last_move.timing_callbacks.append(callback) def note_kinematic_activity(self, kin_time): - self.last_kin_move_time = max(self.last_kin_move_time, kin_time) + self.need_flush_time = max(self.need_flush_time, kin_time) def get_max_velocity(self): return self.max_velocity, self.max_accel def _calc_junction_deviation(self): From c491ea669f8b189f72824b9d20f005fb6e86afe7 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Tue, 28 Nov 2023 11:27:25 -0500 Subject: [PATCH 33/53] toolhead: Support flushing even while lookahead queue is idle Track a "NeedPrime" queue state instead of the "Flushed" state, and continue running the background flushing timer as long as there may be data in any of the move queues. Signed-off-by: Kevin O'Connor --- klippy/toolhead.py | 71 +++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/klippy/toolhead.py b/klippy/toolhead.py index b605a5694..a04451c67 100644 --- a/klippy/toolhead.py +++ b/klippy/toolhead.py @@ -189,6 +189,8 @@ def add_move(self, move): BUFFER_TIME_LOW = 1.0 BUFFER_TIME_HIGH = 2.0 BUFFER_TIME_START = 0.250 +BGFLUSH_LOW_TIME = 0.200 +BGFLUSH_BATCH_TIME = 0.200 MIN_KIN_TIME = 0.100 MOVE_BATCH_TIME = 0.500 STEPCOMPRESS_FLUSH_TIME = 0.050 @@ -230,11 +232,12 @@ def __init__(self, config): self.need_check_pause = -1. # Print time tracking self.print_time = 0. - self.special_queuing_state = "Flushed" + self.special_queuing_state = "NeedPrime" self.priming_timer = None self.drip_completion = None # Flush tracking self.flush_timer = self.reactor.register_timer(self._flush_handler) + self.do_kick_flush_timer = True self.last_flush_time = self.need_flush_time = 0. # Kinematic step generation scan window time tracking self.kin_flush_delay = SDS_CHECK_TIME @@ -315,10 +318,9 @@ def _process_moves(self, moves): # Resync print_time if necessary if self.special_queuing_state: if self.special_queuing_state != "Drip": - # Transition from "Flushed"/"Priming" state to main state + # Transition from "NeedPrime"/"Priming" state to main state self.special_queuing_state = "" self.need_check_pause = -1. - self.reactor.update_timer(self.flush_timer, self.reactor.NOW) self._calc_print_time() # Queue moves into trapezoid motion queue (trapq) next_move_time = self.print_time @@ -341,24 +343,22 @@ def _process_moves(self, moves): self._update_drip_move_time(next_move_time) self.note_kinematic_activity(next_move_time + self.kin_flush_delay) self._advance_move_time(next_move_time) - def flush_step_generation(self): - # Transition from "Flushed"/"Priming"/main state to "Flushed" state + def _flush_lookahead(self): + # Transit from "NeedPrime"/"Priming"/"Drip"/main state to "NeedPrime" self.move_queue.flush() - self.special_queuing_state = "Flushed" + self.special_queuing_state = "NeedPrime" self.need_check_pause = -1. - self.reactor.update_timer(self.flush_timer, self.reactor.NEVER) self.move_queue.set_flush_time(BUFFER_TIME_HIGH) self.check_stall_time = 0. - # Flush all queues + def flush_step_generation(self): + self._flush_lookahead() self._advance_flush_time(self.need_flush_time) - def _flush_lookahead(self): - if self.special_queuing_state: - return self.flush_step_generation() - self.move_queue.flush() def get_last_move_time(self): - self._flush_lookahead() if self.special_queuing_state: + self._flush_lookahead() self._calc_print_time() + else: + self.move_queue.flush() return self.print_time def _check_pause(self): eventtime = self.reactor.monotonic() @@ -366,11 +366,11 @@ def _check_pause(self): buffer_time = self.print_time - est_print_time if self.special_queuing_state: if self.check_stall_time: - # Was in "Flushed" state and got there from idle input + # Was in "NeedPrime" state and got there from idle input if est_print_time < self.check_stall_time: self.print_stall += 1 self.check_stall_time = 0. - # Transition from "Flushed"/"Priming" state to "Priming" state + # Transition from "NeedPrime"/"Priming" state to "Priming" state self.special_queuing_state = "Priming" self.need_check_pause = -1. if self.priming_timer is None: @@ -397,7 +397,7 @@ def _priming_handler(self, eventtime): self.priming_timer = None try: if self.special_queuing_state == "Priming": - self.flush_step_generation() + self._flush_lookahead() self.check_stall_time = self.print_time except: logging.exception("Exception in priming_handler") @@ -405,15 +405,28 @@ def _priming_handler(self, eventtime): return self.reactor.NEVER def _flush_handler(self, eventtime): try: - print_time = self.print_time - buffer_time = print_time - self.mcu.estimated_print_time(eventtime) - if buffer_time > BUFFER_TIME_LOW: - # Running normally - reschedule check - return eventtime + buffer_time - BUFFER_TIME_LOW - # Under ran low buffer mark - flush lookahead queue - self.flush_step_generation() - if print_time != self.print_time: - self.check_stall_time = self.print_time + est_print_time = self.mcu.estimated_print_time(eventtime) + if not self.special_queuing_state: + # In "main" state - flush lookahead if buffer runs low + print_time = self.print_time + buffer_time = print_time - est_print_time + if buffer_time > BUFFER_TIME_LOW: + # Running normally - reschedule check + return eventtime + buffer_time - BUFFER_TIME_LOW + # Under ran low buffer mark - flush lookahead queue + self._flush_lookahead() + if print_time != self.print_time: + self.check_stall_time = self.print_time + # In "NeedPrime"/"Priming" state - flush queues if needed + while 1: + if self.last_flush_time >= self.need_flush_time: + self.do_kick_flush_timer = True + return self.reactor.NEVER + buffer_time = self.last_flush_time - est_print_time + if buffer_time > BGFLUSH_LOW_TIME: + return eventtime + buffer_time - BGFLUSH_LOW_TIME + ftime = est_print_time + BGFLUSH_LOW_TIME + BGFLUSH_BATCH_TIME + self._advance_flush_time(min(self.need_flush_time, ftime)) except: logging.exception("Exception in flush_handler") self.printer.invoke_shutdown("Exception in flush_handler") @@ -483,11 +496,12 @@ def _update_drip_move_time(self, next_print_time): self._advance_move_time(npt) def drip_move(self, newpos, speed, drip_completion): self.dwell(self.kin_flush_delay) - # Transition from "Flushed"/"Priming"/main state to "Drip" state + # Transition from "NeedPrime"/"Priming"/main state to "Drip" state self.move_queue.flush() self.special_queuing_state = "Drip" self.need_check_pause = self.reactor.NEVER self.reactor.update_timer(self.flush_timer, self.reactor.NEVER) + self.do_kick_flush_timer = False self.move_queue.set_flush_time(BUFFER_TIME_HIGH) self.check_stall_time = 0. self.drip_completion = drip_completion @@ -495,6 +509,7 @@ def drip_move(self, newpos, speed, drip_completion): try: self.move(newpos, speed) except self.printer.command_error as e: + self.reactor.update_timer(self.flush_timer, self.reactor.NOW) self.flush_step_generation() raise # Transmit move in "drip" mode @@ -504,6 +519,7 @@ def drip_move(self, newpos, speed, drip_completion): self.move_queue.reset() self.trapq_finalize_moves(self.trapq, self.reactor.NEVER) # Exit "Drip" state + self.reactor.update_timer(self.flush_timer, self.reactor.NOW) self.flush_step_generation() # Misc commands def stats(self, eventtime): @@ -560,6 +576,9 @@ def register_lookahead_callback(self, callback): last_move.timing_callbacks.append(callback) def note_kinematic_activity(self, kin_time): self.need_flush_time = max(self.need_flush_time, kin_time) + if self.do_kick_flush_timer: + self.do_kick_flush_timer = False + self.reactor.update_timer(self.flush_timer, self.reactor.NOW) def get_max_velocity(self): return self.max_velocity, self.max_accel def _calc_junction_deviation(self): From 99d7af87fd7cd74b9a6eab355eb71327ed3b8256 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Tue, 28 Nov 2023 12:49:53 -0500 Subject: [PATCH 34/53] pwm_tool: Notify the toolhead that the move_queue needs to be flushed Call toolhead.note_kinematic_activity() on each pin update to ensure that those updates will be flushed properly. This fixes "Timer too close" errors on SET_PIN commands that are issued when the toolhead is idle. Signed-off-by: Kevin O'Connor --- klippy/extras/pwm_tool.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/klippy/extras/pwm_tool.py b/klippy/extras/pwm_tool.py index c012bb317..e3a7377c8 100644 --- a/klippy/extras/pwm_tool.py +++ b/klippy/extras/pwm_tool.py @@ -143,6 +143,8 @@ def _set_pin(self, print_time, value): self.mcu_pin.set_pwm(print_time, value) self.last_value = value self.last_print_time = print_time + toolhead = self.printer.lookup_object('toolhead') + toolhead.note_kinematic_activity(print_time) cmd_SET_PIN_help = "Set the value of an output pin" def cmd_SET_PIN(self, gcmd): # Read requested value From 62bf52bfcf1aab6d97236c3945320d790774ad92 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 4 Dec 2023 14:00:47 -0500 Subject: [PATCH 35/53] serialqueue: Simplify sequence number upconversion Signed-off-by: Kevin O'Connor --- klippy/chelper/serialqueue.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/klippy/chelper/serialqueue.c b/klippy/chelper/serialqueue.c index e6810933a..c207495cd 100644 --- a/klippy/chelper/serialqueue.c +++ b/klippy/chelper/serialqueue.c @@ -222,12 +222,11 @@ handle_message(struct serialqueue *sq, double eventtime, int len) pthread_mutex_lock(&sq->lock); // Calculate receive sequence number - uint64_t rseq = ((sq->receive_seq & ~MESSAGE_SEQ_MASK) - | (sq->input_buf[MESSAGE_POS_SEQ] & MESSAGE_SEQ_MASK)); + uint32_t rseq_delta = ((sq->input_buf[MESSAGE_POS_SEQ] - sq->receive_seq) + & MESSAGE_SEQ_MASK); + uint64_t rseq = sq->receive_seq + rseq_delta; if (rseq != sq->receive_seq) { // New sequence number - if (rseq < sq->receive_seq) - rseq += MESSAGE_SEQ_MASK+1; if (rseq > sq->send_seq && sq->receive_seq != 1) { // An ack for a message not sent? Out of order message? sq->bytes_invalid += len; From c5bd813d8b2199b3a1c08d1e29ac0a97d49e3ff8 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 4 Dec 2023 14:01:12 -0500 Subject: [PATCH 36/53] clocksync: Simplify 32bit clock upconversion code Signed-off-by: Kevin O'Connor --- klippy/clocksync.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/klippy/clocksync.py b/klippy/clocksync.py index f32ed3cd6..80ed9db61 100644 --- a/klippy/clocksync.py +++ b/klippy/clocksync.py @@ -66,10 +66,8 @@ def _handle_clock(self, params): self.queries_pending = 0 # Extend clock to 64bit last_clock = self.last_clock - clock = (last_clock & ~0xffffffff) | params['clock'] - if clock < last_clock: - clock += 0x100000000 - self.last_clock = clock + clock_delta = (params['clock'] - last_clock) & 0xffffffff + self.last_clock = clock = last_clock + clock_delta # Check if this is the best round-trip-time seen so far sent_time = params['#sent_time'] if not sent_time: @@ -138,10 +136,9 @@ def estimated_print_time(self, eventtime): # misc commands def clock32_to_clock64(self, clock32): last_clock = self.last_clock - clock_diff = (last_clock - clock32) & 0xffffffff - if clock_diff & 0x80000000: - return last_clock + 0x100000000 - clock_diff - return last_clock - clock_diff + clock_diff = (clock32 - last_clock) & 0xffffffff + clock_diff -= (clock_diff & 0x80000000) << 1 + return last_clock + clock_diff def is_active(self): return self.queries_pending <= 4 def dump_debug(self): From fe7082e4a8e0beb7014584a16aaa22dc7084a01a Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 4 Dec 2023 14:03:27 -0500 Subject: [PATCH 37/53] buttons: Simplify ack upconversion code Signed-off-by: Kevin O'Connor --- klippy/extras/buttons.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/klippy/extras/buttons.py b/klippy/extras/buttons.py index 1a6147d1d..472742576 100644 --- a/klippy/extras/buttons.py +++ b/klippy/extras/buttons.py @@ -57,10 +57,9 @@ def build_config(self): def handle_buttons_state(self, params): # Expand the message ack_count from 8-bit ack_count = self.ack_count - ack_diff = (ack_count - params['ack_count']) & 0xff - if ack_diff & 0x80: - ack_diff -= 0x100 - msg_ack_count = ack_count - ack_diff + ack_diff = (params['ack_count'] - ack_count) & 0xff + ack_diff -= (ack_diff & 0x80) << 1 + msg_ack_count = ack_count + ack_diff # Determine new buttons buttons = bytearray(params['state']) new_count = msg_ack_count + len(buttons) - self.ack_count From 1a83845c9f16577921b09053589e6a90627e3674 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 4 Dec 2023 14:08:22 -0500 Subject: [PATCH 38/53] angle: Simplify sequence upconversion Signed-off-by: Kevin O'Connor --- klippy/extras/angle.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/klippy/extras/angle.py b/klippy/extras/angle.py index 2e17749e9..30a4447ad 100644 --- a/klippy/extras/angle.py +++ b/klippy/extras/angle.py @@ -85,9 +85,9 @@ def apply_calibration(self, samples): cal2 = calibration[bucket + 1] adj = (angle & interp_mask) * (cal2 - cal1) adj = cal1 + ((adj + interp_round) >> interp_bits) - angle_diff = (angle - adj) & 0xffff + angle_diff = (adj - angle) & 0xffff angle_diff -= (angle_diff & 0x8000) << 1 - new_angle = angle - angle_diff + new_angle = angle + angle_diff if calibration_reversed: new_angle = -new_angle samples[i] = (samp_time, new_angle) @@ -375,9 +375,9 @@ def update_clock(self): mcu_clock, chip_clock = self._query_clock() mdiff = mcu_clock - self.last_chip_mcu_clock chip_mclock = self.last_chip_clock + int(mdiff * self.chip_freq + .5) - cdiff = (chip_mclock - chip_clock) & 0xffff + cdiff = (chip_clock - chip_mclock) & 0xffff cdiff -= (cdiff & 0x8000) << 1 - new_chip_clock = chip_mclock - cdiff + new_chip_clock = chip_mclock + cdiff self.chip_freq = float(new_chip_clock - self.last_chip_clock) / mdiff self.last_chip_clock = new_chip_clock self.last_chip_mcu_clock = mcu_clock @@ -489,21 +489,19 @@ def _extract_samples(self, raw_samples): count = error_count = 0 samples = [None] * (len(raw_samples) * 16) for params in raw_samples: - seq = (last_sequence & ~0xffff) | params['sequence'] - if seq < last_sequence: - seq += 0x10000 - last_sequence = seq + seq_diff = (params['sequence'] - last_sequence) & 0xffff + last_sequence += seq_diff d = bytearray(params['data']) - msg_mclock = start_clock + seq*16*sample_ticks + msg_mclock = start_clock + last_sequence*16*sample_ticks for i in range(len(d) // 3): tcode = d[i*3] if tcode == TCODE_ERROR: error_count += 1 continue raw_angle = d[i*3 + 1] | (d[i*3 + 2] << 8) - angle_diff = (last_angle - raw_angle) & 0xffff + angle_diff = (raw_angle - last_angle) & 0xffff angle_diff -= (angle_diff & 0x8000) << 1 - last_angle -= angle_diff + last_angle += angle_diff mclock = msg_mclock + i*sample_ticks if is_tcode_absolute: # tcode is tle5012b frame counter From 2c2bb720fae54422cf10a71569ce40476e909c09 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 4 Dec 2023 14:13:37 -0500 Subject: [PATCH 39/53] adxl345: Simplify sequence and limit_count upconversion Signed-off-by: Kevin O'Connor --- klippy/extras/adxl345.py | 18 +++++++----------- klippy/extras/lis2dw.py | 18 +++++++----------- klippy/extras/mpu9250.py | 18 +++++++----------- 3 files changed, 21 insertions(+), 33 deletions(-) diff --git a/klippy/extras/adxl345.py b/klippy/extras/adxl345.py index f5af2f166..52698cb6c 100644 --- a/klippy/extras/adxl345.py +++ b/klippy/extras/adxl345.py @@ -307,9 +307,9 @@ def _extract_samples(self, raw_samples): count = seq = 0 samples = [None] * (len(raw_samples) * SAMPLES_PER_BLOCK) for params in raw_samples: - seq_diff = (last_sequence - params['sequence']) & 0xffff + seq_diff = (params['sequence'] - last_sequence) & 0xffff seq_diff -= (seq_diff & 0x8000) << 1 - seq = last_sequence - seq_diff + seq = last_sequence + seq_diff d = bytearray(params['data']) msg_cdiff = seq * SAMPLES_PER_BLOCK - chip_base for i in range(len(d) // BYTES_PER_SAMPLE): @@ -343,15 +343,11 @@ def _update_clock(self, minclock=0): else: raise self.printer.command_error("Unable to query adxl345 fifo") mcu_clock = self.mcu.clock32_to_clock64(params['clock']) - sequence = (self.last_sequence & ~0xffff) | params['next_sequence'] - if sequence < self.last_sequence: - sequence += 0x10000 - self.last_sequence = sequence + seq_diff = (params['next_sequence'] - self.last_sequence) & 0xffff + self.last_sequence += seq_diff buffered = params['buffered'] - limit_count = (self.last_limit_count & ~0xffff) | params['limit_count'] - if limit_count < self.last_limit_count: - limit_count += 0x10000 - self.last_limit_count = limit_count + lc_diff = (params['limit_count'] - self.last_limit_count) & 0xffff + self.last_limit_count += lc_diff duration = params['query_ticks'] if duration > self.max_query_duration: # Skip measurement as a high query time could skew clock tracking @@ -359,7 +355,7 @@ def _update_clock(self, minclock=0): self.mcu.seconds_to_clock(.000005)) return self.max_query_duration = 2 * duration - msg_count = (sequence * SAMPLES_PER_BLOCK + msg_count = (self.last_sequence * SAMPLES_PER_BLOCK + buffered // BYTES_PER_SAMPLE + fifo) # The "chip clock" is the message counter plus .5 for average # inaccuracy of query responses and plus .5 for assumed offset diff --git a/klippy/extras/lis2dw.py b/klippy/extras/lis2dw.py index 5cf676393..ae62c22f6 100644 --- a/klippy/extras/lis2dw.py +++ b/klippy/extras/lis2dw.py @@ -118,9 +118,9 @@ def _extract_samples(self, raw_samples): count = seq = 0 samples = [None] * (len(raw_samples) * SAMPLES_PER_BLOCK) for params in raw_samples: - seq_diff = (last_sequence - params['sequence']) & 0xffff + seq_diff = (params['sequence'] - last_sequence) & 0xffff seq_diff -= (seq_diff & 0x8000) << 1 - seq = last_sequence - seq_diff + seq = last_sequence + seq_diff d = bytearray(params['data']) msg_cdiff = seq * SAMPLES_PER_BLOCK - chip_base @@ -156,15 +156,11 @@ def _update_clock(self, minclock=0): else: raise self.printer.command_error("Unable to query lis2dw fifo") mcu_clock = self.mcu.clock32_to_clock64(params['clock']) - sequence = (self.last_sequence & ~0xffff) | params['next_sequence'] - if sequence < self.last_sequence: - sequence += 0x10000 - self.last_sequence = sequence + seq_diff = (params['next_sequence'] - self.last_sequence) & 0xffff + self.last_sequence += seq_diff buffered = params['buffered'] - limit_count = (self.last_limit_count & ~0xffff) | params['limit_count'] - if limit_count < self.last_limit_count: - limit_count += 0x10000 - self.last_limit_count = limit_count + lc_diff = (params['limit_count'] - self.last_limit_count) & 0xffff + self.last_limit_count += lc_diff duration = params['query_ticks'] if duration > self.max_query_duration: # Skip measurement as a high query time could skew clock tracking @@ -172,7 +168,7 @@ def _update_clock(self, minclock=0): self.mcu.seconds_to_clock(.000005)) return self.max_query_duration = 2 * duration - msg_count = (sequence * SAMPLES_PER_BLOCK + msg_count = (self.last_sequence * SAMPLES_PER_BLOCK + buffered // BYTES_PER_SAMPLE + fifo) # The "chip clock" is the message counter plus .5 for average # inaccuracy of query responses and plus .5 for assumed offset diff --git a/klippy/extras/mpu9250.py b/klippy/extras/mpu9250.py index dc25449bb..72c1f6817 100644 --- a/klippy/extras/mpu9250.py +++ b/klippy/extras/mpu9250.py @@ -132,9 +132,9 @@ def _extract_samples(self, raw_samples): count = seq = 0 samples = [None] * (len(raw_samples) * SAMPLES_PER_BLOCK) for params in raw_samples: - seq_diff = (last_sequence - params['sequence']) & 0xffff + seq_diff = (params['sequence'] - last_sequence) & 0xffff seq_diff -= (seq_diff & 0x8000) << 1 - seq = last_sequence - seq_diff + seq = last_sequence + seq_diff d = bytearray(params['data']) msg_cdiff = seq * SAMPLES_PER_BLOCK - chip_base @@ -168,15 +168,11 @@ def _update_clock(self, minclock=0): else: raise self.printer.command_error("Unable to query mpu9250 fifo") mcu_clock = self.mcu.clock32_to_clock64(params['clock']) - sequence = (self.last_sequence & ~0xffff) | params['next_sequence'] - if sequence < self.last_sequence: - sequence += 0x10000 - self.last_sequence = sequence + seq_diff = (params['next_sequence'] - self.last_sequence) & 0xffff + self.last_sequence += seq_diff buffered = params['buffered'] - limit_count = (self.last_limit_count & ~0xffff) | params['limit_count'] - if limit_count < self.last_limit_count: - limit_count += 0x10000 - self.last_limit_count = limit_count + lc_diff = (params['limit_count'] - self.last_limit_count) & 0xffff + self.last_limit_count += lc_diff duration = params['query_ticks'] if duration > self.max_query_duration: # Skip measurement as a high query time could skew clock tracking @@ -184,7 +180,7 @@ def _update_clock(self, minclock=0): self.mcu.seconds_to_clock(.000005)) return self.max_query_duration = 2 * duration - msg_count = (sequence * SAMPLES_PER_BLOCK + msg_count = (self.last_sequence * SAMPLES_PER_BLOCK + buffered // BYTES_PER_SAMPLE + fifo) # The "chip clock" is the message counter plus .5 for average # inaccuracy of query responses and plus .5 for assumed offset From 6676c1df86e9cb9480b9dd98f77357f69d8ef0d8 Mon Sep 17 00:00:00 2001 From: Pedro Lamas Date: Mon, 6 Nov 2023 15:06:36 +0000 Subject: [PATCH 40/53] gcode: expose status with available commands Signed-off-by: Pedro Lamas --- docs/Status_Reference.md | 6 ++++++ klippy/gcode.py | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/docs/Status_Reference.md b/docs/Status_Reference.md index 5477049a1..b64108ae2 100644 --- a/docs/Status_Reference.md +++ b/docs/Status_Reference.md @@ -168,6 +168,12 @@ The following information is available in the module. These settings may differ from the config file if a `SET_RETRACTION` command alters them. +## gcode + +The following information is available in the `gcode` object: +- `commands`: Returns a list of all currently available commands. For each + command, if a help string is defined it will also be provided. + ## gcode_button The following information is available in diff --git a/klippy/gcode.py b/klippy/gcode.py index 07e312f98..7d980585f 100644 --- a/klippy/gcode.py +++ b/klippy/gcode.py @@ -104,6 +104,7 @@ def __init__(self, printer): self.ready_gcode_handlers = {} self.mux_commands = {} self.gcode_help = {} + self.status_commands = {} # Register commands needed before config file is loaded handlers = ['M110', 'M112', 'M115', 'RESTART', 'FIRMWARE_RESTART', 'ECHO', 'STATUS', 'HELP'] @@ -126,6 +127,7 @@ def register_command(self, cmd, func, when_not_ready=False, desc=None): del self.ready_gcode_handlers[cmd] if cmd in self.base_gcode_handlers: del self.base_gcode_handlers[cmd] + self._build_status_commands() return old_cmd if cmd in self.ready_gcode_handlers: raise self.printer.config_error( @@ -138,6 +140,7 @@ def register_command(self, cmd, func, when_not_ready=False, desc=None): self.base_gcode_handlers[cmd] = func if desc is not None: self.gcode_help[cmd] = desc + self._build_status_commands() def register_mux_command(self, cmd, key, value, func, desc=None): prev = self.mux_commands.get(cmd) if prev is None: @@ -156,6 +159,14 @@ def register_mux_command(self, cmd, key, value, func, desc=None): prev_values[value] = func def get_command_help(self): return dict(self.gcode_help) + def get_status(self, eventtime): + return {'commands': self.status_commands} + def _build_status_commands(self): + commands = {cmd: {} for cmd in self.gcode_handlers} + for cmd in self.gcode_help: + if cmd in commands: + commands[cmd]['help'] = self.gcode_help[cmd] + self.status_commands = commands def register_output_handler(self, cb): self.output_callbacks.append(cb) def _handle_shutdown(self): @@ -163,12 +174,14 @@ def _handle_shutdown(self): return self.is_printer_ready = False self.gcode_handlers = self.base_gcode_handlers + self._build_status_commands() self._respond_state("Shutdown") def _handle_disconnect(self): self._respond_state("Disconnect") def _handle_ready(self): self.is_printer_ready = True self.gcode_handlers = self.ready_gcode_handlers + self._build_status_commands() self._respond_state("Ready") # Parse input into commands args_r = re.compile('([A-Z_]+|[A-Z*/])') From 43801469c9b4a970dd9b3e0787d3c05c203b75e4 Mon Sep 17 00:00:00 2001 From: "J. Slaker" Date: Mon, 11 Dec 2023 20:04:39 -0500 Subject: [PATCH 41/53] Docs: Add instructions for switching to DK to README.md (#79) * Update README.md Add headings to allow deep linking. Add instructions for switching to DK. * Correct missing git clone --------- Co-authored-by: Rogerio Goncalves --- README.md | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4be4b815a..bfd618e3a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Action Status](https://github.com/DangerKlippers/danger-klipper/actions/workflows/ci-build_test.yaml/badge.svg?branch=master)](https://github.com/DangerKlippers/danger-klipper/actions/workflows/ci-build_test.yaml) -Welcome to the Danger Klipper project! +# Welcome to the Danger Klipper project! This is a community-maintained fork of the [Klipper](https://github.com/Klipper3d/klipper) firmware. @@ -10,7 +10,7 @@ Our goal is to support features and behavior that could be "risky" if used incor If I want my printer to light itself on fire, I should be able to make my printer light itself on fire. -Features merged into the master branch: +## Features merged into the master branch: - [core: No Python2 tests; No PRU boards](https://github.com/DangerKlippers/danger-klipper/pull/39) @@ -65,7 +65,41 @@ If you're feeling adventurous, take a peek at the extra features in the bleeding - [input_shaper: smooth input shapers](https://github.com/DangerKlippers/danger-klipper/pull/69) - [input_shaper: new print_ringing_tower utility](https://github.com/DangerKlippers/danger-klipper/pull/69) - + +## Switch to Danger Klipper +> [!NOTE] +> Any add-on modules you are using will need to be reinstalled after switching to Danger Klipper. This includes things like Beacon support, led-effect, etc. +> +> Any data in ~/printer_data such as printer configs and macros will be unaffected. + +### Manually clone the repository +If desired, make a backup copy of your existing Klipper installation by running: +``` +mv ~/klipper ~/klipper_old +``` +Then clone the Danger Klipper repo and restart the klipper service: +``` +git clone https://github.com/DangerKlippers/danger-klipper.git ~/klipper +sudo systemctl restart klipper +``` +### Using KIAUH +For users that are not comfortable using Git directly, [KIAUH](https://github.com/dw-0/kiauh) is able to use custom repositories. + +To do this, add the Danger Klipper repo to KIAUH's repo list and run the script with the following commands: + +``` +echo "DangerKlippers/danger-klipper" >> ~/kiauh/klipper_repos.txt +~/kiauh/kiauh.sh +``` +From the KIAUH menu select: +- 6 ) Settings +- 1 ) Set custom Klipper repository +- Select the number corresponding to DangerKlipper from the list shown +- Select 'Y' to confirm replacing your existing Klipper install +- Enter 'B' for back twice +- 'Q' to quit + +--- "Dangerous Klipper for dangerous users" Klipper is a 3d-Printer firmware. It combines the power of a general From a903c5b65e0017806c8e3f4a8bff5a9021fb6e17 Mon Sep 17 00:00:00 2001 From: Rogerio Goncalves Date: Tue, 12 Dec 2023 01:31:59 +0000 Subject: [PATCH 42/53] adds git-remote instructions to the readme (#104) --- .gitignore | 1 + README.md | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 4b87b0495..14b15b235 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ ci_cache/ _test_.* klippy/plugins/* !klippy/plugins/__init__.py +.vscode diff --git a/README.md b/README.md index bfd618e3a..b8c15391d 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ If you're feeling adventurous, take a peek at the extra features in the bleeding - [input_shaper: smooth input shapers](https://github.com/DangerKlippers/danger-klipper/pull/69) - [input_shaper: new print_ringing_tower utility](https://github.com/DangerKlippers/danger-klipper/pull/69) - + ## Switch to Danger Klipper > [!NOTE] > Any add-on modules you are using will need to be reinstalled after switching to Danger Klipper. This includes things like Beacon support, led-effect, etc. @@ -74,23 +74,28 @@ If you're feeling adventurous, take a peek at the extra features in the bleeding ### Manually clone the repository If desired, make a backup copy of your existing Klipper installation by running: -``` + +```bash mv ~/klipper ~/klipper_old ``` + Then clone the Danger Klipper repo and restart the klipper service: -``` + +```bash git clone https://github.com/DangerKlippers/danger-klipper.git ~/klipper sudo systemctl restart klipper ``` + ### Using KIAUH For users that are not comfortable using Git directly, [KIAUH](https://github.com/dw-0/kiauh) is able to use custom repositories. To do this, add the Danger Klipper repo to KIAUH's repo list and run the script with the following commands: -``` +```bash echo "DangerKlippers/danger-klipper" >> ~/kiauh/klipper_repos.txt ~/kiauh/kiauh.sh ``` + From the KIAUH menu select: - 6 ) Settings - 1 ) Set custom Klipper repository @@ -99,6 +104,17 @@ From the KIAUH menu select: - Enter 'B' for back twice - 'Q' to quit +### Adding a git-remote to the existing installation +```bash +cd ~/klipper +git remote add danger https://github.com/DangerKlippers/danger-klipper.git +git checkout -b upstream-master origin/master +git branch -D master +git checkout -b master danger/master +sudo systemctl restart klipper +sudo systemctl restart moonraker +``` + --- "Dangerous Klipper for dangerous users" From 2455a7e12d2fb7d8766d8bb16806c8cb67f427d1 Mon Sep 17 00:00:00 2001 From: Rogerio Goncalves Date: Tue, 12 Dec 2023 09:41:29 +0000 Subject: [PATCH 43/53] split upstream z_tilt and sensille's z-tilt (#105) --- README.md | 18 +- docs/Config_Reference.md | 35 ++- docs/G-Codes.md | 2 +- klippy/extras/z_tilt.py | 303 +-------------------- klippy/extras/z_tilt_ng.py | 530 +++++++++++++++++++++++++++++++++++++ test/klippy/z_tilt_ng.cfg | 101 +++++++ test/klippy/z_tilt_ng.test | 28 ++ 7 files changed, 699 insertions(+), 318 deletions(-) create mode 100644 klippy/extras/z_tilt_ng.py create mode 100644 test/klippy/z_tilt_ng.cfg create mode 100644 test/klippy/z_tilt_ng.test diff --git a/README.md b/README.md index b8c15391d..186369ba9 100644 --- a/README.md +++ b/README.md @@ -12,26 +12,28 @@ If I want my printer to light itself on fire, I should be able to make my printe ## Features merged into the master branch: -- [core: No Python2 tests; No PRU boards](https://github.com/DangerKlippers/danger-klipper/pull/39) +- [core: no Python2 tests; no PRU boards](https://github.com/DangerKlippers/danger-klipper/pull/39) -- [fan: Normalising Fan PWM power](https://github.com/DangerKlippers/danger-klipper/pull/44) ([klipper#6307](https://github.com/Klipper3d/klipper/pull/6307)) +- [fan: normalising Fan PWM power](https://github.com/DangerKlippers/danger-klipper/pull/44) ([klipper#6307](https://github.com/Klipper3d/klipper/pull/6307)) -- [fan: Reverse FAN](https://github.com/DangerKlippers/danger-klipper/pull/51) ([klipper#4983](https://github.com/Klipper3d/klipper/pull/4983)) +- [fan: reverse FAN](https://github.com/DangerKlippers/danger-klipper/pull/51) ([klipper#4983](https://github.com/Klipper3d/klipper/pull/4983)) -- [heater: Modify PID without reload](https://github.com/DangerKlippers/danger-klipper/pull/35) +- [heater: modify PID without reload](https://github.com/DangerKlippers/danger-klipper/pull/35) -- [heater: Velocity PID](https://github.com/DangerKlippers/danger-klipper/pull/47) ([klipper#6272](https://github.com/Klipper3d/klipper/pull/6272)) +- [heater: velocity PID](https://github.com/DangerKlippers/danger-klipper/pull/47) ([klipper#6272](https://github.com/Klipper3d/klipper/pull/6272)) -- [gcode: Jinja2.ext.do extension](https://github.com/DangerKlippers/danger-klipper/pull/26) ([klipper#5149](https://github.com/Klipper3d/klipper/pull/5149)) +- [gcode: jinja2.ext.do extension](https://github.com/DangerKlippers/danger-klipper/pull/26) ([klipper#5149](https://github.com/Klipper3d/klipper/pull/5149)) - [gcode: gcode_shell_command](https://github.com/DangerKlippers/danger-klipper/pull/26) ([klipper#2173](https://github.com/Klipper3d/klipper/pull/2173) / [kiuah](https://github.com/dw-0/kiauh/blob/master/resources/gcode_shell_command.py) ) -- [probe: Dockable Probe](https://github.com/DangerKlippers/danger-klipper/pull/43) ([klipper#4328](https://github.com/Klipper3d/klipper/pull/4328)) +- [probe: dockable Probe](https://github.com/DangerKlippers/danger-klipper/pull/43) ([klipper#4328](https://github.com/Klipper3d/klipper/pull/4328)) -- [probe: Drop the first result](https://github.com/DangerKlippers/danger-klipper/pull/2) ([klipper#3397](https://github.com/Klipper3d/klipper/issues/3397)) +- [probe: drop the first result](https://github.com/DangerKlippers/danger-klipper/pull/2) ([klipper#3397](https://github.com/Klipper3d/klipper/issues/3397)) - [probe: z_calibration](https://github.com/DangerKlippers/danger-klipper/pull/31) ([klipper#4614](https://github.com/Klipper3d/klipper/pull/4614) / [protoloft/z_calibration](https://github.com/protoloft/klipper_z_calibration)) +- [z_tilt: z-tilt calibration](https://github.com/DangerKlippers/danger-klipper/pull/105) ([klipper3d#4083](https://github.com/Klipper3d/klipper/pull/4083) / [dk/ztilt_calibration](https://github.com/DangerKlippers/danger-klipper/pull/54)) + - [core: danger_options](https://github.com/DangerKlippers/danger-klipper/pull/67) - [stepper: home_current](https://github.com/DangerKlippers/danger-klipper/pull/65) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 5f81f9ca8..3a2b5abdc 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -1162,15 +1162,7 @@ extended [G-Code command](G-Codes.md#z_tilt) becomes available. # stepper. It is described using nozzle coordinates (the X, Y position # of the nozzle if it could move directly above the point). The # first entry corresponds to stepper_z, the second to stepper_z1, -# the third to stepper_z2, etc. This parameter must be provided, -# unless the parameter "extra_points" is provided. In that case only -# the command Z_TILT_AUTODETECT can be run to automatically determine -# the z_positions. See 'extra_points' below. -#z_offsets: -# A list of Z offsets for each z_position. The z_offset is added to each -# probed value during Z_TILT_ADJUST to offset for unevenness of the bed. -# This values can also be automatically detected by running -# Z_TILT_CALIBRATE. See "extra_points" below. +# the third to stepper_z2, etc. This parameter must be provided. #points: # A list of X, Y coordinates (one per line; subsequent lines # indented) that should be probed during a Z_TILT_ADJUST command. @@ -1193,6 +1185,30 @@ extended [G-Code command](G-Codes.md#z_tilt) becomes available. # more points than steppers then you will likely have a fixed # minimum value for the range of probed points which you can learn # by observing command output. +``` + +``` +[z_tilt_ng] +#z_positions: +# See [z_tilt]. This parameter must be provided, +# unless the parameter "extra_points" is provided. In that case only +# the command Z_TILT_AUTODETECT can be run to automatically determine +# the z_positions. See 'extra_points' below. +#z_offsets: +# A list of Z offsets for each z_position. The z_offset is added to each +# probed value during Z_TILT_ADJUST to offset for unevenness of the bed. +# This values can also be automatically detected by running +# Z_TILT_CALIBRATE. See "extra_points" below. +#points: +# See [z_tilt] +#speed: 50 +# See [z_tilt] +#horizontal_move_z: 5 +# See [z_tilt] +#retries: 0 +# See [z_tilt] +#retry_tolerance: 0 +# See [z_tilt] #extra_points: # A list in the same format as "points" above. This list contains # additional points to be probed during the two calibration commands @@ -1220,6 +1236,7 @@ extended [G-Code command](G-Codes.md#z_tilt) becomes available. # The default is conservative. ``` + ### [quad_gantry_level] Moving gantry leveling using 4 independently controlled Z motors. diff --git a/docs/G-Codes.md b/docs/G-Codes.md index d066b6c7b..e802efc14 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -1464,7 +1464,7 @@ adjustments to each Z stepper to compensate for tilt. See the PROBE command for details on the optional probe parameters. The optional `HORIZONTAL_MOVE_Z` value overrides the `horizontal_move_z` option specified in the config file. The follwing commands are availabe when the parameter "extra_points" is -configured in the z_tilt_section: +configured in the z_tilt_ng section: - `Z_TILT_CALIBRATE [AVGLEN=]`: This command does multiple probe runs similar to Z_TILT_ADJUST, but with the additional points given in "extra_points". This leads to a more balanced bed adjustment in case the diff --git a/klippy/extras/z_tilt.py b/klippy/extras/z_tilt.py index f632aa856..948551f05 100644 --- a/klippy/extras/z_tilt.py +++ b/klippy/extras/z_tilt.py @@ -5,25 +5,9 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import logging import mathutil -import importlib from . import probe -def params_to_normal_form(np, params, offsets): - v = np.array([offsets[0], offsets[1], params["z_adjust"]]) - r = np.array([1, 0, params["x_adjust"]]) - s = np.array([0, 1, params["y_adjust"]]) - cp = np.cross(r, s) - return np.append(cp, np.dot(cp, v)) - - -def intersect_3_planes(np, p1, p2, p3): - a = np.array([p1[0:3], p2[0:3], p3[0:3]]) - b = np.array([p1[3], p2[3], p3[3]]) - sol = np.linalg.solve(a, b) - return sol - - class ZAdjustHelper: def __init__(self, config, z_count): self.printer = config.get_printer() @@ -37,13 +21,7 @@ def __init__(self, config, z_count): def handle_connect(self): kin = self.printer.lookup_object("toolhead").get_kinematics() z_steppers = [s for s in kin.get_steppers() if s.is_active_axis("z")] - if self.z_count is None: - if len(z_steppers) != 3: - raise self.printer.config_error( - "%s z_positions needs exactly 3 items for calibration" - % (self.name) - ) - elif len(z_steppers) != self.z_count: + if len(z_steppers) != self.z_count: raise self.printer.config_error( "%s z_positions needs exactly %d items" % (self.name, len(z_steppers)) @@ -181,53 +159,14 @@ def check_retry(self, z_positions): class ZTilt: def __init__(self, config): self.printer = config.get_printer() - self.section = config.get_name() - - try: - self.numpy = importlib.import_module("numpy") - except ImportError: - logging.info( - "numpy not installed, Z_TILT_CALIBRATE will not be " "available" - ) - self.numpy = None - self.z_positions = config.getlists( "z_positions", seps=(",", "\n"), parser=float, count=2 ) - z_count = len(self.z_positions) - self.retry_helper = RetryHelper(config) self.probe_helper = probe.ProbePointsHelper(config, self.probe_finalize) self.probe_helper.minimum_points(2) - - self.z_offsets = config.getlists( - "z_offsets", parser=float, count=z_count, default=None - ) - self.z_status = ZAdjustStatus(self.printer) - self.z_helper = ZAdjustHelper(config, z_count) - # probe points for calibrate/autodetect - cal_probe_points = list(self.probe_helper.get_probe_points()) - self.num_probe_points = len(cal_probe_points) - self.cal_helper = None - if config.get("extra_points", None) is not None: - self.cal_helper = probe.ProbePointsHelper( - config, self.cal_finalize, option_name="extra_points" - ) - cal_probe_points.extend(self.cal_helper.get_probe_points()) - self.cal_helper.update_probe_points(cal_probe_points, 3) - self.ad_helper = probe.ProbePointsHelper(config, self.ad_finalize) - self.ad_helper.update_probe_points(cal_probe_points, 3) - self.cal_conf_avg_len = config.getint("averaging_len", 3, minval=1) - self.ad_conf_delta = config.getfloat( - "autodetect_delta", 1.0, minval=0.1 - ) - if ( - config.get("autodetect_delta", None) is not None - or self.z_positions is None - ) and self.numpy is None: - raise config.error(self.err_missing_numpy) - + self.z_helper = ZAdjustHelper(config, len(self.z_positions)) # Register Z_TILT_ADJUST command gcode = self.printer.lookup_object("gcode") gcode.register_command( @@ -235,44 +174,19 @@ def __init__(self, config): self.cmd_Z_TILT_ADJUST, desc=self.cmd_Z_TILT_ADJUST_help, ) - if self.cal_helper is not None: - gcode.register_command( - "Z_TILT_CALIBRATE", - self.cmd_Z_TILT_CALIBRATE, - desc=self.cmd_Z_TILT_CALIBRATE_help, - ) - gcode.register_command( - "Z_TILT_AUTODETECT", - self.cmd_Z_TILT_AUTODETECT, - desc=self.cmd_Z_TILT_AUTODETECT_help, - ) cmd_Z_TILT_ADJUST_help = "Adjust the Z tilt" - cmd_Z_TILT_CALIBRATE_help = ( - "Calibrate Z tilt with additional probing " "points" - ) - cmd_Z_TILT_AUTODETECT_help = "Autodetect pivot point of Z motors" - err_missing_numpy = ( - "Failed to import `numpy` module, make sure it was " - "installed via `~/klippy-env/bin/pip install`" - ) def cmd_Z_TILT_ADJUST(self, gcmd): - if self.z_positions is None: - gcmd.respond_info( - "No z_positions configured. Run Z_TILT_AUTODETECT first" - ) - return self.z_status.reset() self.retry_helper.start(gcmd) self.probe_helper.start_probe(gcmd) - def perform_coordinate_descent(self, offsets, positions): + def probe_finalize(self, offsets, positions): # Setup for coordinate descent analysis z_offset = offsets[2] logging.info("Calculating bed tilt with: %s", positions) params = {"x_adjust": 0.0, "y_adjust": 0.0, "z_adjust": z_offset} - # Perform coordinate descent def adjusted_height(pos, params): x, y, z = pos @@ -289,21 +203,12 @@ def errorfunc(params): total_error += adjusted_height(pos, params) ** 2 return total_error - new_params = mathutil.coordinate_descent( - params.keys(), params, errorfunc - ) - new_params = mathutil.coordinate_descent( params.keys(), params, errorfunc ) # Apply results speed = self.probe_helper.get_lift_speed() logging.info("Calculated bed tilt parameters: %s", new_params) - return new_params - - def apply_adjustments(self, offsets, new_params): - z_offset = offsets[2] - speed = self.probe_helper.get_lift_speed() x_adjust = new_params["x_adjust"] y_adjust = new_params["y_adjust"] z_adjust = ( @@ -316,212 +221,10 @@ def apply_adjustments(self, offsets, new_params): x * x_adjust + y * y_adjust + z_adjust for x, y in self.z_positions ] self.z_helper.adjust_steppers(adjustments, speed) - - def probe_finalize(self, offsets, positions): - if self.z_offsets is not None: - positions = [ - [p[0], p[1], p[2] - o] - for (p, o) in zip(positions, self.z_offsets) - ] - new_params = self.perform_coordinate_descent(offsets, positions) - self.apply_adjustments(offsets, new_params) return self.z_status.check_retry_result( self.retry_helper.check_retry([p[2] for p in positions]) ) - def cmd_Z_TILT_CALIBRATE(self, gcmd): - if self.numpy is None: - gcmd.respond_info(self.err_missing_numpy) - return - self.cal_avg_len = gcmd.get_int("AVGLEN", self.cal_conf_avg_len) - self.cal_gcmd = gcmd - self.cal_runs = [] - self.cal_helper.start_probe(gcmd) - - def cal_finalize(self, offsets, positions): - np = self.numpy - avlen = self.cal_avg_len - new_params = self.perform_coordinate_descent(offsets, positions) - self.apply_adjustments(offsets, new_params) - self.cal_runs.append([p[2] for p in positions]) - if len(self.cal_runs) < avlen + 1: - return "retry" - prev_error = np.std(self.cal_runs[-avlen - 1 : -1], axis=0) - prev_error = np.std(prev_error) - this_error = np.std(self.cal_runs[-avlen:], axis=0) - this_error = np.std(this_error) - self.cal_gcmd.respond_info( - "previous error: %.6f current error: %.6f" - % (prev_error, this_error) - ) - if this_error < prev_error: - return "retry" - z_offsets = np.mean(self.cal_runs[-avlen:], axis=0) - z_offsets = [z - offsets[2] for z in z_offsets] - self.z_offsets = z_offsets - s_zoff = "" - for off in z_offsets[0 : self.num_probe_points]: - s_zoff += "%.6f, " % off - s_zoff = s_zoff[:-2] - self.cal_gcmd.respond_info("final z_offsets are: %s" % (s_zoff)) - configfile = self.printer.lookup_object("configfile") - section = self.section - configfile.set(section, "z_offsets", s_zoff) - self.cal_gcmd.respond_info( - "The SAVE_CONFIG command will update the printer config\n" - "file with these parameters and restart the printer." - ) - - def ad_init(self): - self.ad_phase = 0 - self.ad_params = [] - - def cmd_Z_TILT_AUTODETECT(self, gcmd): - if self.numpy is None: - gcmd.respond_info(self.err_missing_numpy) - self.cal_avg_len = gcmd.get_int("AVGLEN", self.cal_conf_avg_len) - self.ad_delta = gcmd.get_float("DELTA", self.ad_conf_delta, minval=0.1) - self.ad_init() - self.ad_gcmd = gcmd - self.ad_runs = [] - self.ad_points = [] - self.ad_error = None - self.ad_helper.start_probe(gcmd) - - ad_adjustments = [ - [0.5, -0.5, -0.5], # p1 up - [-1, 1, 0], # p2 up - [0, -1, 1], # p3 up - [0, 1, 0], # p3 + p2 up - [1, -1, 0], # p3 + p1 up - [0, 1, -1], # p2 + p1 up - [-0.5, -0.5, 0.5], # back to level - ] - - def ad_finalize(self, offsets, positions): - np = self.numpy - avlen = self.cal_avg_len - delta = self.ad_delta - speed = self.probe_helper.get_lift_speed() - new_params = self.perform_coordinate_descent(offsets, positions) - if self.ad_phase in range(1, 4): - new_params["z_adjust"] -= delta / 2 - if self.ad_phase in range(4, 7): - new_params["z_adjust"] += delta / 2 - if self.ad_phase == 0: - self.ad_points.append( - [z for _, _, z in positions[: self.num_probe_points]] - ) - self.ad_params.append(new_params) - adjustments = [_a * delta for _a in self.ad_adjustments[self.ad_phase]] - self.z_helper.adjust_steppers(adjustments, speed) - if self.ad_phase < 6: - self.ad_phase += 1 - return "retry" - # calculcate results - p = [] - for i in range(7): - p.append(params_to_normal_form(np, self.ad_params[i], offsets)) - - # This is how it works. - # To find the pivot point, we take 3 planes: - # a) the original untilted plane - # b) the plane with one motor raised, on one corner opposite the - # one we want to determine the pivot point of - # c) the plane with the other motor opposite the one we want to - # determine the pivot point raised - # The intersection of all 3 planes is a point very near the pivot - # point we search for. If the pivot point would be a point on the - # bed surface, we would already be done. But as the actual pivot - # point is in most cases below the bed, the intersection of the 3 - # points is behind or in front of the actual point (in X/Y). To - # compensate for this error, we do the same calculation again, but - # with the planes b) and c) tilted in the opposite direction and - # take the average of the 2 points. - - z_p1 = ( - intersect_3_planes(np, p[0], p[2], p[3])[:2], - intersect_3_planes(np, p[0], p[1], p[3])[:2], - intersect_3_planes(np, p[0], p[1], p[2])[:2], - ) - - z_p2 = ( - intersect_3_planes(np, p[0], p[5], p[6])[:2], - intersect_3_planes(np, p[0], p[4], p[6])[:2], - intersect_3_planes(np, p[0], p[4], p[5])[:2], - ) - - # take the average of positive and negative measurement - z_pos = [] - for _zp1, _zp2 in zip(z_p1, z_p2): - _z = [] - for _z1, _z2 in zip(_zp1, _zp2): - _z.append((_z1 + _z2) / 2) - z_pos.append(_z) - s_zpos = "" - for zp in z_pos: - s_zpos += "%.6f, %.6f\n" % tuple(zp) - self.ad_gcmd.respond_info("current estimated z_positions %s" % (s_zpos)) - self.ad_runs.append(z_pos) - if len(self.ad_runs) >= avlen: - self.z_positions = np.mean(self.ad_runs[-avlen:], axis=0) - else: - self.z_positions = np.mean(self.ad_runs, axis=0) - - # We got a first estimate of the pivot points. Now apply the - # adjustemts to all motors and repeat the process until the result - # converges. We determine convergence by keeping track of the last - # + 1 runs and compare the standard deviation over that - # len between the last two runs. When the error stops to decrease, we - # are done. The final z_positions are determined by calculating the - # average over the last calculated positions. - - self.apply_adjustments(offsets, self.ad_params[0]) - if len(self.ad_runs) >= avlen: - errors = np.std(self.ad_runs[-avlen:], axis=0) - error = np.std(errors) - if self.ad_error is None: - self.ad_gcmd.respond_info("current error: %.6f" % (error)) - else: - self.ad_gcmd.respond_info( - "previous error: %.6f current error: %.6f" - % (self.ad_error, error) - ) - if self.ad_error is not None: - if error >= self.ad_error: - self.ad_finalize_done(offsets) - return - self.ad_error = error - # restart - self.ad_init() - return "retry" - - def ad_finalize_done(self, offsets): - np = self.numpy - avlen = self.cal_avg_len - # calculate probe point z offsets - z_offsets = np.mean(self.ad_points[-avlen:], axis=0) - z_offsets = [z - offsets[2] for z in z_offsets] - self.z_offsets = z_offsets - logging.info("final z_offsets %s", (z_offsets)) - configfile = self.printer.lookup_object("configfile") - section = self.section - s_zoff = "" - for off in z_offsets: - s_zoff += "%.6f, " % off - s_zoff = s_zoff[:-2] - configfile.set(section, "z_offsets", s_zoff) - s_zpos = "" - for zpos in self.z_positions: - s_zpos += "%.6f, %.6f\n" % tuple(zpos) - configfile.set(section, "z_positions", s_zpos) - self.ad_gcmd.respond_info("final z_positions are %s" % (s_zpos)) - self.ad_gcmd.respond_info("final z_offsets are: %s" % (s_zoff)) - self.ad_gcmd.respond_info( - "The SAVE_CONFIG command will update the printer config\n" - "file with these parameters and restart the printer." - ) - def get_status(self, eventtime): return self.z_status.get_status(eventtime) diff --git a/klippy/extras/z_tilt_ng.py b/klippy/extras/z_tilt_ng.py new file mode 100644 index 000000000..f632aa856 --- /dev/null +++ b/klippy/extras/z_tilt_ng.py @@ -0,0 +1,530 @@ +# Mechanical bed tilt calibration with multiple Z steppers +# +# Copyright (C) 2018-2019 Kevin O'Connor +# +# This file may be distributed under the terms of the GNU GPLv3 license. +import logging +import mathutil +import importlib +from . import probe + + +def params_to_normal_form(np, params, offsets): + v = np.array([offsets[0], offsets[1], params["z_adjust"]]) + r = np.array([1, 0, params["x_adjust"]]) + s = np.array([0, 1, params["y_adjust"]]) + cp = np.cross(r, s) + return np.append(cp, np.dot(cp, v)) + + +def intersect_3_planes(np, p1, p2, p3): + a = np.array([p1[0:3], p2[0:3], p3[0:3]]) + b = np.array([p1[3], p2[3], p3[3]]) + sol = np.linalg.solve(a, b) + return sol + + +class ZAdjustHelper: + def __init__(self, config, z_count): + self.printer = config.get_printer() + self.name = config.get_name() + self.z_count = z_count + self.z_steppers = [] + self.printer.register_event_handler( + "klippy:connect", self.handle_connect + ) + + def handle_connect(self): + kin = self.printer.lookup_object("toolhead").get_kinematics() + z_steppers = [s for s in kin.get_steppers() if s.is_active_axis("z")] + if self.z_count is None: + if len(z_steppers) != 3: + raise self.printer.config_error( + "%s z_positions needs exactly 3 items for calibration" + % (self.name) + ) + elif len(z_steppers) != self.z_count: + raise self.printer.config_error( + "%s z_positions needs exactly %d items" + % (self.name, len(z_steppers)) + ) + if len(z_steppers) < 2: + raise self.printer.config_error( + "%s requires multiple z steppers" % (self.name,) + ) + self.z_steppers = z_steppers + + def adjust_steppers(self, adjustments, speed): + toolhead = self.printer.lookup_object("toolhead") + gcode = self.printer.lookup_object("gcode") + curpos = toolhead.get_position() + # Report on movements + stepstrs = [ + "%s = %.6f" % (s.get_name(), a) + for s, a in zip(self.z_steppers, adjustments) + ] + msg = "Making the following Z adjustments:\n%s" % ("\n".join(stepstrs),) + gcode.respond_info(msg) + # Disable Z stepper movements + toolhead.flush_step_generation() + for s in self.z_steppers: + s.set_trapq(None) + # Move each z stepper (sorted from lowest to highest) until they match + positions = [(-a, s) for a, s in zip(adjustments, self.z_steppers)] + positions.sort(key=(lambda k: k[0])) + first_stepper_offset, first_stepper = positions[0] + z_low = curpos[2] - first_stepper_offset + for i in range(len(positions) - 1): + stepper_offset, stepper = positions[i] + next_stepper_offset, next_stepper = positions[i + 1] + toolhead.flush_step_generation() + stepper.set_trapq(toolhead.get_trapq()) + curpos[2] = z_low + next_stepper_offset + try: + toolhead.move(curpos, speed) + toolhead.set_position(curpos) + except: + logging.exception("ZAdjustHelper adjust_steppers") + toolhead.flush_step_generation() + for s in self.z_steppers: + s.set_trapq(toolhead.get_trapq()) + raise + # Z should now be level - do final cleanup + last_stepper_offset, last_stepper = positions[-1] + toolhead.flush_step_generation() + last_stepper.set_trapq(toolhead.get_trapq()) + curpos[2] += first_stepper_offset + toolhead.set_position(curpos) + + +class ZAdjustStatus: + def __init__(self, printer): + self.applied = False + printer.register_event_handler( + "stepper_enable:motor_off", self._motor_off + ) + + def check_retry_result(self, retry_result): + if retry_result == "done": + self.applied = True + return retry_result + + def reset(self): + self.applied = False + + def get_status(self, eventtime): + return {"applied": self.applied} + + def _motor_off(self, print_time): + self.reset() + + +class RetryHelper: + def __init__(self, config, error_msg_extra=""): + self.gcode = config.get_printer().lookup_object("gcode") + self.default_max_retries = config.getint("retries", 0, minval=0) + self.default_retry_tolerance = config.getfloat( + "retry_tolerance", 0.0, above=0.0 + ) + self.value_label = "Probed points range" + self.error_msg_extra = error_msg_extra + + def start(self, gcmd): + self.max_retries = gcmd.get_int( + "RETRIES", self.default_max_retries, minval=0, maxval=30 + ) + self.retry_tolerance = gcmd.get_float( + "RETRY_TOLERANCE", + self.default_retry_tolerance, + minval=0.0, + maxval=1.0, + ) + self.current_retry = 0 + self.previous = None + self.increasing = 0 + + def check_increase(self, error): + if self.previous and error > self.previous + 0.0000001: + self.increasing += 1 + elif self.increasing > 0: + self.increasing -= 1 + self.previous = error + return self.increasing > 1 + + def check_retry(self, z_positions): + if self.max_retries == 0: + return + error = round(max(z_positions) - min(z_positions), 6) + self.gcode.respond_info( + "Retries: %d/%d %s: %0.6f tolerance: %0.6f" + % ( + self.current_retry, + self.max_retries, + self.value_label, + error, + self.retry_tolerance, + ) + ) + if self.check_increase(error): + raise self.gcode.error( + "Retries aborting: %s is increasing. %s" + % (self.value_label, self.error_msg_extra) + ) + if error <= self.retry_tolerance: + return "done" + self.current_retry += 1 + if self.current_retry > self.max_retries: + raise self.gcode.error("Too many retries") + return "retry" + + +class ZTilt: + def __init__(self, config): + self.printer = config.get_printer() + self.section = config.get_name() + + try: + self.numpy = importlib.import_module("numpy") + except ImportError: + logging.info( + "numpy not installed, Z_TILT_CALIBRATE will not be " "available" + ) + self.numpy = None + + self.z_positions = config.getlists( + "z_positions", seps=(",", "\n"), parser=float, count=2 + ) + z_count = len(self.z_positions) + + self.retry_helper = RetryHelper(config) + self.probe_helper = probe.ProbePointsHelper(config, self.probe_finalize) + self.probe_helper.minimum_points(2) + + self.z_offsets = config.getlists( + "z_offsets", parser=float, count=z_count, default=None + ) + + self.z_status = ZAdjustStatus(self.printer) + self.z_helper = ZAdjustHelper(config, z_count) + # probe points for calibrate/autodetect + cal_probe_points = list(self.probe_helper.get_probe_points()) + self.num_probe_points = len(cal_probe_points) + self.cal_helper = None + if config.get("extra_points", None) is not None: + self.cal_helper = probe.ProbePointsHelper( + config, self.cal_finalize, option_name="extra_points" + ) + cal_probe_points.extend(self.cal_helper.get_probe_points()) + self.cal_helper.update_probe_points(cal_probe_points, 3) + self.ad_helper = probe.ProbePointsHelper(config, self.ad_finalize) + self.ad_helper.update_probe_points(cal_probe_points, 3) + self.cal_conf_avg_len = config.getint("averaging_len", 3, minval=1) + self.ad_conf_delta = config.getfloat( + "autodetect_delta", 1.0, minval=0.1 + ) + if ( + config.get("autodetect_delta", None) is not None + or self.z_positions is None + ) and self.numpy is None: + raise config.error(self.err_missing_numpy) + + # Register Z_TILT_ADJUST command + gcode = self.printer.lookup_object("gcode") + gcode.register_command( + "Z_TILT_ADJUST", + self.cmd_Z_TILT_ADJUST, + desc=self.cmd_Z_TILT_ADJUST_help, + ) + if self.cal_helper is not None: + gcode.register_command( + "Z_TILT_CALIBRATE", + self.cmd_Z_TILT_CALIBRATE, + desc=self.cmd_Z_TILT_CALIBRATE_help, + ) + gcode.register_command( + "Z_TILT_AUTODETECT", + self.cmd_Z_TILT_AUTODETECT, + desc=self.cmd_Z_TILT_AUTODETECT_help, + ) + + cmd_Z_TILT_ADJUST_help = "Adjust the Z tilt" + cmd_Z_TILT_CALIBRATE_help = ( + "Calibrate Z tilt with additional probing " "points" + ) + cmd_Z_TILT_AUTODETECT_help = "Autodetect pivot point of Z motors" + err_missing_numpy = ( + "Failed to import `numpy` module, make sure it was " + "installed via `~/klippy-env/bin/pip install`" + ) + + def cmd_Z_TILT_ADJUST(self, gcmd): + if self.z_positions is None: + gcmd.respond_info( + "No z_positions configured. Run Z_TILT_AUTODETECT first" + ) + return + self.z_status.reset() + self.retry_helper.start(gcmd) + self.probe_helper.start_probe(gcmd) + + def perform_coordinate_descent(self, offsets, positions): + # Setup for coordinate descent analysis + z_offset = offsets[2] + logging.info("Calculating bed tilt with: %s", positions) + params = {"x_adjust": 0.0, "y_adjust": 0.0, "z_adjust": z_offset} + + # Perform coordinate descent + def adjusted_height(pos, params): + x, y, z = pos + return ( + z + - x * params["x_adjust"] + - y * params["y_adjust"] + - params["z_adjust"] + ) + + def errorfunc(params): + total_error = 0.0 + for pos in positions: + total_error += adjusted_height(pos, params) ** 2 + return total_error + + new_params = mathutil.coordinate_descent( + params.keys(), params, errorfunc + ) + + new_params = mathutil.coordinate_descent( + params.keys(), params, errorfunc + ) + # Apply results + speed = self.probe_helper.get_lift_speed() + logging.info("Calculated bed tilt parameters: %s", new_params) + return new_params + + def apply_adjustments(self, offsets, new_params): + z_offset = offsets[2] + speed = self.probe_helper.get_lift_speed() + x_adjust = new_params["x_adjust"] + y_adjust = new_params["y_adjust"] + z_adjust = ( + new_params["z_adjust"] + - z_offset + - x_adjust * offsets[0] + - y_adjust * offsets[1] + ) + adjustments = [ + x * x_adjust + y * y_adjust + z_adjust for x, y in self.z_positions + ] + self.z_helper.adjust_steppers(adjustments, speed) + + def probe_finalize(self, offsets, positions): + if self.z_offsets is not None: + positions = [ + [p[0], p[1], p[2] - o] + for (p, o) in zip(positions, self.z_offsets) + ] + new_params = self.perform_coordinate_descent(offsets, positions) + self.apply_adjustments(offsets, new_params) + return self.z_status.check_retry_result( + self.retry_helper.check_retry([p[2] for p in positions]) + ) + + def cmd_Z_TILT_CALIBRATE(self, gcmd): + if self.numpy is None: + gcmd.respond_info(self.err_missing_numpy) + return + self.cal_avg_len = gcmd.get_int("AVGLEN", self.cal_conf_avg_len) + self.cal_gcmd = gcmd + self.cal_runs = [] + self.cal_helper.start_probe(gcmd) + + def cal_finalize(self, offsets, positions): + np = self.numpy + avlen = self.cal_avg_len + new_params = self.perform_coordinate_descent(offsets, positions) + self.apply_adjustments(offsets, new_params) + self.cal_runs.append([p[2] for p in positions]) + if len(self.cal_runs) < avlen + 1: + return "retry" + prev_error = np.std(self.cal_runs[-avlen - 1 : -1], axis=0) + prev_error = np.std(prev_error) + this_error = np.std(self.cal_runs[-avlen:], axis=0) + this_error = np.std(this_error) + self.cal_gcmd.respond_info( + "previous error: %.6f current error: %.6f" + % (prev_error, this_error) + ) + if this_error < prev_error: + return "retry" + z_offsets = np.mean(self.cal_runs[-avlen:], axis=0) + z_offsets = [z - offsets[2] for z in z_offsets] + self.z_offsets = z_offsets + s_zoff = "" + for off in z_offsets[0 : self.num_probe_points]: + s_zoff += "%.6f, " % off + s_zoff = s_zoff[:-2] + self.cal_gcmd.respond_info("final z_offsets are: %s" % (s_zoff)) + configfile = self.printer.lookup_object("configfile") + section = self.section + configfile.set(section, "z_offsets", s_zoff) + self.cal_gcmd.respond_info( + "The SAVE_CONFIG command will update the printer config\n" + "file with these parameters and restart the printer." + ) + + def ad_init(self): + self.ad_phase = 0 + self.ad_params = [] + + def cmd_Z_TILT_AUTODETECT(self, gcmd): + if self.numpy is None: + gcmd.respond_info(self.err_missing_numpy) + self.cal_avg_len = gcmd.get_int("AVGLEN", self.cal_conf_avg_len) + self.ad_delta = gcmd.get_float("DELTA", self.ad_conf_delta, minval=0.1) + self.ad_init() + self.ad_gcmd = gcmd + self.ad_runs = [] + self.ad_points = [] + self.ad_error = None + self.ad_helper.start_probe(gcmd) + + ad_adjustments = [ + [0.5, -0.5, -0.5], # p1 up + [-1, 1, 0], # p2 up + [0, -1, 1], # p3 up + [0, 1, 0], # p3 + p2 up + [1, -1, 0], # p3 + p1 up + [0, 1, -1], # p2 + p1 up + [-0.5, -0.5, 0.5], # back to level + ] + + def ad_finalize(self, offsets, positions): + np = self.numpy + avlen = self.cal_avg_len + delta = self.ad_delta + speed = self.probe_helper.get_lift_speed() + new_params = self.perform_coordinate_descent(offsets, positions) + if self.ad_phase in range(1, 4): + new_params["z_adjust"] -= delta / 2 + if self.ad_phase in range(4, 7): + new_params["z_adjust"] += delta / 2 + if self.ad_phase == 0: + self.ad_points.append( + [z for _, _, z in positions[: self.num_probe_points]] + ) + self.ad_params.append(new_params) + adjustments = [_a * delta for _a in self.ad_adjustments[self.ad_phase]] + self.z_helper.adjust_steppers(adjustments, speed) + if self.ad_phase < 6: + self.ad_phase += 1 + return "retry" + # calculcate results + p = [] + for i in range(7): + p.append(params_to_normal_form(np, self.ad_params[i], offsets)) + + # This is how it works. + # To find the pivot point, we take 3 planes: + # a) the original untilted plane + # b) the plane with one motor raised, on one corner opposite the + # one we want to determine the pivot point of + # c) the plane with the other motor opposite the one we want to + # determine the pivot point raised + # The intersection of all 3 planes is a point very near the pivot + # point we search for. If the pivot point would be a point on the + # bed surface, we would already be done. But as the actual pivot + # point is in most cases below the bed, the intersection of the 3 + # points is behind or in front of the actual point (in X/Y). To + # compensate for this error, we do the same calculation again, but + # with the planes b) and c) tilted in the opposite direction and + # take the average of the 2 points. + + z_p1 = ( + intersect_3_planes(np, p[0], p[2], p[3])[:2], + intersect_3_planes(np, p[0], p[1], p[3])[:2], + intersect_3_planes(np, p[0], p[1], p[2])[:2], + ) + + z_p2 = ( + intersect_3_planes(np, p[0], p[5], p[6])[:2], + intersect_3_planes(np, p[0], p[4], p[6])[:2], + intersect_3_planes(np, p[0], p[4], p[5])[:2], + ) + + # take the average of positive and negative measurement + z_pos = [] + for _zp1, _zp2 in zip(z_p1, z_p2): + _z = [] + for _z1, _z2 in zip(_zp1, _zp2): + _z.append((_z1 + _z2) / 2) + z_pos.append(_z) + s_zpos = "" + for zp in z_pos: + s_zpos += "%.6f, %.6f\n" % tuple(zp) + self.ad_gcmd.respond_info("current estimated z_positions %s" % (s_zpos)) + self.ad_runs.append(z_pos) + if len(self.ad_runs) >= avlen: + self.z_positions = np.mean(self.ad_runs[-avlen:], axis=0) + else: + self.z_positions = np.mean(self.ad_runs, axis=0) + + # We got a first estimate of the pivot points. Now apply the + # adjustemts to all motors and repeat the process until the result + # converges. We determine convergence by keeping track of the last + # + 1 runs and compare the standard deviation over that + # len between the last two runs. When the error stops to decrease, we + # are done. The final z_positions are determined by calculating the + # average over the last calculated positions. + + self.apply_adjustments(offsets, self.ad_params[0]) + if len(self.ad_runs) >= avlen: + errors = np.std(self.ad_runs[-avlen:], axis=0) + error = np.std(errors) + if self.ad_error is None: + self.ad_gcmd.respond_info("current error: %.6f" % (error)) + else: + self.ad_gcmd.respond_info( + "previous error: %.6f current error: %.6f" + % (self.ad_error, error) + ) + if self.ad_error is not None: + if error >= self.ad_error: + self.ad_finalize_done(offsets) + return + self.ad_error = error + # restart + self.ad_init() + return "retry" + + def ad_finalize_done(self, offsets): + np = self.numpy + avlen = self.cal_avg_len + # calculate probe point z offsets + z_offsets = np.mean(self.ad_points[-avlen:], axis=0) + z_offsets = [z - offsets[2] for z in z_offsets] + self.z_offsets = z_offsets + logging.info("final z_offsets %s", (z_offsets)) + configfile = self.printer.lookup_object("configfile") + section = self.section + s_zoff = "" + for off in z_offsets: + s_zoff += "%.6f, " % off + s_zoff = s_zoff[:-2] + configfile.set(section, "z_offsets", s_zoff) + s_zpos = "" + for zpos in self.z_positions: + s_zpos += "%.6f, %.6f\n" % tuple(zpos) + configfile.set(section, "z_positions", s_zpos) + self.ad_gcmd.respond_info("final z_positions are %s" % (s_zpos)) + self.ad_gcmd.respond_info("final z_offsets are: %s" % (s_zoff)) + self.ad_gcmd.respond_info( + "The SAVE_CONFIG command will update the printer config\n" + "file with these parameters and restart the printer." + ) + + def get_status(self, eventtime): + return self.z_status.get_status(eventtime) + + +def load_config(config): + return ZTilt(config) diff --git a/test/klippy/z_tilt_ng.cfg b/test/klippy/z_tilt_ng.cfg new file mode 100644 index 000000000..300d3089f --- /dev/null +++ b/test/klippy/z_tilt_ng.cfg @@ -0,0 +1,101 @@ +# Test config for z_tilt and quad_gantry_level +[stepper_x] +step_pin: PF0 +dir_pin: PF1 +enable_pin: !PD7 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PE5 +position_endstop: 0 +position_max: 250 +homing_speed: 50 + +[stepper_y] +step_pin: PF6 +dir_pin: !PF7 +enable_pin: !PF2 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PJ1 +position_endstop: 0 +position_max: 250 +homing_speed: 50 + +[stepper_z] +step_pin: PL3 +dir_pin: PL1 +enable_pin: !PK0 +microsteps: 16 +rotation_distance: 8 +endstop_pin: ^PD3 +position_endstop: 0.5 +position_max: 250 + +[stepper_z1] +step_pin: PC1 +dir_pin: PC3 +enable_pin: !PC7 +microsteps: 16 +rotation_distance: 8 +endstop_pin: ^PD2 + +[stepper_z2] +step_pin: PH1 +dir_pin: PH0 +enable_pin: !PA1 +microsteps: 16 +rotation_distance: 8 + +[z_tilt_ng] +z_positions: + -55,-7 + -55,320 + 305,-7 +points: + 50,50 + 50,195 + 195,195 +extra_points: + 50,50 + 50,195 + 195,195 + +[extruder] +step_pin: PA4 +dir_pin: PA6 +enable_pin: !PA2 +microsteps: 16 +rotation_distance: 33.5 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +heater_pin: PB4 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PK5 +control: pid +pid_Kp: 22.2 +pid_Ki: 1.08 +pid_Kd: 114 +min_temp: 0 +max_temp: 250 + +[heater_bed] +heater_pin: PH5 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PK6 +control: watermark +min_temp: 0 +max_temp: 130 + +[probe] +pin: PH6 +z_offset: 1.15 + +[mcu] +serial: /dev/ttyACM0 + +[printer] +kinematics: cartesian +max_velocity: 300 +max_accel: 3000 +max_z_velocity: 5 +max_z_accel: 100 diff --git a/test/klippy/z_tilt_ng.test b/test/klippy/z_tilt_ng.test new file mode 100644 index 000000000..2649097ad --- /dev/null +++ b/test/klippy/z_tilt_ng.test @@ -0,0 +1,28 @@ +# Test case for z_tilt_ng +CONFIG z_tilt_ng.cfg +DICTIONARY atmega2560.dict + +# Start by homing the printer. +G28 +G1 Z5 X10 Y10 F6000 +M400 +GET_POSITION + +# Run Z_TILT_ADJUST in manual mode +Z_TILT_ADJUST METHOD=MANUAL +G1 Z2.909972 +ACCEPT +G1 Z2.924972 +ACCEPT +G1 Z2.959972 +ACCEPT +G1 Z2.924972 +ACCEPT + +# Report position +G1 Z5 X10 Y10 F6000 +M400 +GET_POSITION + +# Run again in automatic mode +Z_TILT_ADJUST From f0753bd3381a86826082d5bf7a349c1f0b9f7e48 Mon Sep 17 00:00:00 2001 From: Razor Date: Wed, 13 Dec 2023 18:13:39 +0100 Subject: [PATCH 44/53] docs: add rp2040 to internal temp sensor list (#6426) Signed-off-by: Levi Szabo --- docs/Config_Reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index bcccebe06..c81dc0a2c 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -2559,7 +2559,7 @@ sensor_type: LM75 ### Builtin micro-controller temperature sensor -The atsam, atsamd, and stm32 micro-controllers contain an internal +The atsam, atsamd, stm32 and rp2040 micro-controllers contain an internal temperature sensor. One can use the "temperature_mcu" sensor to monitor these temperatures. From 19862bc3b71437770ac8b847b106c50308e99fb9 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 11 Dec 2023 12:42:11 -0500 Subject: [PATCH 45/53] toolhead: Track separate time for flush_step_generation() and need_flush_time Introduce a new step_gen_time variable for flush_step_generation(). This allows need_flush_time to be set to future times without interfering with flush_step_generation(). Signed-off-by: Kevin O'Connor --- klippy/toolhead.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/klippy/toolhead.py b/klippy/toolhead.py index a04451c67..3e9b339ad 100644 --- a/klippy/toolhead.py +++ b/klippy/toolhead.py @@ -238,7 +238,7 @@ def __init__(self, config): # Flush tracking self.flush_timer = self.reactor.register_timer(self._flush_handler) self.do_kick_flush_timer = True - self.last_flush_time = self.need_flush_time = 0. + self.last_flush_time = self.need_flush_time = self.step_gen_time = 0. # Kinematic step generation scan window time tracking self.kin_flush_delay = SDS_CHECK_TIME self.kin_flush_times = [] @@ -341,7 +341,8 @@ def _process_moves(self, moves): # Generate steps for moves if self.special_queuing_state: self._update_drip_move_time(next_move_time) - self.note_kinematic_activity(next_move_time + self.kin_flush_delay) + self.note_kinematic_activity(next_move_time + self.kin_flush_delay, + set_step_gen_time=True) self._advance_move_time(next_move_time) def _flush_lookahead(self): # Transit from "NeedPrime"/"Priming"/"Drip"/main state to "NeedPrime" @@ -352,7 +353,7 @@ def _flush_lookahead(self): self.check_stall_time = 0. def flush_step_generation(self): self._flush_lookahead() - self._advance_flush_time(self.need_flush_time) + self._advance_flush_time(self.step_gen_time) def get_last_move_time(self): if self.special_queuing_state: self._flush_lookahead() @@ -492,7 +493,8 @@ def _update_drip_move_time(self, next_print_time): self.drip_completion.wait(curtime + wait_time) continue npt = min(self.print_time + DRIP_SEGMENT_TIME, next_print_time) - self.note_kinematic_activity(npt + self.kin_flush_delay) + self.note_kinematic_activity(npt + self.kin_flush_delay, + set_step_gen_time=True) self._advance_move_time(npt) def drip_move(self, newpos, speed, drip_completion): self.dwell(self.kin_flush_delay) @@ -574,8 +576,10 @@ def register_lookahead_callback(self, callback): callback(self.get_last_move_time()) return last_move.timing_callbacks.append(callback) - def note_kinematic_activity(self, kin_time): + def note_kinematic_activity(self, kin_time, set_step_gen_time=False): self.need_flush_time = max(self.need_flush_time, kin_time) + if set_step_gen_time: + self.step_gen_time = max(self.step_gen_time, kin_time) if self.do_kick_flush_timer: self.do_kick_flush_timer = False self.reactor.update_timer(self.flush_timer, self.reactor.NOW) From 1e5f688b5363c9a8d6d0363a6a53fd6418919b9d Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Thu, 30 Nov 2023 11:12:55 -0500 Subject: [PATCH 46/53] mcu: Add support for registering callbacks during move queue flush Support notification callbacks each time the mcu move queue is flushed. Signed-off-by: Kevin O'Connor --- klippy/mcu.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/klippy/mcu.py b/klippy/mcu.py index 68179bdd4..2d8bacc4a 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -606,6 +606,7 @@ def __init__(self, config, clocksync): self._reserved_move_slots = 0 self._stepqueues = [] self._steppersync = None + self._flush_callbacks = [] # Stats self._get_status_info = {} self._stats_sumsq_base = 0. @@ -854,10 +855,6 @@ def get_query_slot(self, oid): slot = self.seconds_to_clock(oid * .01) t = int(self.estimated_print_time(self._reactor.monotonic()) + 1.5) return self.print_time_to_clock(t) + slot - def register_stepqueue(self, stepqueue): - self._stepqueues.append(stepqueue) - def request_move_queue_slot(self): - self._reserved_move_slots += 1 def seconds_to_clock(self, time): return int(time * self._mcu_freq) def get_max_stepper_error(self): @@ -951,19 +948,21 @@ def _firmware_restart(self, force=False): self._restart_arduino() def _firmware_restart_bridge(self): self._firmware_restart(True) - # Misc external commands - def is_fileoutput(self): - return self._printer.get_start_args().get('debugoutput') is not None - def is_shutdown(self): - return self._is_shutdown - def get_shutdown_clock(self): - return self._shutdown_clock + # Move queue tracking + def register_stepqueue(self, stepqueue): + self._stepqueues.append(stepqueue) + def request_move_queue_slot(self): + self._reserved_move_slots += 1 + def register_flush_callback(self, callback): + self._flush_callbacks.append(callback) def flush_moves(self, print_time): if self._steppersync is None: return clock = self.print_time_to_clock(print_time) if clock < 0: return + for cb in self._flush_callbacks: + cb(print_time, clock) ret = self._ffi_lib.steppersync_flush(self._steppersync, clock) if ret: raise error("Internal error in MCU '%s' stepcompress" @@ -981,6 +980,13 @@ def check_active(self, print_time, eventtime): self._name, eventtime) self._printer.invoke_shutdown("Lost communication with MCU '%s'" % ( self._name,)) + # Misc external commands + def is_fileoutput(self): + return self._printer.get_start_args().get('debugoutput') is not None + def is_shutdown(self): + return self._is_shutdown + def get_shutdown_clock(self): + return self._shutdown_clock def get_status(self, eventtime=None): return dict(self._get_status_info) def stats(self, eventtime): From 2defd7374a8c7636e8fccaa1855eb8d75d7c070b Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Thu, 30 Nov 2023 11:06:16 -0500 Subject: [PATCH 47/53] pwm_tool: Add support for maximum_mcu_duration Implement the maximum_mcu_duration config parameter along with its associated queue flushing. Signed-off-by: Kevin O'Connor --- docs/Config_Reference.md | 1 + klippy/extras/pwm_tool.py | 79 ++++++++++++++++++++++++++------------- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index c81dc0a2c..313a50ecd 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -3149,6 +3149,7 @@ pin: # The pin to configure as an output. This parameter must be provided. #value: #shutdown_value: +#maximum_mcu_duration: #cycle_time: 0.100 #hardware_pwm: False #scale: diff --git a/klippy/extras/pwm_tool.py b/klippy/extras/pwm_tool.py index e3a7377c8..0b3dd0c09 100644 --- a/klippy/extras/pwm_tool.py +++ b/klippy/extras/pwm_tool.py @@ -5,8 +5,8 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import chelper -PIN_MIN_TIME = 0.100 MAX_SCHEDULE_TIME = 5.0 +CLOCK_SYNC_EXTRA_TIME = 0.050 class error(Exception): pass @@ -27,9 +27,15 @@ def __init__(self, pin_params): self._pin = pin_params['pin'] self._invert = pin_params['invert'] self._start_value = self._shutdown_value = float(self._invert) - self._last_clock = self._cycle_ticks = 0 + self._last_clock = self._last_value = self._default_value = 0 + self._duration_ticks = 0 self._pwm_max = 0. self._set_cmd_tag = None + self._toolhead = None + printer = self._mcu.get_printer() + printer.register_event_handler("klippy:connect", self._handle_connect) + def _handle_connect(self): + self._toolhead = self._mcu.get_printer().lookup_object("toolhead") def get_mcu(self): return self._mcu def setup_max_duration(self, max_duration): @@ -52,20 +58,26 @@ def _build_config(self): printtime = self._mcu.estimated_print_time(curtime) self._last_clock = self._mcu.print_time_to_clock(printtime + 0.200) cycle_ticks = self._mcu.seconds_to_clock(self._cycle_time) - mdur_ticks = self._mcu.seconds_to_clock(self._max_duration) - if mdur_ticks >= 1<<31: + if cycle_ticks >= 1<<31: + raise pins.error("PWM pin cycle time too large") + self._duration_ticks = self._mcu.seconds_to_clock(self._max_duration) + if self._duration_ticks >= 1<<31: raise pins.error("PWM pin max duration too large") + if self._duration_ticks: + self._mcu.register_flush_callback(self._flush_notification) if self._hardware_pwm: self._pwm_max = self._mcu.get_constant_float("PWM_MAX") + self._default_value = self._shutdown_value * self._pwm_max self._mcu.add_config_cmd( "config_pwm_out oid=%d pin=%s cycle_ticks=%d value=%d" " default_value=%d max_duration=%d" % (self._oid, self._pin, cycle_ticks, self._start_value * self._pwm_max, - self._shutdown_value * self._pwm_max, mdur_ticks)) - svalue = int(self._start_value * self._pwm_max + 0.5) + self._default_value, self._duration_ticks)) + self._last_value = int(self._start_value * self._pwm_max + 0.5) self._mcu.add_config_cmd("queue_pwm_out oid=%d clock=%d value=%d" - % (self._oid, self._last_clock, svalue), + % (self._oid, self._last_clock, + self._last_value), on_restart=True) self._set_cmd_tag = self._mcu.lookup_command( "queue_pwm_out oid=%c clock=%u value=%hu", @@ -74,39 +86,50 @@ def _build_config(self): # Software PWM if self._shutdown_value not in [0., 1.]: raise pins.error("shutdown value must be 0.0 or 1.0 on soft pwm") - if cycle_ticks >= 1<<31: - raise pins.error("PWM pin cycle time too large") self._mcu.add_config_cmd( "config_digital_out oid=%d pin=%s value=%d" " default_value=%d max_duration=%d" % (self._oid, self._pin, self._start_value >= 1.0, - self._shutdown_value >= 0.5, mdur_ticks)) + self._shutdown_value >= 0.5, self._duration_ticks)) + self._default_value = int(self._shutdown_value >= 0.5) * cycle_ticks self._mcu.add_config_cmd( "set_digital_out_pwm_cycle oid=%d cycle_ticks=%d" % (self._oid, cycle_ticks)) - self._cycle_ticks = cycle_ticks - svalue = int(self._start_value * cycle_ticks + 0.5) + self._pwm_max = float(cycle_ticks) + self._last_value = int(self._start_value * self._pwm_max + 0.5) self._mcu.add_config_cmd( "queue_digital_out oid=%d clock=%d on_ticks=%d" - % (self._oid, self._last_clock, svalue), is_init=True) + % (self._oid, self._last_clock, self._last_value), is_init=True) self._set_cmd_tag = self._mcu.lookup_command( "queue_digital_out oid=%c clock=%u on_ticks=%u", cq=cmd_queue).get_command_tag() - def set_pwm(self, print_time, value): - clock = self._mcu.print_time_to_clock(print_time) - minclock = self._last_clock - self._last_clock = clock - if self._invert: - value = 1. - value - max_count = self._cycle_ticks - if self._hardware_pwm: - max_count = self._pwm_max - v = int(max(0., min(1., value)) * max_count + 0.5) - data = (self._set_cmd_tag, self._oid, clock & 0xffffffff, v) + def _send_update(self, clock, val): + self._last_clock = clock = max(self._last_clock, clock) + self._last_value = val + data = (self._set_cmd_tag, self._oid, clock & 0xffffffff, val) ret = self._stepcompress_queue_mq_msg(self._stepqueue, clock, data, len(data)) if ret: raise error("Internal error in stepcompress") + # Notify toolhead so that it will flush this update + wakeclock = clock + if self._last_value != self._default_value: + # Continue flushing to resend time + wakeclock += self._duration_ticks + wake_print_time = self._mcu.clock_to_print_time(wakeclock) + self._toolhead.note_kinematic_activity(wake_print_time + + CLOCK_SYNC_EXTRA_TIME) + def set_pwm(self, print_time, value): + clock = self._mcu.print_time_to_clock(print_time) + if self._invert: + value = 1. - value + v = int(max(0., min(1., value)) * self._pwm_max + 0.5) + self._send_update(clock, v) + def _flush_notification(self, print_time, clock): + if self._last_value != self._default_value: + while clock >= self._last_clock + self._duration_ticks: + self._send_update(self._last_clock + self._duration_ticks, + self._last_value) class PrinterOutputPin: def __init__(self, config): @@ -121,7 +144,11 @@ def __init__(self, config): self.mcu_pin.setup_cycle_time(cycle_time, hardware_pwm) self.scale = config.getfloat('scale', 1., above=0.) self.last_print_time = 0. - self.mcu_pin.setup_max_duration(0.) + # Support mcu checking for maximum duration + max_mcu_duration = config.getfloat('maximum_mcu_duration', 0., + minval=0.500, + maxval=MAX_SCHEDULE_TIME) + self.mcu_pin.setup_max_duration(max_mcu_duration) # Determine start and shutdown values self.last_value = config.getfloat( 'value', 0., minval=0., maxval=self.scale) / self.scale @@ -143,8 +170,6 @@ def _set_pin(self, print_time, value): self.mcu_pin.set_pwm(print_time, value) self.last_value = value self.last_print_time = print_time - toolhead = self.printer.lookup_object('toolhead') - toolhead.note_kinematic_activity(print_time) cmd_SET_PIN_help = "Set the value of an output pin" def cmd_SET_PIN(self, gcmd): # Read requested value From 3417940fd82adf621f429f42289d3693ee832582 Mon Sep 17 00:00:00 2001 From: Sami Haahtinen Date: Sat, 16 Dec 2023 01:03:51 +0000 Subject: [PATCH 48/53] hall_filament_width_sensor: max filament diameter Add support for maximum filament diameter to hall filament width sensor. If the diameter of the filament diameter is larger than the limit, the virtual runout sensor will trigger. The default value is set to maximum flow adjustment threshold to prevent oversized filament from clogging. Signed-off-by: Sami Haahtinen --- docs/Config_Changes.md | 6 ++++++ docs/Config_Reference.md | 3 +++ klippy/extras/hall_filament_width_sensor.py | 5 +++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/Config_Changes.md b/docs/Config_Changes.md index 3606c6d00..2ceb868db 100644 --- a/docs/Config_Changes.md +++ b/docs/Config_Changes.md @@ -8,6 +8,12 @@ All dates in this document are approximate. ## Changes +20231216: The `[hall_filament_width_sensor]` is changed to trigger filament runout +when the thickness of the filament exceeds `max_diameter`. The maximum diameter +defaults to `default_nominal_filament_diameter + max_difference`. See +[[hall_filament_width_sensor] configuration +reference](./Config_Reference.md#hall_filament_width_sensor) for more details. + 20231207: Several undocumented config parameters in the `[printer]` config section have been removed (the buffer_time_low, buffer_time_high, buffer_time_start, and move_flush_time parameters). diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 313a50ecd..4b53ef264 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -4429,6 +4429,9 @@ adc2: # command. #min_diameter: 1.0 # Minimal diameter for trigger virtual filament_switch_sensor. +#max_diameter: +# Maximum diameter for triggering virtual filament_switch_sensor. +# The default is default_nominal_filament_diameter + max_difference. #use_current_dia_while_delay: False # Use the current diameter instead of the nominal diameter while # the measurement delay has not run through. diff --git a/klippy/extras/hall_filament_width_sensor.py b/klippy/extras/hall_filament_width_sensor.py index 276dd144d..e08028874 100644 --- a/klippy/extras/hall_filament_width_sensor.py +++ b/klippy/extras/hall_filament_width_sensor.py @@ -30,7 +30,8 @@ def __init__(self, config): - self.measurement_max_difference) self.diameter =self.nominal_filament_dia self.is_active =config.getboolean('enable', False) - self.runout_dia=config.getfloat('min_diameter', 1.0) + self.runout_dia_min=config.getfloat('min_diameter', 1.0) + self.runout_dia_max=config.getfloat('max_diameter', self.max_diameter) self.is_log =config.getboolean('logging', False) # Use the current diameter instead of nominal while the first # measurement isn't in place @@ -125,7 +126,7 @@ def extrude_factor_update_event(self, eventtime): self.update_filament_array(last_epos) # Check runout self.runout_helper.note_filament_present( - self.diameter > self.runout_dia) + self.runout_dia_min <= self.diameter <= self.runout_dia_max) # Does filament exists if self.diameter > 0.5: if len(self.filament_array) > 0: From 68b0277b71d85162da8f83f5924c5c0808f5b8d0 Mon Sep 17 00:00:00 2001 From: Brandon Date: Tue, 19 Dec 2023 06:52:10 -0500 Subject: [PATCH 49/53] fixes for good sensorless (props to fbeauKmi and nielsvz) (#90) --- README.md | 22 ++++++++++++++++------ docs/Config_Reference.md | 21 +++++++++++++++++++++ klippy/extras/homing.py | 18 +++++++++++------- klippy/extras/tmc2130.py | 6 ++++++ klippy/extras/tmc2240.py | 6 ++++++ klippy/extras/tmc2660.py | 6 ++++++ klippy/extras/tmc5160.py | 6 ++++++ klippy/stepper.py | 5 +++++ test/klippy/tmc.cfg | 1 + 9 files changed, 78 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 186369ba9..2c3beb08b 100644 --- a/README.md +++ b/README.md @@ -54,27 +54,33 @@ If I want my printer to light itself on fire, I should be able to make my printe - [temperature_mcu: add reference_voltage](https://github.com/DangerKlippers/danger-klipper/pull/99) ([klipper#5713](https://github.com/Klipper3d/klipper/pull/5713)) +- [stepper: current_change_dwell_time](https://github.com/DangerKlippers/danger-klipper/pull/90) + +- [homing: min_home_dist](https://github.com/DangerKlippers/danger-klipper/pull/90) + If you're feeling adventurous, take a peek at the extra features in the bleeding-edge branch: - - [dmbutyugin's advanced-features branch](https://github.com/DangerKlippers/danger-klipper/pull/69) [dmbutyugin/advanced-features](https://github.com/dmbutyugin/klipper/commits/advanced-features) +- [dmbutyugin's advanced-features branch](https://github.com/DangerKlippers/danger-klipper/pull/69) [dmbutyugin/advanced-features](https://github.com/dmbutyugin/klipper/commits/advanced-features) - - [stepper: high precision stepping protocol](https://github.com/DangerKlippers/danger-klipper/pull/69) +- [stepper: high precision stepping protocol](https://github.com/DangerKlippers/danger-klipper/pull/69) - - [extruder: sync extruder motion with input shaper](https://github.com/DangerKlippers/danger-klipper/pull/69) +- [extruder: sync extruder motion with input shaper](https://github.com/DangerKlippers/danger-klipper/pull/69) - - [extruder: new print_pa_tower utility](https://github.com/DangerKlippers/danger-klipper/pull/69) +- [extruder: new print_pa_tower utility](https://github.com/DangerKlippers/danger-klipper/pull/69) - - [input_shaper: smooth input shapers](https://github.com/DangerKlippers/danger-klipper/pull/69) +- [input_shaper: smooth input shapers](https://github.com/DangerKlippers/danger-klipper/pull/69) - - [input_shaper: new print_ringing_tower utility](https://github.com/DangerKlippers/danger-klipper/pull/69) +- [input_shaper: new print_ringing_tower utility](https://github.com/DangerKlippers/danger-klipper/pull/69) ## Switch to Danger Klipper + > [!NOTE] > Any add-on modules you are using will need to be reinstalled after switching to Danger Klipper. This includes things like Beacon support, led-effect, etc. > > Any data in ~/printer_data such as printer configs and macros will be unaffected. ### Manually clone the repository + If desired, make a backup copy of your existing Klipper installation by running: ```bash @@ -89,6 +95,7 @@ sudo systemctl restart klipper ``` ### Using KIAUH + For users that are not comfortable using Git directly, [KIAUH](https://github.com/dw-0/kiauh) is able to use custom repositories. To do this, add the Danger Klipper repo to KIAUH's repo list and run the script with the following commands: @@ -99,6 +106,7 @@ echo "DangerKlippers/danger-klipper" >> ~/kiauh/klipper_repos.txt ``` From the KIAUH menu select: + - 6 ) Settings - 1 ) Set custom Klipper repository - Select the number corresponding to DangerKlipper from the list shown @@ -107,6 +115,7 @@ From the KIAUH menu select: - 'Q' to quit ### Adding a git-remote to the existing installation + ```bash cd ~/klipper git remote add danger https://github.com/DangerKlippers/danger-klipper.git @@ -118,6 +127,7 @@ sudo systemctl restart moonraker ``` --- + "Dangerous Klipper for dangerous users" Klipper is a 3d-Printer firmware. It combines the power of a general diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 3a2b5abdc..e10c618c3 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -218,6 +218,11 @@ position_max: # Speed to use on the retract move after homing in case this should # be different from the homing speed, which is the default for this # parameter +#min_home_dist: +# Minimum distance (in mm) for toolhead before sensorless homing. If closer +# than `min_home_dist` to endstop, it moves away to this distance, then homes. +# If further, it directly homes and retracts to `homing_retract_dist`. +# The default is equal to `homing_retract_dist`. #second_homing_speed: # Velocity (in mm/s) of the stepper when performing the second home. # The default is homing_speed/2. @@ -3481,6 +3486,9 @@ run_current: #home_current: # The amount of current (in amps RMS) to configure the driver to use # during homing procedures. The default is to not reduce the current. +#current_change_dwell_time: +# The amount of time (in seconds) to wait after changing homing current. +# The default is 0.5 seconds. #sense_resistor: 0.110 # The resistance (in ohms) of the motor sense resistor. The default # is 0.110 ohms. @@ -3577,6 +3585,9 @@ run_current: #home_current: # The amount of current (in amps RMS) to configure the driver to use # during homing procedures. The default is to not reduce the current. +#current_change_dwell_time: +# The amount of time (in seconds) to wait after changing homing current. +# The default is 0.5 seconds. #sense_resistor: 0.110 # The resistance (in ohms) of the motor sense resistor. The default # is 0.110 ohms. @@ -3621,6 +3632,7 @@ uart_pin: run_current: #hold_current: #home_current: +#current_change_dwell_time: #sense_resistor: 0.110 #stealthchop_threshold: 0 # See the "tmc2208" section for the definition of these parameters. @@ -3692,6 +3704,9 @@ run_current: #home_current: # The amount of current (in amps RMS) to configure the driver to use # during homing procedures. The default is to not reduce the current. +#current_change_dwell_time: +# The amount of time (in seconds) to wait after changing homing current. +# The default is 0.5 seconds. #sense_resistor: # The resistance (in ohms) of the motor sense resistor. This # parameter must be provided. @@ -3775,6 +3790,9 @@ run_current: #home_current: # The amount of current (in amps RMS) to configure the driver to use # during homing procedures. The default is to not reduce the current. +#current_change_dwell_time: +# The amount of time (in seconds) to wait after changing homing current. +# The default is 0.5 seconds. #rref: 12000 # The resistance (in ohms) of the resistor between IREF and GND. The # default is 12000. @@ -3899,6 +3917,9 @@ run_current: #home_current: # The amount of current (in amps RMS) to configure the driver to use # during homing procedures. The default is to not reduce the current. +#current_change_dwell_time: +# The amount of time (in seconds) to wait after changing homing current. +# The default is 0.5 seconds. #sense_resistor: 0.075 # The resistance (in ohms) of the motor sense resistor. The default # is 0.075 ohms. diff --git a/klippy/extras/homing.py b/klippy/extras/homing.py index 12decd06d..4f43d7e2a 100644 --- a/klippy/extras/homing.py +++ b/klippy/extras/homing.py @@ -240,9 +240,9 @@ def _set_current_homing(self, homing_axes): for rail in affected_rails: ch = rail.get_tmc_current_helper() - if ch is not None: + if ch is not None and ch.needs_home_current_change(): ch.set_current_for_homing(print_time) - self.toolhead.dwell(0.5) + self.toolhead.dwell(ch.current_change_dwell_time) def _set_current_post_homing(self, homing_axes): print_time = self.toolhead.get_last_move_time() @@ -254,9 +254,9 @@ def _set_current_post_homing(self, homing_axes): for rail in affected_rails: ch = rail.get_tmc_current_helper() - if ch is not None: + if ch is not None and ch.needs_home_current_change(): ch.set_current_for_normal(print_time) - self.toolhead.dwell(0.5) + self.toolhead.dwell(ch.current_change_dwell_time) def home_rails(self, rails, forcepos, movepos): # Notify of upcoming homing operation @@ -284,8 +284,12 @@ def home_rails(self, rails, forcepos, movepos): # Perform second home if hi.retract_dist: needs_rehome = False - if any([dist < hi.retract_dist for dist in homing_axis_distances]): + retract_dist = hi.retract_dist + if any( + [abs(dist) < hi.min_home_dist for dist in homing_axis_distances] + ): needs_rehome = True + retract_dist = hi.min_home_dist logging.info("needs rehome: %s", needs_rehome) # Retract @@ -293,13 +297,12 @@ def home_rails(self, rails, forcepos, movepos): homepos = self._fill_coord(movepos) axes_d = [hp - sp for hp, sp in zip(homepos, startpos)] move_d = math.sqrt(sum([d * d for d in axes_d[:3]])) - retract_r = min(1.0, hi.retract_dist / move_d) + retract_r = min(1.0, retract_dist / move_d) retractpos = [ hp - ad * retract_r for hp, ad in zip(homepos, axes_d) ] self.toolhead.move(retractpos, hi.retract_speed) if not hi.use_sensorless_homing or needs_rehome: - self.toolhead.dwell(0.5) # Home again startpos = [ rp - ad * retract_r for rp, ad in zip(retractpos, axes_d) @@ -309,6 +312,7 @@ def home_rails(self, rails, forcepos, movepos): for endstop in endstops: # re-querying a tmc endstop seems to reset the state # otherwise it triggers almost immediately upon second home + # this seems to be an adequate substitute for a 2 second dwell. endstop[0].query_endstop(print_time) hmove = HomingMove(self.printer, endstops) hmove.homing_move(homepos, hi.second_homing_speed) diff --git a/klippy/extras/tmc2130.py b/klippy/extras/tmc2130.py index 374a53608..f3b6f7577 100644 --- a/klippy/extras/tmc2130.py +++ b/klippy/extras/tmc2130.py @@ -212,6 +212,9 @@ def __init__(self, config, mcu_tmc): self._home_current = config.getfloat( "home_current", self.run_current, above=0.0, maxval=MAX_CURRENT ) + self.current_change_dwell_time = config.getfloat( + "current_change_dwell_time", 0.5, above=0.0 + ) self.prev_current = self.run_current self.req_hold_current = self.hold_current self.sense_resistor = config.getfloat( @@ -224,6 +227,9 @@ def __init__(self, config, mcu_tmc): self.fields.set_field("ihold", ihold) self.fields.set_field("irun", irun) + def needs_home_current_change(self): + return self._home_current != self.run_current + def set_home_current(self, new_home_current): self._home_current = min(MAX_CURRENT, new_home_current) diff --git a/klippy/extras/tmc2240.py b/klippy/extras/tmc2240.py index 5d7f3f08e..de8ca986a 100644 --- a/klippy/extras/tmc2240.py +++ b/klippy/extras/tmc2240.py @@ -281,6 +281,9 @@ def __init__(self, config, mcu_tmc): self._home_current = config.getfloat( "home_current", self.run_current, above=0.0, maxval=self.max_cur ) + self.current_change_dwell_time = config.getfloat( + "current_change_dwell_time", 0.5, above=0.0 + ) self.prev_current = self.run_current self.req_hold_current = self.hold_current current_range = self._calc_current_range(self.run_current) @@ -292,6 +295,9 @@ def __init__(self, config, mcu_tmc): self.fields.set_field("ihold", ihold) self.fields.set_field("irun", irun) + def needs_home_current_change(self): + return self._home_current != self.run_current + def set_home_current(self, new_home_current): self._home_current = min(self.max_cur, new_home_current) diff --git a/klippy/extras/tmc2660.py b/klippy/extras/tmc2660.py index 85a891969..5aa020f97 100644 --- a/klippy/extras/tmc2660.py +++ b/klippy/extras/tmc2660.py @@ -126,6 +126,9 @@ def __init__(self, config, mcu_tmc): self._home_current = config.getfloat( "home_current", self.current, above=0.0, maxval=MAX_CURRENT ) + self.current_change_dwell_time = config.getfloat( + "current_change_dwell_time", 0.5, above=0.0 + ) self._prev_current = self.current self.sense_resistor = config.getfloat("sense_resistor") vsense, cs = self._calc_current(self.current) @@ -144,6 +147,9 @@ def __init__(self, config, mcu_tmc): "idle_timeout:ready", self._handle_ready ) + def needs_home_current_change(self): + return self._home_current != self.current + def set_home_current(self, new_home_current): self._home_current = min(MAX_CURRENT, new_home_current) diff --git a/klippy/extras/tmc5160.py b/klippy/extras/tmc5160.py index ecf8d3ac8..fa0fe0632 100644 --- a/klippy/extras/tmc5160.py +++ b/klippy/extras/tmc5160.py @@ -265,6 +265,9 @@ def __init__(self, config, mcu_tmc): self._home_current = config.getfloat( "home_current", self.run_current, above=0.0, maxval=MAX_CURRENT ) + self.current_change_dwell_time = config.getfloat( + "current_change_dwell_time", 0.5, above=0.0 + ) self._prev_current = self.run_current self.req_hold_current = self.hold_current self.sense_resistor = config.getfloat( @@ -277,6 +280,9 @@ def __init__(self, config, mcu_tmc): self.fields.set_field("ihold", ihold) self.fields.set_field("irun", irun) + def needs_home_current_change(self): + return self._home_current != self.run_current + def set_home_current(self, new_home_current): self._home_current = min(MAX_CURRENT, new_home_current) diff --git a/klippy/stepper.py b/klippy/stepper.py index 77f3b26c5..0a79f88ea 100644 --- a/klippy/stepper.py +++ b/klippy/stepper.py @@ -454,6 +454,9 @@ def __init__( self.use_sensorless_homing = config.getboolean( "use_sensorless_homing", endstop_is_virtual ) + self.min_home_dist = config.getfloat( + "min_home_dist", self.homing_retract_dist, minval=0.0 + ) if self.homing_positive_dir is None: axis_len = self.position_max - self.position_min @@ -493,6 +496,7 @@ def get_homing_info(self): "positive_dir", "second_homing_speed", "use_sensorless_homing", + "min_home_dist", ], )( self.homing_speed, @@ -502,6 +506,7 @@ def get_homing_info(self): self.homing_positive_dir, self.second_homing_speed, self.use_sensorless_homing, + self.min_home_dist, ) return homing_info diff --git a/test/klippy/tmc.cfg b/test/klippy/tmc.cfg index 560691d73..1d68007d7 100644 --- a/test/klippy/tmc.cfg +++ b/test/klippy/tmc.cfg @@ -43,6 +43,7 @@ use_sensorless_homing: true cs_pin: PG2 run_current: .7 home_current: .5 +current_change_dwell_time: 1 sense_resistor: 0.220 diag1_pin: !PK7 From 17c6d6433d194d10a08c677f00eb891324f9f63d Mon Sep 17 00:00:00 2001 From: Brandon Date: Tue, 19 Dec 2023 13:42:09 -0500 Subject: [PATCH 50/53] Sensorless retract dist fix (#109) * move retract_dist calc * conditionally retract --- klippy/extras/homing.py | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/klippy/extras/homing.py b/klippy/extras/homing.py index 4f43d7e2a..c1335a772 100644 --- a/klippy/extras/homing.py +++ b/klippy/extras/homing.py @@ -280,17 +280,15 @@ def home_rails(self, rails, forcepos, movepos): for i, dist in enumerate(hmove.distance_elapsed) if i in homing_axes ] - + needs_rehome = False + retract_dist = hi.retract_dist + if any( + [abs(dist) < hi.min_home_dist for dist in homing_axis_distances] + ): + needs_rehome = True + retract_dist = hi.min_home_dist # Perform second home - if hi.retract_dist: - needs_rehome = False - retract_dist = hi.retract_dist - if any( - [abs(dist) < hi.min_home_dist for dist in homing_axis_distances] - ): - needs_rehome = True - retract_dist = hi.min_home_dist - + if retract_dist: logging.info("needs rehome: %s", needs_rehome) # Retract startpos = self._fill_coord(forcepos) @@ -321,17 +319,17 @@ def home_rails(self, rails, forcepos, movepos): "Endstop %s still triggered after retract" % (hmove.check_no_movement(),) ) - - # Retract (again) - startpos = self._fill_coord(forcepos) - homepos = self._fill_coord(movepos) - axes_d = [hp - sp for hp, sp in zip(homepos, startpos)] - move_d = math.sqrt(sum([d * d for d in axes_d[:3]])) - retract_r = min(1.0, hi.retract_dist / move_d) - retractpos = [ - hp - ad * retract_r for hp, ad in zip(homepos, axes_d) - ] - self.toolhead.move(retractpos, hi.retract_speed) + if hi.retract_dist: + # Retract (again) + startpos = self._fill_coord(forcepos) + homepos = self._fill_coord(movepos) + axes_d = [hp - sp for hp, sp in zip(homepos, startpos)] + move_d = math.sqrt(sum([d * d for d in axes_d[:3]])) + retract_r = min(1.0, hi.retract_dist / move_d) + retractpos = [ + hp - ad * retract_r for hp, ad in zip(homepos, axes_d) + ] + self.toolhead.move(retractpos, hi.retract_speed) self._set_current_post_homing(homing_axes) # Signal home operation complete self.toolhead.flush_step_generation() From 547bfbf81847c758a3fb07e76963544bdbc6438d Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Tue, 19 Dec 2023 14:57:39 -0500 Subject: [PATCH 51/53] pwm_tool: Fix error reporting References to pins.error are not valid as the pins module is not imported. Reported by @Piezoid. Signed-off-by: Kevin O'Connor --- klippy/extras/pwm_tool.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/klippy/extras/pwm_tool.py b/klippy/extras/pwm_tool.py index 0b3dd0c09..5fc09eab9 100644 --- a/klippy/extras/pwm_tool.py +++ b/klippy/extras/pwm_tool.py @@ -50,19 +50,20 @@ def setup_start_value(self, start_value, shutdown_value): self._start_value = max(0., min(1., start_value)) self._shutdown_value = max(0., min(1., shutdown_value)) def _build_config(self): + config_error = self._mcu.get_printer().config_error if self._max_duration and self._start_value != self._shutdown_value: - raise pins.error("Pin with max duration must have start" - " value equal to shutdown value") + raise config_error("Pin with max duration must have start" + " value equal to shutdown value") cmd_queue = self._mcu.alloc_command_queue() curtime = self._mcu.get_printer().get_reactor().monotonic() printtime = self._mcu.estimated_print_time(curtime) self._last_clock = self._mcu.print_time_to_clock(printtime + 0.200) cycle_ticks = self._mcu.seconds_to_clock(self._cycle_time) if cycle_ticks >= 1<<31: - raise pins.error("PWM pin cycle time too large") + raise config_error("PWM pin cycle time too large") self._duration_ticks = self._mcu.seconds_to_clock(self._max_duration) if self._duration_ticks >= 1<<31: - raise pins.error("PWM pin max duration too large") + raise config_error("PWM pin max duration too large") if self._duration_ticks: self._mcu.register_flush_callback(self._flush_notification) if self._hardware_pwm: @@ -85,7 +86,7 @@ def _build_config(self): return # Software PWM if self._shutdown_value not in [0., 1.]: - raise pins.error("shutdown value must be 0.0 or 1.0 on soft pwm") + raise config_error("shutdown value must be 0.0 or 1.0 on soft pwm") self._mcu.add_config_cmd( "config_digital_out oid=%d pin=%s value=%d" " default_value=%d max_duration=%d" From 544e3eeeb8402edb1ecdce232fa4e899fbe89f5e Mon Sep 17 00:00:00 2001 From: Rogerio Goncalves Date: Wed, 20 Dec 2023 11:17:28 +0000 Subject: [PATCH 52/53] rework sensorless rehome; check traveled distance (with tolerance (#110) config) Co-authored-by: Brandon Nance --- README.md | 2 + docs/Config_Reference.md | 78 +++++++++++++++++++-------------- klippy/extras/danger_options.py | 3 ++ klippy/extras/homing.py | 40 +++++++++++++---- 4 files changed, 81 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 2c3beb08b..11a20406a 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ If I want my printer to light itself on fire, I should be able to make my printe - [danger_options: expose the multi mcu homing timeout as a parameter](https://github.com/DangerKlippers/danger-klipper/pull/93) +- [danger_options: option to configure the homing elapsed distance tolerance](https://github.com/DangerKlippers/danger-klipper/pull/110) + - [temperature_mcu: add reference_voltage](https://github.com/DangerKlippers/danger-klipper/pull/99) ([klipper#5713](https://github.com/Klipper3d/klipper/pull/5713)) - [stepper: current_change_dwell_time](https://github.com/DangerKlippers/danger-klipper/pull/90) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index e10c618c3..b67ac2e10 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -73,36 +73,43 @@ pins such as "extra_mcu:ar9" may then be used elsewhere in the config [mcu my_extra_mcu] # See the "mcu" section for configuration parameters. ``` + ## ⚠️ Danger Options + A collection of DangerKlipper-specific system options + ``` [danger_options] - -# If an unused config option or section should cause an error -# if False, will warn but allow klipper to still run -error_on_unused_config_options: True - -# If statistics should be logged -# (helpful for keeping the log clean during development) -log_statistics: True - -# If the config file should be logged at startup -log_config_file_at_startup: True - -# If the bed mesh should be logged on startup -# (helpful for keeping the log clean during development) -log_bed_mesh_at_startup: True - -# If we should log detailed crash info when an exception occurs -# Most of it is overly-verbose and fluff and we still get a stack trace -# for normal exceptions, so setting to False can help save time while developing -log_shutdown_info: True - -# Allows modules in `plugins` to override modules of the same name in `extras` -allow_plugin_override: False - -# The timeout (in seconds) for MCU synchronization during the homing process when multiple MCUs are in use. -multi_mcu_trsync_timeout: 0.025 +#error_on_unused_config_options: True +# If an unused config option or section should cause an error +# if False, will warn but allow klipper to still run. +# The default is True. +#log_statistics: True +# If statistics should be logged +# (helpful for keeping the log clean during development) +# The default is True. +#log_config_file_at_startup: True +# If the config file should be logged at startup +# The default is True. +#log_bed_mesh_at_startup: True +# If the bed mesh should be logged on startup +# (helpful for keeping the log clean during development) +# The default is True. +#log_shutdown_info: True +# If we should log detailed crash info when an exception occurs +# Most of it is overly-verbose and fluff and we still get a stack trace +# for normal exceptions, so setting to False can help save time while developing +# The default is True. +#allow_plugin_override: False +# Allows modules in `plugins` to override modules of the same name in `extras` +# The default is False. +#multi_mcu_trsync_timeout: 0.025 +# The timeout (in seconds) for MCU synchronization during the homing process when +# multiple MCUs are in use. The default is 0.025 +#homing_elapsed_distance_tolerance: 0.5 +# Tolerance (in mm) for distance moved in the second homing. Ensures the +# second homing distance closely matches the `min_home_dist` when using +# sensorless homing. The default is 0.5mm. ``` ## Common kinematic settings @@ -365,7 +372,7 @@ example deltesian kinematics config file. Only parameters specific to deltesian printers are described here - see [common kinematic settings](#common-kinematic-settings) for available - parameters. +parameters. ``` [printer] @@ -914,6 +921,7 @@ See the [bed mesh guide](Bed_Mesh.md) and [command reference](G-Codes.md#bed_mesh) for additional information. Visual Examples: + ``` rectangular bed, probe_count = 3, 3: x---x---x (max_point) @@ -1241,7 +1249,6 @@ extended [G-Code command](G-Codes.md#z_tilt) becomes available. # The default is conservative. ``` - ### [quad_gantry_level] Moving gantry leveling using 4 independently controlled Z motors. @@ -1251,6 +1258,7 @@ WARNING: Using this on a moving bed may lead to undesirable results. If this section is present then a QUAD_GANTRY_LEVEL extended G-Code command becomes available. This routine assumes the following Z motor configuration: + ``` ---------------- |Z1 Z2| @@ -1261,6 +1269,7 @@ configuration: |Z Z3| ---------------- ``` + Where x is the 0, 0 point on the bed ``` @@ -1669,6 +1678,7 @@ Enable the "M118" and "RESPOND" extended ``` ### [exclude_object] + Enables support to exclude or cancel individual objects during the printing process. @@ -1885,7 +1895,7 @@ aliases_: Include file support. One may include additional config file from the main printer config file. Wildcards may also be used (eg, -"configs/*.cfg"). +"configs/\*.cfg"). ``` [include my_other_config.cfg] @@ -4890,8 +4900,8 @@ Octoprint as they will conflict, and 1 will fail to initialize properly likely aborting your print. If you use Octoprint and stream gcode over the serial port instead of -printing from virtual_sd, then remo **M1** and **M0** from *Pausing commands* -in *Settings > Serial Connection > Firmware & protocol* will prevent +printing from virtual_sd, then remo **M1** and **M0** from _Pausing commands_ +in _Settings > Serial Connection > Firmware & protocol_ will prevent the need to start print on the Palette 2 and unpausing in Octoprint for your print to begin. @@ -4914,7 +4924,7 @@ serial: ### [angle] Magnetic hall angle sensor support for reading stepper motor angle -shaft measurements using a1333, as5047d, or tle5012b SPI chips. The +shaft measurements using a1333, as5047d, or tle5012b SPI chips. The measurements are available via the [API Server](API_Server.md) and [motion analysis tool](Debugging.md#motion-analysis-and-data-logging). See the [G-Code reference](G-Codes.md#angle) for available commands. @@ -4982,8 +4992,8 @@ It is generally recommended to only use I2C devices that are on the same printed circuit board as the micro-controller. Most Klipper micro-controller implementations only support an -`i2c_speed` of 100000 (*standard mode*, 100kbit/s). The Klipper "Linux" -micro-controller supports a 400000 speed (*fast mode*, 400kbit/s), but it must be +`i2c_speed` of 100000 (_standard mode_, 100kbit/s). The Klipper "Linux" +micro-controller supports a 400000 speed (_fast mode_, 400kbit/s), but it must be [set in the operating system](RPi_microcontroller.md#optional-enabling-i2c) and the `i2c_speed` parameter is otherwise ignored. The Klipper "RP2040" micro-controller and ATmega AVR family support a rate of 400000 diff --git a/klippy/extras/danger_options.py b/klippy/extras/danger_options.py index 314b6f092..71774a278 100644 --- a/klippy/extras/danger_options.py +++ b/klippy/extras/danger_options.py @@ -19,6 +19,9 @@ def __init__(self, config): self.multi_mcu_trsync_timeout = config.getfloat( "multi_mcu_trsync_timeout", 0.025, minval=0.0 ) + self.homing_elapsed_distance_tolerance = config.getfloat( + "homing_elapsed_distance_tolerance", 0.5, minval=0.0 + ) def load_config(config): diff --git a/klippy/extras/homing.py b/klippy/extras/homing.py index c1335a772..adfd9d062 100644 --- a/klippy/extras/homing.py +++ b/klippy/extras/homing.py @@ -50,6 +50,7 @@ def __init__(self, printer, endstops, toolhead=None): self.toolhead = toolhead self.stepper_positions = [] self.distance_elapsed = [] + self.danger_options = printer.lookup_object("danger_options") def get_mcu_endstops(self): return [es for es, name in self.endstops] @@ -197,6 +198,25 @@ def check_no_movement(self): return sp.endstop_name return None + def moved_less_than_dist(self, min_dist, homing_axes): + homing_axis_distances = [ + dist + for i, dist in enumerate(self.distance_elapsed) + if i in homing_axes + ] + distance_tolerance = ( + self.danger_options.homing_elapsed_distance_tolerance + ) + if any( + [ + abs(dist) < min_dist + and min_dist - abs(dist) >= distance_tolerance + for dist in homing_axis_distances + ] + ): + return True + return False + # State tracking of homing requests class Homing: @@ -275,18 +295,12 @@ def home_rails(self, rails, forcepos, movepos): hmove.homing_move(homepos, hi.speed) - homing_axis_distances = [ - dist - for i, dist in enumerate(hmove.distance_elapsed) - if i in homing_axes - ] needs_rehome = False retract_dist = hi.retract_dist - if any( - [abs(dist) < hi.min_home_dist for dist in homing_axis_distances] - ): + if hmove.moved_less_than_dist(hi.min_home_dist, homing_axes): needs_rehome = True retract_dist = hi.min_home_dist + # Perform second home if retract_dist: logging.info("needs rehome: %s", needs_rehome) @@ -319,6 +333,16 @@ def home_rails(self, rails, forcepos, movepos): "Endstop %s still triggered after retract" % (hmove.check_no_movement(),) ) + if ( + hi.use_sensorless_homing + and needs_rehome + and hmove.moved_less_than_dist( + hi.min_home_dist, homing_axes + ) + ): + raise self.printer.command_error( + "Early homing trigger on second home!" + ) if hi.retract_dist: # Retract (again) startpos = self._fill_coord(forcepos) From 64fff4eaf36f92ff0286502bc8ab615d6066c313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kerbiriou?= Date: Tue, 19 Dec 2023 18:03:10 +0100 Subject: [PATCH 53/53] CI: remove outdated upstream-sync workflow --- .github/workflows/upstream-sync.yaml | 141 --------------------------- 1 file changed, 141 deletions(-) delete mode 100644 .github/workflows/upstream-sync.yaml diff --git a/.github/workflows/upstream-sync.yaml b/.github/workflows/upstream-sync.yaml deleted file mode 100644 index d3869a01f..000000000 --- a/.github/workflows/upstream-sync.yaml +++ /dev/null @@ -1,141 +0,0 @@ -name: "Upstream Sync" - -on: - # schedule: - # - cron: '0 0 * * *' - # every day at midnight - - workflow_dispatch: # click the button on Github repo! - -# Remotes: -# - origin: our fork -# - klipper3d: original Klipper repository - -# Git references at play: -# - prev_upstream: tag pointing to the last mainline commit that was formatted -# - prev_upstream..klipper3d/master: the new commits that we want to rebase on top of upstream -# - upstream: branch on our fork containing formatted klipper with new commits being rebased + formatted -# - upstream_pr: copy of the upstream branch, allowing to edit the PR - -# Fetching: (trying to remain minimalist) -# - origin/prev_upstream: single commit deep -# - klipper3d/master: tries to select the range prev_upstream..klipper3d/master by date (--shallow-exclude not supported by github?) -# After tat the presence of new commits is confirmed: -# - origin/master: single commit deep, for getting the tooling config -# - origin/upstream: single commit deep, for rebasing on top - -# Workspace: -# - Root contains scripts/python-format.sh pyproject.toml .flake8 -# - repo contains the checked out repository at prev_upstream - -jobs: - sync_latest_from_klipper3d: - runs-on: ubuntu-20.04 - name: Sync latest commits from klipper3d repository - - steps: - # Fetch origin/prev_upstream - - uses: actions/checkout@v3 - with: - ref: prev_upstream - path: "repo" - - # Setup klipper3d remote and fetch klipper3d/master - - name: "Checks for new commits" - id: "fetch" - run: | - cd repo - git remote add klipper3d https://github.com/Klipper3d/klipper.git - git fetch --prune --no-tags --shallow-since="$(git show -s --format=%ct prev_upstream)" klipper3d master - COMMITS=$(git rev-list prev_upstream..klipper3d/master) - - if [ "$COMMITS" ]; then - echo "Upstream commits processed $(git rev-parse prev_upstream)..$(git rev-parse klipper3d/master):" >> $GITHUB_STEP_SUMMARY - echo "$COMMITS" | tee -a $GITHUB_STEP_SUMMARY - echo >> $GITHUB_STEP_SUMMARY - echo "::set-output name=new_commits::1" - else - echo "::set-output name=new_commits::0" - fi - cat $GITHUB_STEP_SUMMARY >> "$RUNNER_TEMP/summary" - - # Fetch origin/upstream (branch on which we add rebased commits) and origin/master (for tooling configs) - - name: Checkout tooling configs - if: ${{ steps.fetch.outputs.new_commits }} == '1' - run: | - cd repo - git fetch --depth=1 --prune --no-tags origin master upstream - git --work-tree=.. checkout origin/master -- ../scripts/requirements_dev.txt ../scripts/python-format.sh ../pyproject.toml ../.flake8 - git reset - echo "tooling configs checked out from: $(git rev-parse origin/master)" >> $GITHUB_STEP_SUMMARY - cat $GITHUB_STEP_SUMMARY >> "$RUNNER_TEMP/summary" - - - name: Setup python - if: ${{ steps.fetch.outputs.new_commits }} == '1' - uses: actions/setup-python@v4 - with: - python-version: 3.10.3 - cache-dependency-path: scripts/requirements_dev.txt - cache: "pip" - - name: Pip install - if: ${{ steps.fetch.outputs.new_commits }} == '1' - run: pip install -r scripts/requirements_dev.txt - - - name: Rebase + Formatting - if: ${{ steps.fetch.outputs.new_commits }} == '1' - run: | - cd repo - git config --global user.name 'GitHub Action' - git config --global user.email 'action@github.com' - GIT_COMMIT=HEAD AMEND=Y BLACK_ARGS="--config ../pyproject.toml"\ - git rebase prev_upstream klipper3d/master --onto=origin/upstream \ - -Xtheirs -Xignore-all-space --exec ../scripts/python-format.sh - git checkout origin/master .github - git add .github - git commit --amend --no-edit - git push origin +klipper3d/master:refs/tags/prev_upstream +HEAD:refs/heads/upstream +HEAD:refs/heads/upstream_pr - - echo "formatted \`upstream\` branch was: $(git rev-parse origin/upstream)" >> $GITHUB_STEP_SUMMARY - echo "new \`upstream\`: $(git rev-parse HEAD)" >> $GITHUB_STEP_SUMMARY - cd .. - echo "tooling sha1sums (from master):" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - sha1sum scripts/python-format.sh pyproject.toml .flake8 >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - cat $GITHUB_STEP_SUMMARY >> "$RUNNER_TEMP/summary" - - - name: Create Pull Request - if: ${{ steps.fetch.outputs.new_commits }} == '1' - uses: actions/github-script@v6 - with: - script: | - const fs = require('fs') - const { repo, owner } = context.repo; - const result = await github.rest.pulls.create({ - title: "[GA] Upstream changes", - owner, - repo, - head: "upstream_pr", - base: "master", - body: [ - "New commits from Klipper3D.", - "", - fs.readFileSync(process.env.RUNNER_TEMP + '/summary'), - "", - "*Please don't squash me.*", - "When there are conflicts, this branch can be manually rebased on top of master:", - "```", - "git checkout upstream_pr", - "git pull -f # If your alread have an old local version of that branch", - "git rebase origin/master", - "", - "git push -f", - "```", - ].join("\n"), - }); - github.rest.issues.addLabels({ - owner, - repo, - issue_number: result.data.number, - labels: ["upstream"], - });