mirror of https://github.com/Desuuuu/klipper.git
Merge remote-tracking branch 'upstream/master' into dgus-display
This commit is contained in:
commit
b466915998
|
@ -1 +1,2 @@
|
||||||
patreon: koconnor
|
ko_fi: koconnor
|
||||||
|
custom: https://www.klipper3d.org/Sponsors.html#klipper-developers
|
||||||
|
|
|
@ -7,7 +7,6 @@ on:
|
||||||
- master
|
- master
|
||||||
paths:
|
paths:
|
||||||
- docs/**
|
- docs/**
|
||||||
- mkdocs.yml
|
|
||||||
- .github/workflows/klipper3d-deploy.yaml
|
- .github/workflows/klipper3d-deploy.yaml
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
|
|
8
COPYING
8
COPYING
|
@ -1,7 +1,7 @@
|
||||||
GNU GENERAL PUBLIC LICENSE
|
GNU GENERAL PUBLIC LICENSE
|
||||||
Version 3, 29 June 2007
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
For more information on this, and how to apply and follow the GNU GPL, see
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
<http://www.gnu.org/licenses/>.
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
The GNU General Public License does not permit incorporating your program
|
The GNU General Public License does not permit incorporating your program
|
||||||
into proprietary programs. If your program is a subroutine library, you
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
may consider it more useful to permit linking proprietary applications with
|
may consider it more useful to permit linking proprietary applications with
|
||||||
the library. If this is what you want to do, use the GNU Lesser General
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
Public License instead of this License. But first, please read
|
Public License instead of this License. But first, please read
|
||||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -31,7 +31,7 @@ cc-option=$(shell if test -z "`$(1) $(2) -S -o /dev/null -xc /dev/null 2>&1`" \
|
||||||
|
|
||||||
CFLAGS := -I$(OUT) -Isrc -I$(OUT)board-generic/ -std=gnu11 -O2 -MD \
|
CFLAGS := -I$(OUT) -Isrc -I$(OUT)board-generic/ -std=gnu11 -O2 -MD \
|
||||||
-Wall -Wold-style-definition $(call cc-option,$(CC),-Wtype-limits,) \
|
-Wall -Wold-style-definition $(call cc-option,$(CC),-Wtype-limits,) \
|
||||||
-ffunction-sections -fdata-sections
|
-ffunction-sections -fdata-sections -fno-delete-null-pointer-checks
|
||||||
CFLAGS += -flto -fwhole-program -fno-use-linker-plugin -ggdb3
|
CFLAGS += -flto -fwhole-program -fno-use-linker-plugin -ggdb3
|
||||||
|
|
||||||
OBJS_klipper.elf = $(patsubst %.c, $(OUT)src/%.o,$(src-y))
|
OBJS_klipper.elf = $(patsubst %.c, $(OUT)src/%.o,$(src-y))
|
||||||
|
|
|
@ -13,4 +13,6 @@ To begin using Klipper start by
|
||||||
[installing](https://www.klipper3d.org/Installation.html) it.
|
[installing](https://www.klipper3d.org/Installation.html) it.
|
||||||
|
|
||||||
Klipper is Free Software. See the [license](COPYING) or read the
|
Klipper is Free Software. See the [license](COPYING) or read the
|
||||||
[documentation](https://www.klipper3d.org/Overview.html).
|
[documentation](https://www.klipper3d.org/Overview.html). We depend on
|
||||||
|
the generous support from our
|
||||||
|
[sponsors](https://www.klipper3d.org/Sponsors.html).
|
||||||
|
|
|
@ -129,7 +129,7 @@ max_temp: 130
|
||||||
[fan]
|
[fan]
|
||||||
pin: PC26
|
pin: PC26
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PC25
|
pin: PC25
|
||||||
|
|
||||||
[mcu]
|
[mcu]
|
||||||
|
|
|
@ -90,7 +90,7 @@ pid_Kd: 948.182
|
||||||
min_temp: 0
|
min_temp: 0
|
||||||
max_temp: 130
|
max_temp: 130
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PB6
|
pin: PB6
|
||||||
|
|
||||||
[fan]
|
[fan]
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
# This file contains common pin mappings for the BigTreeTech SKR 3.
|
||||||
|
# To use this config, during "make menuconfig" enable "low-level
|
||||||
|
# options", "STM32H743", "128KiB bootloader", and "25MHz clock".
|
||||||
|
|
||||||
|
# See docs/Config_Reference.md for a description of parameters.
|
||||||
|
|
||||||
|
[stepper_x]
|
||||||
|
step_pin: PD4
|
||||||
|
dir_pin: PD3
|
||||||
|
enable_pin: !PD6
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: ^PC1
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 200
|
||||||
|
homing_speed: 50
|
||||||
|
|
||||||
|
[stepper_y]
|
||||||
|
step_pin: PA15
|
||||||
|
dir_pin: !PA8
|
||||||
|
enable_pin: !PD1
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: ^PC3
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 200
|
||||||
|
homing_speed: 50
|
||||||
|
|
||||||
|
[stepper_z]
|
||||||
|
step_pin: PE2
|
||||||
|
dir_pin: PE3
|
||||||
|
enable_pin: !PE0
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: ^PC0
|
||||||
|
position_endstop: 0.5
|
||||||
|
position_max: 200
|
||||||
|
|
||||||
|
[extruder]
|
||||||
|
step_pin: PD15
|
||||||
|
dir_pin: PD14
|
||||||
|
enable_pin: !PC7
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 33.500
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: PB3
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PA2
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 22.2
|
||||||
|
pid_Ki: 1.08
|
||||||
|
pid_Kd: 114
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 250
|
||||||
|
|
||||||
|
#[extruder1]
|
||||||
|
#step_pin: PD11
|
||||||
|
#dir_pin: PD10
|
||||||
|
#enable_pin: !PD13
|
||||||
|
#heater_pin: PB4
|
||||||
|
#sensor_pin: PA3
|
||||||
|
#...
|
||||||
|
|
||||||
|
[heater_bed]
|
||||||
|
heater_pin: PD7
|
||||||
|
sensor_type: Generic 3950
|
||||||
|
sensor_pin: PA1
|
||||||
|
control: watermark
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 130
|
||||||
|
|
||||||
|
[fan]
|
||||||
|
pin: PB7
|
||||||
|
|
||||||
|
#[heater_fan fan1]
|
||||||
|
#pin: PB6
|
||||||
|
|
||||||
|
#[heater_fan fan2]
|
||||||
|
#pin: PB5
|
||||||
|
|
||||||
|
[mcu]
|
||||||
|
serial: /dev/serial/by-id/usb-Klipper_Klipper_firmware_12345-if00
|
||||||
|
|
||||||
|
[printer]
|
||||||
|
kinematics: cartesian
|
||||||
|
max_velocity: 300
|
||||||
|
max_accel: 3000
|
||||||
|
max_z_velocity: 5
|
||||||
|
max_z_accel: 100
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# EXP1 / EXP2 (display) pins
|
||||||
|
########################################
|
||||||
|
|
||||||
|
[board_pins]
|
||||||
|
aliases:
|
||||||
|
# EXP1 header
|
||||||
|
EXP1_1=PC5, EXP1_3=PB1, EXP1_5=PE9, EXP1_7=PE11, EXP1_9=<GND>,
|
||||||
|
EXP1_2=PB0, EXP1_4=PE8, EXP1_6=PE10, EXP1_8=PE12, EXP1_10=<5V>,
|
||||||
|
# EXP2 header
|
||||||
|
EXP2_1=PA6, EXP2_3=PE7, EXP2_5=PB2, EXP2_7=PC4, EXP2_9=<GND>,
|
||||||
|
EXP2_2=PA5, EXP2_4=PA4, EXP2_6=PA7, EXP2_8=<RST>, EXP2_10=<NC>
|
||||||
|
|
||||||
|
# See the sample-lcd.cfg file for definitions of common LCD displays.
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# TMC2209 configuration
|
||||||
|
########################################
|
||||||
|
|
||||||
|
#[tmc2209 stepper_x]
|
||||||
|
#uart_pin: PD5
|
||||||
|
#run_current: 0.800
|
||||||
|
#diag_pin:
|
||||||
|
|
||||||
|
#[tmc2209 stepper_y]
|
||||||
|
#uart_pin: PD0
|
||||||
|
#run_current: 0.800
|
||||||
|
#diag_pin:
|
||||||
|
|
||||||
|
#[tmc2209 stepper_z]
|
||||||
|
#uart_pin: PE1
|
||||||
|
#run_current: 0.800
|
||||||
|
#diag_pin:
|
||||||
|
|
||||||
|
#[tmc2209 extruder]
|
||||||
|
#uart_pin: PC6
|
||||||
|
#run_current: 0.600
|
||||||
|
#diag_pin:
|
||||||
|
|
||||||
|
#[tmc2209 extruder1]
|
||||||
|
#uart_pin: PD12
|
||||||
|
#run_current: 0.600
|
||||||
|
#diag_pin:
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# TMC2130 configuration
|
||||||
|
########################################
|
||||||
|
|
||||||
|
#[tmc2130 stepper_x]
|
||||||
|
#cs_pin: PD5
|
||||||
|
#spi_software_miso_pin: PE15
|
||||||
|
#spi_software_mosi_pin: PE13
|
||||||
|
#spi_software_sclk_pin: PE14
|
||||||
|
#run_current: 0.800
|
||||||
|
#stealthchop_threshold: 999999
|
||||||
|
#diag1_pin: PC1
|
||||||
|
|
||||||
|
#[tmc2130 stepper_y]
|
||||||
|
#cs_pin: PD0
|
||||||
|
#spi_software_miso_pin: PE15
|
||||||
|
#spi_software_mosi_pin: PE13
|
||||||
|
#spi_software_sclk_pin: PE14
|
||||||
|
#run_current: 0.800
|
||||||
|
#stealthchop_threshold: 999999
|
||||||
|
#diag1_pin: PC3
|
||||||
|
|
||||||
|
#[tmc2130 stepper_z]
|
||||||
|
#cs_pin: PE1
|
||||||
|
#spi_software_miso_pin: PE15
|
||||||
|
#spi_software_mosi_pin: PE13
|
||||||
|
#spi_software_sclk_pin: PE14
|
||||||
|
#run_current: 0.650
|
||||||
|
#stealthchop_threshold: 999999
|
||||||
|
#diag1_pin: PC0
|
||||||
|
|
||||||
|
#[tmc2130 extruder]
|
||||||
|
#cs_pin: PC6
|
||||||
|
#spi_software_miso_pin: PE15
|
||||||
|
#spi_software_mosi_pin: PE13
|
||||||
|
#spi_software_sclk_pin: PE14
|
||||||
|
#run_current: 0.800
|
||||||
|
#stealthchop_threshold: 999999
|
||||||
|
#diag1_pin: PC2
|
||||||
|
|
||||||
|
#[tmc2130 extruder1]
|
||||||
|
#cs_pin: PD12
|
||||||
|
#spi_software_miso_pin: PE15
|
||||||
|
#spi_software_mosi_pin: PE13
|
||||||
|
#spi_software_sclk_pin: PE14
|
||||||
|
#run_current: 0.800
|
||||||
|
#stealthchop_threshold: 999999
|
||||||
|
#diag1_pin: PA0
|
|
@ -105,7 +105,7 @@ max_temp: 130
|
||||||
[fan]
|
[fan]
|
||||||
pin: P2.1
|
pin: P2.1
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: P2.2
|
pin: P2.2
|
||||||
|
|
||||||
[mcu]
|
[mcu]
|
||||||
|
|
|
@ -100,7 +100,7 @@ pid_Kd: 948.182
|
||||||
min_temp: 0
|
min_temp: 0
|
||||||
max_temp: 130
|
max_temp: 130
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PC7
|
pin: PC7
|
||||||
|
|
||||||
[fan]
|
[fan]
|
||||||
|
|
|
@ -98,7 +98,7 @@ pid_Kd: 948.182
|
||||||
min_temp: 0
|
min_temp: 0
|
||||||
max_temp: 130
|
max_temp: 130
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PC7
|
pin: PC7
|
||||||
|
|
||||||
[heater_fan controller_fan]
|
[heater_fan controller_fan]
|
||||||
|
|
|
@ -103,7 +103,7 @@ pid_Kd: 948.182
|
||||||
min_temp: 0
|
min_temp: 0
|
||||||
max_temp: 130
|
max_temp: 130
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PC7
|
pin: PC7
|
||||||
|
|
||||||
[fan]
|
[fan]
|
||||||
|
|
|
@ -100,7 +100,7 @@ max_temp: 130
|
||||||
[fan]
|
[fan]
|
||||||
pin: gpio17
|
pin: gpio17
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: gpio18
|
pin: gpio18
|
||||||
|
|
||||||
[heater_fan controller_fan]
|
[heater_fan controller_fan]
|
||||||
|
|
|
@ -288,7 +288,7 @@ max_temp: 130
|
||||||
pin: PC23
|
pin: PC23
|
||||||
|
|
||||||
# Fan1 controlled by extruder
|
# Fan1 controlled by extruder
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PC26
|
pin: PC26
|
||||||
heater: extruder
|
heater: extruder
|
||||||
heater_temp: 45
|
heater_temp: 45
|
||||||
|
|
|
@ -123,7 +123,7 @@ max_temp: 130
|
||||||
[fan]
|
[fan]
|
||||||
pin: PC23 # FAN0
|
pin: PC23 # FAN0
|
||||||
|
|
||||||
#[heater_fan nozzle_cooling_fan]
|
#[heater_fan heatbreak_cooling_fan]
|
||||||
#pin: PC22 # FAN1
|
#pin: PC22 # FAN1
|
||||||
|
|
||||||
#[heater_fan board_cooling_fan]
|
#[heater_fan board_cooling_fan]
|
||||||
|
|
|
@ -101,7 +101,7 @@ max_temp: 130
|
||||||
[fan]
|
[fan]
|
||||||
pin: PC23 # FAN0
|
pin: PC23 # FAN0
|
||||||
|
|
||||||
#[heater_fan nozzle_cooling_fan]
|
#[heater_fan heatbreak_cooling_fan]
|
||||||
#pin: PC26 # FAN1
|
#pin: PC26 # FAN1
|
||||||
|
|
||||||
#[heater_fan board_cooling_fan]
|
#[heater_fan board_cooling_fan]
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
# SBC SPISS pin:PA6, SBCTfrReady:PA3, SerComPins:{PA4, PA5, PA6, PA7}
|
# SBC SPISS pin:PA6, SBCTfrReady:PA3, SerComPins:{PA4, PA5, PA6, PA7}
|
||||||
# CAN Pins - TX:PB14 RX:PB15
|
# CAN Pins - TX:PB14 RX:PB15
|
||||||
# Heaters, Fan outputs - {Out0:PB17 Out1:PC10 Out2:PB13 Out3:PB11 Out4:PA11, Out5:PB2, Out6:PB1} | Out6 is shared with VFD_Out
|
# Heaters, Fan outputs - {Out0:PB17 Out1:PC10 Out2:PB13 Out3:PB11 Out4:PA11, Out5:PB2, Out6:PB1} | Out6 is shared with VFD_Out
|
||||||
|
# Tach Pins for Fans - {Out3.Tach:PB27 Out4.Tach:PB26}
|
||||||
# GPIO_out - {IO1:PB31 IO2:PD9 IO3:PB12 IO4:PD10} IO4 is shared with PSON
|
# GPIO_out - {IO1:PB31 IO2:PD9 IO3:PB12 IO4:PD10} IO4 is shared with PSON
|
||||||
# GPIO_in - {IO1:PB30 IO2:PD8 IO3:PB7 IO4:PC5 IO5:PC4 IO6:PC31}
|
# GPIO_in - {IO1:PB30 IO2:PD8 IO3:PB7 IO4:PC5 IO5:PC4 IO6:PC31}
|
||||||
# Driver Diag - {D0:PA10, D1:PB8, D2:PA22, D3:PA23, D4:PC21, D5:PB10, D6:PA27}
|
# Driver Diag - {D0:PA10, D1:PB8, D2:PA22, D3:PA23, D4:PC21, D5:PB10, D6:PA27}
|
||||||
|
|
|
@ -95,7 +95,7 @@ max_temp: 130
|
||||||
[fan]
|
[fan]
|
||||||
pin: PH5
|
pin: PH5
|
||||||
|
|
||||||
#[heater_fan nozzle_cooling_fan]
|
#[heater_fan heatbreak_cooling_fan]
|
||||||
#pin: PH3
|
#pin: PH3
|
||||||
|
|
||||||
[temperature_sensor board_sensor]
|
[temperature_sensor board_sensor]
|
||||||
|
|
|
@ -97,7 +97,7 @@ max_temp: 130
|
||||||
[fan]
|
[fan]
|
||||||
pin: PC8
|
pin: PC8
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PB0
|
pin: PB0
|
||||||
|
|
||||||
[mcu]
|
[mcu]
|
||||||
|
|
|
@ -65,7 +65,7 @@ max_temp: 130
|
||||||
[fan]
|
[fan]
|
||||||
pin: PH5
|
pin: PH5
|
||||||
|
|
||||||
#[heater_fan nozzle_cooling_fan]
|
#[heater_fan heatbreak_cooling_fan]
|
||||||
#pin: PH3
|
#pin: PH3
|
||||||
|
|
||||||
[mcu]
|
[mcu]
|
||||||
|
|
|
@ -104,7 +104,7 @@ max_temp: 290
|
||||||
[fan]
|
[fan]
|
||||||
pin: PB27
|
pin: PB27
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PA6
|
pin: PA6
|
||||||
|
|
||||||
[mcu]
|
[mcu]
|
||||||
|
|
|
@ -81,7 +81,7 @@ max_temp: 130
|
||||||
[fan]
|
[fan]
|
||||||
pin: PC21
|
pin: PC21
|
||||||
|
|
||||||
#[heater_fan nozzle_cooling_fan]
|
#[heater_fan heatbreak_cooling_fan]
|
||||||
#pin: PC22
|
#pin: PC22
|
||||||
|
|
||||||
[mcu]
|
[mcu]
|
||||||
|
|
|
@ -75,7 +75,7 @@ max_temp: 130
|
||||||
[fan]
|
[fan]
|
||||||
pin: PH5
|
pin: PH5
|
||||||
|
|
||||||
#[heater_fan nozzle_cooling_fan]
|
#[heater_fan heatbreak_cooling_fan]
|
||||||
#pin: PH3
|
#pin: PH3
|
||||||
|
|
||||||
[mcu]
|
[mcu]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# This file contains common pin mappings for the TH3D EZBoard Lite v2.
|
# This file contains common pin mappings for the TH3D EZBoard Lite v2.
|
||||||
# To use this config, the firmware should be compiled for the
|
# To use this config, the firmware should be compiled for the
|
||||||
# STM32F407 with 12mhz Crystal, 48KiB Bootloader, and USB communication.
|
# STM32F405 with 12mhz Crystal, 48KiB Bootloader, and USB communication.
|
||||||
|
|
||||||
# The "make flash" command does not work on this board. Instead,
|
# The "make flash" command does not work on this board. Instead,
|
||||||
# after running "make", copy the generated "out/klipper.bin" file to a
|
# after running "make", copy the generated "out/klipper.bin" file to a
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
# For Anycubic 4Max Pro (not 2.0) owners:
|
# For Anycubic 4Max Pro (not 2.0) owners:
|
||||||
# Be careful when using this config! This config tested only on Anycubic
|
# Be careful when using this config! This config tested only on Anycubic
|
||||||
# 4Max Pro 2.0 with klipper v0.9.1-667-g31761500! At first, you should
|
# 4Max Pro 2.0! At first, you should
|
||||||
# set homing_speed on 5, and run homing and click on the endstops with
|
# set homing_speed on 5, and run homing and click on the endstops with
|
||||||
# your fingers. It is necessary to make sure that all the motors are
|
# your fingers. It is necessary to make sure that all the motors are
|
||||||
# spinning in the right direction, all the temperature sensors show the
|
# spinning in the right direction, all the temperature sensors show the
|
||||||
|
@ -139,3 +139,50 @@ screw4: 265, 5
|
||||||
|
|
||||||
[filament_switch_sensor filament_sensor]
|
[filament_switch_sensor filament_sensor]
|
||||||
switch_pin: ^!PC4
|
switch_pin: ^!PC4
|
||||||
|
|
||||||
|
[output_pin buzz]
|
||||||
|
pin: PC6
|
||||||
|
pwm: True
|
||||||
|
|
||||||
|
[output_pin AUTO_POWEROFF]
|
||||||
|
pin: PD0
|
||||||
|
pwm: True
|
||||||
|
cycle_time: 0.02
|
||||||
|
value: 1
|
||||||
|
|
||||||
|
|
||||||
|
# This macro (M300) uses internal integrated beeper
|
||||||
|
# Just use it in your G-code for making sounds. Example: M300 S1000 P500
|
||||||
|
[gcode_macro M300]
|
||||||
|
gcode:
|
||||||
|
{% set S = params.S|default(800)|float %}
|
||||||
|
{% set P = params.P|default(100)|int %}
|
||||||
|
SET_PIN PIN=buzz VALUE=0.5 CYCLE_TIME={ 1.0 / S | float }
|
||||||
|
G4 P{P}
|
||||||
|
SET_PIN PIN=buzz VALUE=0
|
||||||
|
|
||||||
|
# This macro (M81) uses internal integrated PSU control-relay.
|
||||||
|
# Just use M81 in your end_gcode if you want to poweroff your printer after print.
|
||||||
|
# Note: as in original Marlin firmware, before powerdown, printer will be cool hotend
|
||||||
|
# until temperature will be below 45°С / 113°F.
|
||||||
|
|
||||||
|
[gcode_macro M81]
|
||||||
|
gcode:
|
||||||
|
{% set required_extruder_temp = params.T|default(45)|int %}
|
||||||
|
{% if printer.extruder.temperature > required_extruder_temp|default(45)|int %}
|
||||||
|
M300
|
||||||
|
M300
|
||||||
|
M300
|
||||||
|
M117 COOLING DOWN BEFORE POWER OFF
|
||||||
|
M109 S{required_extruder_temp}
|
||||||
|
SET_PIN PIN=AUTO_POWEROFF VALUE=0.5
|
||||||
|
G4 P60
|
||||||
|
SET_PIN PIN=AUTO_POWEROFF VALUE=1
|
||||||
|
{% else %}
|
||||||
|
M300
|
||||||
|
M117 POWER OFF SOON
|
||||||
|
G4 P10000
|
||||||
|
SET_PIN PIN=AUTO_POWEROFF VALUE=0.5
|
||||||
|
G4 P60
|
||||||
|
SET_PIN PIN=AUTO_POWEROFF VALUE=1
|
||||||
|
{% endif %}
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
# This file contains common pin mappings for the Biqu B1 SE Plus.
|
||||||
|
# To use this config, the firmware should be compiled for the
|
||||||
|
# STM32F407 with a "32KiB bootloader".
|
||||||
|
|
||||||
|
# In newer versions of this board shipped in late 2021 the STM32F429
|
||||||
|
# is used, if this is the case compile for this with a "32KiB bootloader"
|
||||||
|
# You will need to check the chip on your board to identify which you have.
|
||||||
|
#
|
||||||
|
# The "make flash" command does not work on the SKR 2. Instead,
|
||||||
|
# after running "make", copy the generated "out/klipper.bin" file to a
|
||||||
|
# file named "firmware.bin" on an SD card and then restart the SKR 2
|
||||||
|
# with that SD card.
|
||||||
|
|
||||||
|
# See docs/Config_Reference.md for a description of parameters.
|
||||||
|
|
||||||
|
[mcu]
|
||||||
|
serial: /dev/serial/by-id/usb-Klipper_stm32f407xx_1D0039000F47393438343535-if00
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Stepper X Pins and TMC2208 configuration
|
||||||
|
########################################
|
||||||
|
[stepper_x]
|
||||||
|
step_pin: PE2
|
||||||
|
dir_pin: !PE1
|
||||||
|
enable_pin: !PE3
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: !PC1
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 310
|
||||||
|
homing_speed: 50
|
||||||
|
|
||||||
|
[tmc2208 stepper_x]
|
||||||
|
uart_pin: PE0
|
||||||
|
run_current: 0.800
|
||||||
|
stealthchop_threshold: 999999
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Stepper Y Pins and TMC2208 configuration
|
||||||
|
########################################
|
||||||
|
[stepper_y]
|
||||||
|
step_pin: PD5
|
||||||
|
dir_pin: PD4
|
||||||
|
enable_pin: !PD6
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: !PC3
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 310
|
||||||
|
homing_speed: 50
|
||||||
|
|
||||||
|
[tmc2208 stepper_y]
|
||||||
|
uart_pin: PD3
|
||||||
|
run_current: 0.800
|
||||||
|
stealthchop_threshold: 999999
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Stepper Z Pins and TMC2208 configuration
|
||||||
|
########################################
|
||||||
|
[stepper_z]
|
||||||
|
step_pin: PA15
|
||||||
|
dir_pin: PA8
|
||||||
|
enable_pin: !PD1
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 8
|
||||||
|
endstop_pin: probe:z_virtual_endstop
|
||||||
|
homing_speed: 10
|
||||||
|
second_homing_speed: 1
|
||||||
|
position_min: -2
|
||||||
|
position_max: 340
|
||||||
|
|
||||||
|
[tmc2208 stepper_z]
|
||||||
|
uart_pin: PD0
|
||||||
|
run_current: 0.800
|
||||||
|
stealthchop_threshold: 999999
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Extruder Pins and TMC2208 configuration
|
||||||
|
########################################
|
||||||
|
[extruder]
|
||||||
|
step_pin: PD15
|
||||||
|
dir_pin: !PD14
|
||||||
|
enable_pin: !PC7
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 34.2152 # Calibrar - ver https://www.klipper3d.org/Rotation_Distance.html
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: PB3
|
||||||
|
sensor_type: Generic 3950
|
||||||
|
sensor_pin: PA2 #thermistor pin
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 22.2
|
||||||
|
pid_Ki: 1.08
|
||||||
|
pid_Kd: 114
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 250
|
||||||
|
|
||||||
|
[tmc2208 extruder]
|
||||||
|
uart_pin: PC6
|
||||||
|
run_current: 0.800
|
||||||
|
stealthchop_threshold: 999999
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Heater Bed Pins
|
||||||
|
########################################
|
||||||
|
[heater_bed]
|
||||||
|
heater_pin: PD7
|
||||||
|
sensor_type: Generic 3950
|
||||||
|
sensor_pin: PA1
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 54
|
||||||
|
pid_Ki: 0.77
|
||||||
|
pid_Kd: 900
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 110
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Printer Configuration
|
||||||
|
########################################
|
||||||
|
[printer]
|
||||||
|
kinematics: cartesian
|
||||||
|
max_velocity: 200
|
||||||
|
max_accel: 1000
|
||||||
|
max_z_velocity: 5
|
||||||
|
max_z_accel: 100
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Probe configuration
|
||||||
|
########################################
|
||||||
|
[probe]
|
||||||
|
pin: ^!PE4
|
||||||
|
z_offset: 0.0
|
||||||
|
x_offset: 0.0
|
||||||
|
y_offset: 0.0
|
||||||
|
speed: 10.0
|
||||||
|
samples: 2
|
||||||
|
samples_result: average
|
||||||
|
sample_retract_dist: 2.0
|
||||||
|
samples_tolerance: 0.2
|
||||||
|
|
||||||
|
[safe_z_home]
|
||||||
|
home_xy_position: 155,155
|
||||||
|
speed: 100
|
||||||
|
z_hop: 5
|
||||||
|
z_hop_speed: 5
|
||||||
|
|
||||||
|
[output_pin probe_enable]
|
||||||
|
pin: PE5
|
||||||
|
value: 1
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Bed Mesh configuration
|
||||||
|
########################################
|
||||||
|
[bed_mesh]
|
||||||
|
speed: 2000
|
||||||
|
horizontal_move_z: 3
|
||||||
|
mesh_min: 20, 20
|
||||||
|
mesh_max: 290, 290
|
||||||
|
probe_count: 7, 7
|
||||||
|
mesh_pps: 2,2
|
||||||
|
algorithm: bicubic
|
||||||
|
bicubic_tension: 0.2
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Fan Nozzle configuration
|
||||||
|
########################################
|
||||||
|
[fan]
|
||||||
|
pin: PB7
|
||||||
|
|
||||||
|
[heater_fan Cooling_fan]
|
||||||
|
pin: PB6
|
||||||
|
max_power: 1.0
|
||||||
|
kick_start_time: 0.100
|
||||||
|
heater: heater_bed
|
||||||
|
|
||||||
|
[heater_fan Board_fan]
|
||||||
|
pin: PB5
|
||||||
|
max_power: 1.0
|
||||||
|
kick_start_time: 0.100
|
||||||
|
heater: extruder
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Filament Sensor configuration
|
||||||
|
########################################
|
||||||
|
[filament_switch_sensor Sensor_Filamento]
|
||||||
|
switch_pin: !PC2
|
||||||
|
pause_on_runout: true #pause handled by macro
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Motor Power Pin
|
||||||
|
########################################
|
||||||
|
[output_pin motor_power]
|
||||||
|
pin: PC13
|
||||||
|
value: 1
|
|
@ -0,0 +1,107 @@
|
||||||
|
# This file contains pin mappings for the BQ Prusa i3 Hephestos from 2014
|
||||||
|
# (https://www.reprap.org/wiki/Prusa_i3_Hephestos)
|
||||||
|
# It was sold in kit form, and uses a RAMPS board with HD44780 display without
|
||||||
|
# heated bed or any modern amenities.
|
||||||
|
|
||||||
|
# To use this config, the firmware should be compiled for the AVR atmega2560.
|
||||||
|
|
||||||
|
# See docs/Config_Reference.md for a description of parameters.
|
||||||
|
|
||||||
|
[display]
|
||||||
|
lcd_type: hd44780
|
||||||
|
rs_pin: PH1
|
||||||
|
e_pin: PH0
|
||||||
|
d4_pin: PA1
|
||||||
|
d5_pin: PA3
|
||||||
|
d6_pin: PA5
|
||||||
|
d7_pin: PA7
|
||||||
|
encoder_pins: ^PC4, ^PC6
|
||||||
|
click_pin: ^!PC2
|
||||||
|
kill_pin: ^!PG0
|
||||||
|
|
||||||
|
[stepper_x]
|
||||||
|
step_pin: PF0
|
||||||
|
dir_pin: !PF1
|
||||||
|
enable_pin: !PD7
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: ^!PE5
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 215
|
||||||
|
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: 210
|
||||||
|
homing_speed: 50
|
||||||
|
|
||||||
|
[stepper_z]
|
||||||
|
step_pin: PL3
|
||||||
|
dir_pin: !PL1
|
||||||
|
enable_pin: !PK0
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 0.8
|
||||||
|
endstop_pin: ^!PD3
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 200
|
||||||
|
homing_speed: 3
|
||||||
|
|
||||||
|
[extruder]
|
||||||
|
step_pin: PA4
|
||||||
|
dir_pin: PA6
|
||||||
|
enable_pin: !PA2
|
||||||
|
microsteps: 16
|
||||||
|
# measured extruding 100mm of filament with stock Hephestos extruder
|
||||||
|
rotation_distance: 31.825
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: PB4
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PK5
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 250
|
||||||
|
control: pid
|
||||||
|
pid_kp: 19.462
|
||||||
|
pid_ki: 0.713
|
||||||
|
pid_kd: 132.830
|
||||||
|
|
||||||
|
|
||||||
|
# 5 points for manual bed leveling that still leave room for accessing the stock screws
|
||||||
|
[bed_screws]
|
||||||
|
screw1: 40, 40
|
||||||
|
screw2: 180, 40
|
||||||
|
screw3: 180, 160
|
||||||
|
screw4: 40, 160
|
||||||
|
screw5: 110, 100
|
||||||
|
|
||||||
|
[fan]
|
||||||
|
pin: PH6
|
||||||
|
|
||||||
|
[printer]
|
||||||
|
kinematics: cartesian
|
||||||
|
max_velocity: 300
|
||||||
|
max_accel: 3000
|
||||||
|
# Must limit Z velocity, since RAMPS does not have enough timer resolution
|
||||||
|
max_z_velocity: 3
|
||||||
|
max_z_accel: 100
|
||||||
|
|
||||||
|
[mcu]
|
||||||
|
serial: /dev/ttyUSB0
|
||||||
|
|
||||||
|
# Common EXP1 / EXP2 (display) pins
|
||||||
|
[board_pins]
|
||||||
|
aliases:
|
||||||
|
# Common EXP1 header found on many "all-in-one" ramps clones
|
||||||
|
EXP1_1=PC0, EXP1_3=PH0, EXP1_5=PA1, EXP1_7=PA5, EXP1_9=<GND>,
|
||||||
|
EXP1_2=PC2, EXP1_4=PH1, EXP1_6=PA3, EXP1_8=PA7, EXP1_10=<5V>,
|
||||||
|
# EXP2 header
|
||||||
|
EXP2_1=PB3, EXP2_3=PC6, EXP2_5=PC4, EXP2_7=PL0, EXP2_9=<GND>,
|
||||||
|
EXP2_2=PB1, EXP2_4=PB0, EXP2_6=PB2, EXP2_8=PG0, EXP2_10=<RST>
|
||||||
|
# Pins EXP2_1, EXP2_6, EXP2_2 are also MISO, MOSI, SCK of bus "spi"
|
||||||
|
# Note, some boards wire: EXP2_8=<RST>, EXP2_10=PG0
|
|
@ -0,0 +1,162 @@
|
||||||
|
# This file contains common pin mappings for the 2020 Creality CR-10
|
||||||
|
# V3. The mainboard is a Creality 3D v2.5.2 (8-bit mainboard with
|
||||||
|
# ATMega2560). To use this config, the firmware should be compiled for
|
||||||
|
# the AVR atmega2560.
|
||||||
|
|
||||||
|
# See docs/Config_Reference.md for a description of parameters.
|
||||||
|
|
||||||
|
# For better compatibility with GCodes generated for Marlin, you
|
||||||
|
# may wish to add the following section, if you have BLTouch:
|
||||||
|
#[gcode_macro G29]
|
||||||
|
#gcode:
|
||||||
|
# BED_MESH_CALIBRATE
|
||||||
|
|
||||||
|
[stepper_x]
|
||||||
|
step_pin: PF0 #ar54
|
||||||
|
dir_pin: PF1 #ar55
|
||||||
|
enable_pin: !PD7 #!ar38
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: ^PE5 #^ar3
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 300
|
||||||
|
homing_speed: 50
|
||||||
|
|
||||||
|
[stepper_y]
|
||||||
|
step_pin: PF6 #ar60
|
||||||
|
dir_pin: PF7 #ar61
|
||||||
|
enable_pin: !PF2 #!ar56
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: ^PJ1 #^ar14
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 300
|
||||||
|
homing_speed: 50
|
||||||
|
|
||||||
|
[stepper_z]
|
||||||
|
step_pin: PL3 #ar46
|
||||||
|
dir_pin: !PL1 #!ar48
|
||||||
|
enable_pin: !PK0 #!ar62
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 8
|
||||||
|
position_max: 400
|
||||||
|
#Uncomment if you have a BL-Touch:
|
||||||
|
#position_min: -4
|
||||||
|
#endstop_pin: probe:z_virtual_endstop
|
||||||
|
#and comment the follwing lines:
|
||||||
|
position_endstop: 0.0
|
||||||
|
endstop_pin: ^PD3 #ar18
|
||||||
|
|
||||||
|
[safe_z_home]
|
||||||
|
home_xy_position: 104.25,147.6
|
||||||
|
speed: 80
|
||||||
|
z_hop: 10
|
||||||
|
z_hop_speed: 10
|
||||||
|
|
||||||
|
[extruder]
|
||||||
|
step_pin: PA4 # ar26
|
||||||
|
dir_pin: !PA6 # !ar28
|
||||||
|
enable_pin: !PA2 # !ar24
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 7.7201944 # 16 microsteps * 200 steps/rotation / steps/mm
|
||||||
|
#Correction formula is new_rotation_distance = old_rotation_distance * mmsExtracted / 100.0
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: PB4 #ar10
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PK5 #analog13
|
||||||
|
control: pid
|
||||||
|
pid_kp: 22.107
|
||||||
|
pid_ki: 1.170
|
||||||
|
pid_kd: 104.458
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 255
|
||||||
|
|
||||||
|
[heater_bed]
|
||||||
|
heater_pin: PH5 #ar8
|
||||||
|
sensor_type: ATC Semitec 104GT-2
|
||||||
|
sensor_pin: PK6 #analog14
|
||||||
|
control: pid
|
||||||
|
#Stock PID configuration taken from Marlin
|
||||||
|
pid_Kp: 201.86
|
||||||
|
pid_Ki: 10.67
|
||||||
|
pid_Kd: 954.96
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 130
|
||||||
|
|
||||||
|
[fan]
|
||||||
|
pin: PH6 #ar9
|
||||||
|
|
||||||
|
[mcu]
|
||||||
|
serial: /dev/ttyUSB0
|
||||||
|
|
||||||
|
[printer]
|
||||||
|
kinematics: cartesian
|
||||||
|
max_velocity: 300
|
||||||
|
max_accel: 3000
|
||||||
|
max_z_velocity: 5
|
||||||
|
max_z_accel: 100
|
||||||
|
|
||||||
|
[display]
|
||||||
|
lcd_type: st7920
|
||||||
|
cs_pin: PH1 #ar16
|
||||||
|
sclk_pin: PA1 #ar23
|
||||||
|
sid_pin: PH0 #ar17
|
||||||
|
encoder_pins: ^PC4, ^PC6 #^ar33, ^ar31
|
||||||
|
click_pin: ^!PC2 #^!ar35
|
||||||
|
|
||||||
|
|
||||||
|
#Uncomment the following lines if you have a BL-Touch
|
||||||
|
#[bltouch]
|
||||||
|
#sensor_pin: ^PD2 #^ar19
|
||||||
|
#control_pin: PB5 #ar11
|
||||||
|
#set_output_mode: 5V
|
||||||
|
#pin_move_time: 0.4
|
||||||
|
#stow_on_each_sample: False
|
||||||
|
#probe_with_touch_mode: False
|
||||||
|
#x_offset: 45.75
|
||||||
|
#y_offset: -3.40
|
||||||
|
#z_offset: 3.28
|
||||||
|
#samples: 2
|
||||||
|
#sample_retract_dist: 2
|
||||||
|
#samples_result: average
|
||||||
|
|
||||||
|
#Uncomment the following lines if you have a BL-Touch
|
||||||
|
#[bed_mesh]
|
||||||
|
#speed: 50
|
||||||
|
#horizontal_move_z: 6
|
||||||
|
#mesh_min: 46.50,0.75
|
||||||
|
#mesh_max: 253.5,295.85
|
||||||
|
#probe_count: 7,7
|
||||||
|
#algorithm: bicubic
|
||||||
|
|
||||||
|
[pause_resume]
|
||||||
|
recover_velocity: 50
|
||||||
|
|
||||||
|
[filament_switch_sensor fil_runout_sensor]
|
||||||
|
pause_on_runout: True
|
||||||
|
switch_pin: PE4 #ar2
|
||||||
|
|
||||||
|
[bed_screws]
|
||||||
|
screw1: 33,29
|
||||||
|
screw1_name: front left screw
|
||||||
|
screw2: 273,29
|
||||||
|
screw2_name: front right screw
|
||||||
|
screw3: 273,269
|
||||||
|
screw3_name: rear right screw
|
||||||
|
screw4: 33,269
|
||||||
|
screw4_name: rear left screw
|
||||||
|
|
||||||
|
#Uncomment the following lines if you have a BL-Touch
|
||||||
|
#[screws_tilt_adjust]
|
||||||
|
#screw1: 0,29
|
||||||
|
#screw1_name: front left screw
|
||||||
|
#screw2: 228,29
|
||||||
|
#screw2_name: front right screw
|
||||||
|
#screw3: 228,269
|
||||||
|
#screw3_name: rear right screw
|
||||||
|
#screw4: 0,269
|
||||||
|
#screw4_name: rear left screw
|
||||||
|
#speed: 50
|
||||||
|
#horizontal_move_z: 10
|
||||||
|
#screw_thread: CW-M3
|
|
@ -0,0 +1,131 @@
|
||||||
|
# This file contains pin mappings for the stock 2021 Creality Ender 3
|
||||||
|
# S1 (and S1 pro). To use this config, check the STM32 Chip on the
|
||||||
|
# V2.4S1 Board then during "make menuconfig" select either the
|
||||||
|
# STM32F103 with a "28KiB bootloader" or select the STM32F401 with a
|
||||||
|
# "64KiB bootloader" and serial (on USART1 PA10/PA9) communication for
|
||||||
|
# both depending on the STM32 chip installed on your printer's
|
||||||
|
# motherboard.
|
||||||
|
|
||||||
|
# If you prefer a direct serial connection, in "make menuconfig"
|
||||||
|
# select "Enable extra low-level configuration options" and select
|
||||||
|
# Serial (on USART2 PA3/PA2), which is broken out on the 10 pin IDC
|
||||||
|
# cable used for the LCD module as follows:
|
||||||
|
# 3: Tx, 4: Rx, 9: GND, 10: VCC
|
||||||
|
|
||||||
|
# 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 changed to "firmware.bin"
|
||||||
|
|
||||||
|
# See docs/Config_Reference.md for a description of parameters.
|
||||||
|
|
||||||
|
[stepper_x]
|
||||||
|
step_pin: PC2
|
||||||
|
dir_pin: PB9
|
||||||
|
enable_pin: !PC3
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: !PA5
|
||||||
|
position_endstop: -10
|
||||||
|
position_max: 235
|
||||||
|
position_min: -15
|
||||||
|
homing_speed: 50
|
||||||
|
|
||||||
|
[stepper_y]
|
||||||
|
step_pin: PB8
|
||||||
|
dir_pin: PB7
|
||||||
|
enable_pin: !PC3
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: !PA6
|
||||||
|
position_endstop: -10
|
||||||
|
position_max: 241
|
||||||
|
position_min: -15
|
||||||
|
homing_speed: 50
|
||||||
|
|
||||||
|
[stepper_z]
|
||||||
|
step_pin: PB6
|
||||||
|
dir_pin: !PB5
|
||||||
|
enable_pin: !PC3
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 8
|
||||||
|
endstop_pin: probe:z_virtual_endstop
|
||||||
|
position_max: 270
|
||||||
|
position_min: -4
|
||||||
|
|
||||||
|
[extruder]
|
||||||
|
step_pin: PB4
|
||||||
|
dir_pin: PB3
|
||||||
|
enable_pin: !PC3
|
||||||
|
microsteps: 16
|
||||||
|
gear_ratio: 42:12
|
||||||
|
rotation_distance: 26.359
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: PA1
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PC5
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 23.561
|
||||||
|
pid_Ki: 1.208
|
||||||
|
pid_Kd: 114.859
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 250
|
||||||
|
|
||||||
|
[heater_bed]
|
||||||
|
heater_pin: PA7
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PC4
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 71.867
|
||||||
|
pid_Ki: 1.536
|
||||||
|
pid_Kd: 840.843
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 110
|
||||||
|
|
||||||
|
[heater_fan hotend_fan]
|
||||||
|
pin: PC0
|
||||||
|
|
||||||
|
[fan]
|
||||||
|
pin: PA0
|
||||||
|
|
||||||
|
[mcu]
|
||||||
|
serial: /dev/serial/by-id/usb-1a86_USB_Serial-if00-port0
|
||||||
|
restart_method: command
|
||||||
|
|
||||||
|
[printer]
|
||||||
|
kinematics: cartesian
|
||||||
|
max_velocity: 300
|
||||||
|
max_accel: 2000
|
||||||
|
max_z_velocity: 5
|
||||||
|
max_z_accel: 100
|
||||||
|
|
||||||
|
[bltouch]
|
||||||
|
sensor_pin: ^PC14
|
||||||
|
control_pin: PC13
|
||||||
|
x_offset: -31.8
|
||||||
|
y_offset: -40.5
|
||||||
|
z_offset: 0
|
||||||
|
probe_with_touch_mode: true
|
||||||
|
stow_on_each_sample: false
|
||||||
|
|
||||||
|
[bed_mesh]
|
||||||
|
speed: 120
|
||||||
|
mesh_min: 20, 20
|
||||||
|
mesh_max: 200, 200
|
||||||
|
probe_count: 4,4
|
||||||
|
algorithm: bicubic
|
||||||
|
|
||||||
|
[safe_z_home]
|
||||||
|
home_xy_position: 147, 154
|
||||||
|
speed: 75
|
||||||
|
z_hop: 5
|
||||||
|
z_hop_speed: 5
|
||||||
|
move_to_previous: true
|
||||||
|
|
||||||
|
[filament_switch_sensor e0_sensor]
|
||||||
|
switch_pin: !PC15
|
||||||
|
pause_on_runout: true
|
||||||
|
runout_gcode: PAUSE
|
||||||
|
|
||||||
|
[pause_resume]
|
||||||
|
recover_velocity: 25
|
|
@ -0,0 +1,109 @@
|
||||||
|
# This file contains pin mappings for the Creality Sermoon V1
|
||||||
|
# with CR-FDM-v2.4.S1.200 motherboard.
|
||||||
|
|
||||||
|
# To use this config, during "make menuconfig" select the STM32F401
|
||||||
|
# with a "64KiB bootloader" and serial (on USART1 PA10/PA9)
|
||||||
|
# communication.
|
||||||
|
|
||||||
|
# If you prefer a direct serial connection, in "make menuconfig"
|
||||||
|
# select "Enable extra low-level configuration options" and select
|
||||||
|
# Serial (on USART2 PA3/PA2), which is broken out on the 10 pin IDC
|
||||||
|
# cable used for the LCD module as follows:
|
||||||
|
# 3: Tx, 4: Rx, 9: GND, 10: VCC
|
||||||
|
|
||||||
|
# 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 changed to "firmware.bin"
|
||||||
|
|
||||||
|
# See docs/Config_Reference.md for a description of parameters.
|
||||||
|
|
||||||
|
[stepper_x]
|
||||||
|
step_pin: PA7
|
||||||
|
dir_pin: !PA4
|
||||||
|
enable_pin: !PB8
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: PC4
|
||||||
|
position_endstop: 175
|
||||||
|
position_max: 175
|
||||||
|
position_min: 0
|
||||||
|
homing_speed: 50
|
||||||
|
|
||||||
|
[stepper_y]
|
||||||
|
step_pin: PB0
|
||||||
|
dir_pin: PB10
|
||||||
|
enable_pin: !PB8
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
endstop_pin: PB13
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 175
|
||||||
|
position_min: 0
|
||||||
|
homing_speed: 50
|
||||||
|
|
||||||
|
[stepper_z]
|
||||||
|
step_pin: PB7
|
||||||
|
dir_pin: PB6
|
||||||
|
enable_pin: !PB8
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 8
|
||||||
|
endstop_pin: PB3
|
||||||
|
position_endstop: 165
|
||||||
|
position_max: 168
|
||||||
|
position_min: -3
|
||||||
|
|
||||||
|
[extruder]
|
||||||
|
step_pin: PB1
|
||||||
|
dir_pin: PB12
|
||||||
|
enable_pin: !PB8
|
||||||
|
microsteps: 16
|
||||||
|
gear_ratio: 42:12
|
||||||
|
rotation_distance: 26.359
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: PC5
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PC1
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 30.090
|
||||||
|
pid_Ki: 1.875
|
||||||
|
pid_Kd: 120.735
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 290
|
||||||
|
|
||||||
|
[heater_bed]
|
||||||
|
heater_pin: PB9
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PC0
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 75.694
|
||||||
|
pid_Ki: 1.160
|
||||||
|
pid_Kd: 1234.759
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 90
|
||||||
|
|
||||||
|
[fan]
|
||||||
|
pin: PA5
|
||||||
|
|
||||||
|
[fan_generic side_fan]
|
||||||
|
pin: PC15
|
||||||
|
|
||||||
|
# [controller_fan controller_fan]
|
||||||
|
# In order to access the controller fan, the controller fan needs to be plugged
|
||||||
|
# in another location. See https://github.com/Klipper3d/klipper/pull/5621
|
||||||
|
# for more information.
|
||||||
|
# pin: PB4
|
||||||
|
|
||||||
|
[mcu]
|
||||||
|
serial: /dev/serial/by-id/usb-1a86_USB_Serial-if00-port0
|
||||||
|
restart_method: command
|
||||||
|
|
||||||
|
[printer]
|
||||||
|
kinematics: cartesian
|
||||||
|
max_velocity: 300
|
||||||
|
max_accel: 2000
|
||||||
|
max_z_velocity: 5
|
||||||
|
max_z_accel: 100
|
||||||
|
|
||||||
|
[pause_resume]
|
||||||
|
recover_velocity: 25
|
|
@ -112,7 +112,7 @@ max_temp: 100
|
||||||
[fan]
|
[fan]
|
||||||
pin: PB5
|
pin: PB5
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PB4
|
pin: PB4
|
||||||
|
|
||||||
[mcu]
|
[mcu]
|
||||||
|
|
|
@ -67,7 +67,7 @@ max_extrude_only_distance: 300
|
||||||
[fan]
|
[fan]
|
||||||
pin: PH5
|
pin: PH5
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PH3
|
pin: PH3
|
||||||
|
|
||||||
[heater_bed]
|
[heater_bed]
|
||||||
|
|
|
@ -114,7 +114,7 @@ max_temp: 130
|
||||||
#define FAN_PIN 8
|
#define FAN_PIN 8
|
||||||
pin: PH5
|
pin: PH5
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
#define FAN1_PIN 6
|
#define FAN1_PIN 6
|
||||||
pin: PH3
|
pin: PH3
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ max_temp: 130
|
||||||
[fan]
|
[fan]
|
||||||
pin: PH5
|
pin: PH5
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PH3
|
pin: PH3
|
||||||
|
|
||||||
[mcu]
|
[mcu]
|
||||||
|
|
|
@ -128,7 +128,7 @@ max_temp: 130
|
||||||
#On Dual v3 heat break fan is connected to PH3 (part cooling fan on single extruder)
|
#On Dual v3 heat break fan is connected to PH3 (part cooling fan on single extruder)
|
||||||
pin: PH3
|
pin: PH3
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
#On Dual v3 part fans are connected to PH5 (heat break fan on single extruder)
|
#On Dual v3 part fans are connected to PH5 (heat break fan on single extruder)
|
||||||
pin: PH5
|
pin: PH5
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ max_temp: 90
|
||||||
[fan]
|
[fan]
|
||||||
pin: PH5
|
pin: PH5
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PH3
|
pin: PH3
|
||||||
|
|
||||||
[mcu]
|
[mcu]
|
||||||
|
|
|
@ -104,7 +104,7 @@ mesh_max: 225, 225
|
||||||
[fan]
|
[fan]
|
||||||
pin: PH5
|
pin: PH5
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PH3
|
pin: PH3
|
||||||
|
|
||||||
[mcu]
|
[mcu]
|
||||||
|
|
|
@ -63,7 +63,7 @@ max_temp: 300
|
||||||
[fan]
|
[fan]
|
||||||
pin: PH5
|
pin: PH5
|
||||||
|
|
||||||
[heater_fan nozzle_cooling_fan]
|
[heater_fan heatbreak_cooling_fan]
|
||||||
pin: PH4
|
pin: PH4
|
||||||
heater: extruder
|
heater: extruder
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
# This file contains common pin mappings for the BIGTREETECH EBBCan
|
||||||
|
# Canbus board. To use this config, the firmware should be compiled for the
|
||||||
|
# STM32F072 with "8 MHz crystal" and "USB (on PA11/PA12)" or "CAN bus (on PB8/PB9)".
|
||||||
|
# The "EBB Can" micro-controller will be used to control the components on the nozzle.
|
||||||
|
|
||||||
|
# See docs/Config_Reference.md for a description of parameters.
|
||||||
|
|
||||||
|
[mcu EBBCan]
|
||||||
|
serial: /dev/serial/by-id/usb-Klipper_Klipper_firmware_12345-if00
|
||||||
|
#canbus_uuid: 0e0d81e4210c
|
||||||
|
|
||||||
|
[adxl345]
|
||||||
|
cs_pin: EBBCan: PB12
|
||||||
|
spi_bus: spi2
|
||||||
|
axes_map: x,y,z
|
||||||
|
|
||||||
|
[extruder]
|
||||||
|
step_pin: EBBCan: PA9
|
||||||
|
dir_pin: !EBBCan: PA8
|
||||||
|
enable_pin: !EBBCan: PA10
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 33.500
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: EBBCan: PB1
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: EBBCan: PA0
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 21.527
|
||||||
|
pid_Ki: 1.063
|
||||||
|
pid_Kd: 108.982
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 250
|
||||||
|
|
||||||
|
#sensor_type:MAX31865
|
||||||
|
#sensor_pin: EBBCan: PA15
|
||||||
|
#spi_bus: spi1a
|
||||||
|
#rtd_nominal_r: 100
|
||||||
|
#rtd_reference_r: 430
|
||||||
|
#rtd_num_of_wires: 2
|
||||||
|
|
||||||
|
[tmc2209 extruder]
|
||||||
|
uart_pin: EBBCan: PA13
|
||||||
|
run_current: 0.650
|
||||||
|
stealthchop_threshold: 999999
|
||||||
|
|
||||||
|
[fan]
|
||||||
|
pin: EBBCan: PA1
|
||||||
|
|
||||||
|
[heater_fan hotend_fan]
|
||||||
|
pin: EBBCan: PA2
|
||||||
|
heater: extruder
|
||||||
|
heater_temp: 50.0
|
||||||
|
|
||||||
|
#[neopixel hotend_rgb]
|
||||||
|
#pin: EBBCan:PA3
|
||||||
|
|
||||||
|
#[bltouch]
|
||||||
|
#sensor_pin: ^EBBCan:PA5
|
||||||
|
#control_pin: EBBCan:PA4
|
||||||
|
|
||||||
|
#[filament_switch_sensor switch_sensor]
|
||||||
|
#switch_pin: EBBCan:PB6
|
||||||
|
|
||||||
|
#[filament_motion_sensor motion_sensor]
|
||||||
|
#switch_pin: ^EBBCan:PB7
|
|
@ -0,0 +1,68 @@
|
||||||
|
# This file contains common pin mappings for the BIGTREETECH EBBCan
|
||||||
|
# Canbus board. To use this config, the firmware should be compiled for the
|
||||||
|
# STM32G0B1 with "8 MHz crystal" and "USB (on PA11/PA12)" or "CAN bus (on PB0/PB1)".
|
||||||
|
# The "EBB Can" micro-controller will be used to control the components on the nozzle.
|
||||||
|
|
||||||
|
# See docs/Config_Reference.md for a description of parameters.
|
||||||
|
|
||||||
|
[mcu EBBCan]
|
||||||
|
serial: /dev/serial/by-id/usb-Klipper_Klipper_firmware_12345-if00
|
||||||
|
#canbus_uuid: 0e0d81e4210c
|
||||||
|
|
||||||
|
[adxl345]
|
||||||
|
cs_pin: EBBCan: PB12
|
||||||
|
spi_software_sclk_pin: EBBCan: PB10
|
||||||
|
spi_software_mosi_pin: EBBCan: PB11
|
||||||
|
spi_software_miso_pin: EBBCan: PB2
|
||||||
|
axes_map: x,y,z
|
||||||
|
|
||||||
|
[extruder]
|
||||||
|
step_pin: EBBCan: PD0
|
||||||
|
dir_pin: !EBBCan: PD1
|
||||||
|
enable_pin: !EBBCan: PD2
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 33.500
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: EBBCan: PA2
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: EBBCan: PA3
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 21.527
|
||||||
|
pid_Ki: 1.063
|
||||||
|
pid_Kd: 108.982
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 250
|
||||||
|
|
||||||
|
# sensor_type:MAX31865
|
||||||
|
# sensor_pin: EBBCan: PA4
|
||||||
|
# spi_bus: spi1
|
||||||
|
# rtd_nominal_r: 100
|
||||||
|
# rtd_reference_r: 430
|
||||||
|
# rtd_num_of_wires: 2
|
||||||
|
|
||||||
|
[tmc2209 extruder]
|
||||||
|
uart_pin: EBBCan: PA15
|
||||||
|
run_current: 0.650
|
||||||
|
stealthchop_threshold: 999999
|
||||||
|
|
||||||
|
[fan]
|
||||||
|
pin: EBBCan: PA0
|
||||||
|
|
||||||
|
[heater_fan hotend_fan]
|
||||||
|
pin: EBBCan: PA1
|
||||||
|
heater: extruder
|
||||||
|
heater_temp: 50.0
|
||||||
|
|
||||||
|
#[neopixel hotend_rgb]
|
||||||
|
#pin: EBBCan:PD3
|
||||||
|
|
||||||
|
#[bltouch]
|
||||||
|
#sensor_pin: ^EBBCan:PB8
|
||||||
|
#control_pin: EBBCan:PB9
|
||||||
|
|
||||||
|
#[filament_switch_sensor switch_sensor]
|
||||||
|
#switch_pin: EBBCan:PB4
|
||||||
|
|
||||||
|
#[filament_motion_sensor motion_sensor]
|
||||||
|
#switch_pin: ^EBBCan:PB3
|
|
@ -0,0 +1,58 @@
|
||||||
|
# This file contains common pin mappings for the Huvud V0.61 by Bondus.
|
||||||
|
# https://github.com/bondus/Klipperhuvudboard
|
||||||
|
# To use this config, copy the contents into your main config file.
|
||||||
|
|
||||||
|
# The huvud is not capable of running a printer on it's own. It
|
||||||
|
# needs to be paired with another board that will control other
|
||||||
|
# aspects of the printer.
|
||||||
|
|
||||||
|
# The firmware should be compiled for the STM32F103 with a "2KiB
|
||||||
|
# bootloader" and a "8MHz crystal" clock reference.
|
||||||
|
# Select CAN bus (on PB8/PB9) or USB under communication interface.
|
||||||
|
# Flash by running make flash FLASH_DEVICE=1209:beba
|
||||||
|
|
||||||
|
# See docs/Config_Reference.md for a description of parameters.
|
||||||
|
|
||||||
|
[mcu huvud]
|
||||||
|
canbus_uuid: ac20f0bbda05
|
||||||
|
# Identify your canbus_uuid by running:
|
||||||
|
# ~/klippy-env/bin/python ~/klipper/scripts/canbus_query.py can0
|
||||||
|
|
||||||
|
[extruder]
|
||||||
|
step_pin: huvud: PB3
|
||||||
|
dir_pin: huvud: PB4
|
||||||
|
enable_pin: !huvud: PB5
|
||||||
|
rotation_distance: 22.52453125
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.75
|
||||||
|
heater_pin: huvud: PA6
|
||||||
|
sensor_type: NTC 100K MGB18-104F39050L32
|
||||||
|
sensor_pin: huvud: PA0
|
||||||
|
pullup_resistor: 2200
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 300
|
||||||
|
control: pid
|
||||||
|
pid_kp: 26.213
|
||||||
|
pid_ki: 1.304
|
||||||
|
pid_kd: 131.721
|
||||||
|
|
||||||
|
[tmc2209 extruder]
|
||||||
|
uart_pin: huvud: PA10
|
||||||
|
tx_pin: huvud: PA9
|
||||||
|
run_current: 0.35
|
||||||
|
|
||||||
|
[probe]
|
||||||
|
pin: huvud: PB12
|
||||||
|
z_offset: 0
|
||||||
|
|
||||||
|
[fan]
|
||||||
|
pin: huvud: PA8
|
||||||
|
|
||||||
|
[heater_fan extruder_fan]
|
||||||
|
pin: huvud: PA7
|
||||||
|
|
||||||
|
[adxl345]
|
||||||
|
cs_pin: PB1
|
||||||
|
|
||||||
|
[led huvud_led]
|
||||||
|
blue_pin: huvud: PC13
|
|
@ -95,6 +95,21 @@ click_pin: ^!EXP1_2
|
||||||
pin: EXP1_1
|
pin: EXP1_1
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
# Anet 128x64 LCD with alternative wiring (ANET_FULL_GRAPHICS_LCD_ALT_WIRING in Marlin)
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
[display]
|
||||||
|
lcd_type: st7920
|
||||||
|
cs_pin: EXP1_8
|
||||||
|
sclk_pin: EXP1_4
|
||||||
|
sid_pin: EXP1_6
|
||||||
|
encoder_pins: ^!EXP1_7, ^!EXP1_5
|
||||||
|
click_pin: ^!EXP1_3
|
||||||
|
|
||||||
|
[output_pin beeper]
|
||||||
|
pin: EXP1_1
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Fysetc Mini 12864Panel v2.1 (with neopixel backlight leds)
|
# Fysetc Mini 12864Panel v2.1 (with neopixel backlight leds)
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
|
@ -175,6 +175,29 @@ gcode:
|
||||||
sensor.temperature,
|
sensor.temperature,
|
||||||
sensor.humidity))}
|
sensor.humidity))}
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Override M117 command with rawparams
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
# The macro below will override the default M117 command to echo the message.
|
||||||
|
#
|
||||||
|
# It uses the rawparams pseudo-variable that contains the full unparsed
|
||||||
|
# parameters that was passed to the M117 command.
|
||||||
|
#
|
||||||
|
# As this can include comments, we are trimming the text when a `;` or `#` is
|
||||||
|
# found, and escaping any existing `"`
|
||||||
|
|
||||||
|
[gcode_macro M117]
|
||||||
|
rename_existing: M117.1
|
||||||
|
gcode:
|
||||||
|
{% if rawparams %}
|
||||||
|
{% set escaped_msg = rawparams.split(';', 1)[0].split('\x23', 1)[0]|replace('"', '\\"') %}
|
||||||
|
SET_DISPLAY_TEXT MSG="{escaped_msg}"
|
||||||
|
RESPOND TYPE=command MSG="{escaped_msg}"
|
||||||
|
{% else %}
|
||||||
|
SET_DISPLAY_TEXT
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
# SDCard 'looping' (aka Marlin M808 commands) support
|
# SDCard 'looping' (aka Marlin M808 commands) support
|
||||||
#
|
#
|
||||||
# Support SDCard looping
|
# Support SDCard looping
|
||||||
|
@ -186,3 +209,55 @@ gcode:
|
||||||
{% if params.K is not defined and params.L is defined %}SDCARD_LOOP_BEGIN COUNT={params.L|int}{% endif %}
|
{% if params.K is not defined and params.L is defined %}SDCARD_LOOP_BEGIN COUNT={params.L|int}{% endif %}
|
||||||
{% if params.K is not defined and params.L is not defined %}SDCARD_LOOP_END{% endif %}
|
{% if params.K is not defined and params.L is not defined %}SDCARD_LOOP_END{% endif %}
|
||||||
{% if params.K is defined and params.L is not defined %}SDCARD_LOOP_DESIST{% endif %}
|
{% if params.K is defined and params.L is not defined %}SDCARD_LOOP_DESIST{% endif %}
|
||||||
|
|
||||||
|
# Cancel object (aka Marlin/RRF M486 commands) support
|
||||||
|
#
|
||||||
|
# Enable object exclusion
|
||||||
|
[exclude_object]
|
||||||
|
|
||||||
|
[gcode_macro M486]
|
||||||
|
gcode:
|
||||||
|
# Parameters known to M486 are as follows:
|
||||||
|
# [C<flag>] Cancel the current object
|
||||||
|
# [P<index>] Cancel the object with the given index
|
||||||
|
# [S<index>] Set the index of the current object.
|
||||||
|
# If the object with the given index has been canceled, this will cause
|
||||||
|
# the firmware to skip to the next object. The value -1 is used to
|
||||||
|
# indicate something that isn’t an object and shouldn’t be skipped.
|
||||||
|
# [T<count>] Reset the state and set the number of objects
|
||||||
|
# [U<index>] Un-cancel the object with the given index. This command will be
|
||||||
|
# ignored if the object has already been skipped
|
||||||
|
|
||||||
|
{% if 'exclude_object' not in printer %}
|
||||||
|
{action_raise_error("[exclude_object] is not enabled")}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if 'T' in params %}
|
||||||
|
EXCLUDE_OBJECT RESET=1
|
||||||
|
|
||||||
|
{% for i in range(params.T | int) %}
|
||||||
|
EXCLUDE_OBJECT_DEFINE NAME={i}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if 'C' in params %}
|
||||||
|
EXCLUDE_OBJECT CURRENT=1
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if 'P' in params %}
|
||||||
|
EXCLUDE_OBJECT NAME={params.P}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if 'S' in params %}
|
||||||
|
{% if params.S == '-1' %}
|
||||||
|
{% if printer.exclude_object.current_object %}
|
||||||
|
EXCLUDE_OBJECT_END NAME={printer.exclude_object.current_object}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
EXCLUDE_OBJECT_START NAME={params.S}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if 'U' in params %}
|
||||||
|
EXCLUDE_OBJECT RESET=1 NAME={params.U}
|
||||||
|
{% endif %}
|
||||||
|
|
|
@ -4,9 +4,9 @@ This document describes Klipper's CAN bus support.
|
||||||
|
|
||||||
## Device Hardware
|
## Device Hardware
|
||||||
|
|
||||||
Klipper currently only supports CAN on stm32 chips. In addition, the
|
Klipper currently supports CAN on stm32 and rp2040 chips. In addition,
|
||||||
micro-controller chip must support CAN and it must be on a board that
|
the micro-controller chip must be on a board that has a CAN
|
||||||
has a CAN transceiver.
|
transceiver.
|
||||||
|
|
||||||
To compile for CAN, run `make menuconfig` and select "CAN bus" as the
|
To compile for CAN, run `make menuconfig` and select "CAN bus" as the
|
||||||
communication interface. Finally, compile the micro-controller code
|
communication interface. Finally, compile the micro-controller code
|
||||||
|
@ -73,7 +73,7 @@ powered and wired correctly, and then run:
|
||||||
If uninitialized CAN devices are detected the above command will
|
If uninitialized CAN devices are detected the above command will
|
||||||
report lines like the following:
|
report lines like the following:
|
||||||
```
|
```
|
||||||
Found canbus_uuid=11aa22bb33cc
|
Found canbus_uuid=11aa22bb33cc, Application: Klipper
|
||||||
```
|
```
|
||||||
|
|
||||||
Each device will have a unique identifier. In the above example,
|
Each device will have a unique identifier. In the above example,
|
||||||
|
@ -91,3 +91,40 @@ the CAN bus to communicate with the device - for example:
|
||||||
[mcu my_can_mcu]
|
[mcu my_can_mcu]
|
||||||
canbus_uuid: 11aa22bb33cc
|
canbus_uuid: 11aa22bb33cc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## USB to CAN bus bridge mode
|
||||||
|
|
||||||
|
Some micro-controllers support selecting "USB to CAN bus bridge" mode
|
||||||
|
during "make menuconfig". This mode may allow one to use a
|
||||||
|
micro-controller as both a "USB to CAN bus adapter" and as a Klipper
|
||||||
|
node.
|
||||||
|
|
||||||
|
When Klipper uses this mode the micro-controller appears as a "USB CAN
|
||||||
|
bus adapter" under Linux. The "Klipper bridge mcu" itself will appear
|
||||||
|
as if was on this CAN bus - it can be identified via `canbus_query.py`
|
||||||
|
and configured like other CAN bus Klipper nodes. It will appear
|
||||||
|
alongside other devices that are actually on the CAN bus.
|
||||||
|
|
||||||
|
Some important notes when using this mode:
|
||||||
|
|
||||||
|
* The "bridge mcu" is not actually on the CAN bus. Messages to and
|
||||||
|
from it do not consume bandwidth on the CAN bus. The mcu can not be
|
||||||
|
seen by other adapters that may be on the CAN bus.
|
||||||
|
|
||||||
|
* It is necessary to configure the `can0` (or similar) interface in
|
||||||
|
Linux in order to communicate with the bus. However, Linux CAN bus
|
||||||
|
speed and CAN bus bit-timing options are ignored by Klipper.
|
||||||
|
Currently, the CAN bus frequency is specified during "make
|
||||||
|
menuconfig" and the bus speed specified in Linux is ignored.
|
||||||
|
|
||||||
|
* Whenever the "bridge mcu" is reset, Linux will disable the
|
||||||
|
corresponding `can0` interface. To ensure proper handling of
|
||||||
|
FIRMWARE_RESTART and RESTART commands, it is recommended to replace
|
||||||
|
`auto` with `allow-hotplug` in the `/etc/network/interfaces.d/can0`
|
||||||
|
file. For example:
|
||||||
|
```
|
||||||
|
allow-hotplug can0
|
||||||
|
iface can0 can static
|
||||||
|
bitrate 500000
|
||||||
|
up ifconfig $IFACE txqueuelen 128
|
||||||
|
```
|
||||||
|
|
|
@ -38,23 +38,23 @@ with a RESP_NEED_NODEID response message.
|
||||||
The CMD_QUERY_UNASSIGNED message format is:
|
The CMD_QUERY_UNASSIGNED message format is:
|
||||||
`<1-byte message_id = 0x00>`
|
`<1-byte message_id = 0x00>`
|
||||||
|
|
||||||
### CMD_SET_NODEID message
|
### CMD_SET_KLIPPER_NODEID message
|
||||||
|
|
||||||
This command assigns a `canbus_nodeid` to the micro-controller with a
|
This command assigns a `canbus_nodeid` to the micro-controller with a
|
||||||
given `canbus_uuid`.
|
given `canbus_uuid`.
|
||||||
|
|
||||||
The CMD_SET_NODEID message format is:
|
The CMD_SET_KLIPPER_NODEID message format is:
|
||||||
`<1-byte message_id = 0x01><6-byte canbus_uuid><1-byte canbus_nodeid>`
|
`<1-byte message_id = 0x01><6-byte canbus_uuid><1-byte canbus_nodeid>`
|
||||||
|
|
||||||
### RESP_NEED_NODEID message
|
### RESP_NEED_NODEID message
|
||||||
|
|
||||||
The RESP_NEED_NODEID message format is:
|
The RESP_NEED_NODEID message format is:
|
||||||
`<1-byte message_id = 0x20><6-byte canbus_uuid>`
|
`<1-byte message_id = 0x20><6-byte canbus_uuid><1-byte set_klipper_nodeid = 0x01>`
|
||||||
|
|
||||||
## Data Packets
|
## Data Packets
|
||||||
|
|
||||||
A micro-controller that has been assigned a nodeid via the
|
A micro-controller that has been assigned a nodeid via the
|
||||||
CMD_SET_NODEID command can send and receive data packets.
|
CMD_SET_KLIPPER_NODEID command can send and receive data packets.
|
||||||
|
|
||||||
The packet data in messages using the node's receive CAN bus id
|
The packet data in messages using the node's receive CAN bus id
|
||||||
(`canbus_nodeid * 2 + 256`) are simply appended to a buffer, and when
|
(`canbus_nodeid * 2 + 256`) are simply appended to a buffer, and when
|
||||||
|
|
|
@ -130,17 +130,13 @@ gcode:
|
||||||
|
|
||||||
### The "rawparams" variable
|
### The "rawparams" variable
|
||||||
|
|
||||||
The full unparsed parameters for the running macro can be access via the `rawparams` pseudo-variable.
|
The full unparsed parameters for the running macro can be access via the
|
||||||
|
`rawparams` pseudo-variable.
|
||||||
|
|
||||||
This is quite useful if you want to change the behavior of certain commands like the `M117`. For example:
|
Note that this will include any comments that were part of the original command.
|
||||||
|
|
||||||
```
|
See the [sample-macros.cfg](../config/sample-macros.cfg) file for an example
|
||||||
[gcode_macro M117]
|
showing how to override the `M117` command using `rawparams`.
|
||||||
rename_existing: M117.1
|
|
||||||
gcode:
|
|
||||||
M117.1 { rawparams }
|
|
||||||
M118 { rawparams }
|
|
||||||
```
|
|
||||||
|
|
||||||
### The "printer" Variable
|
### The "printer" Variable
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,16 @@ All dates in this document are approximate.
|
||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
|
|
||||||
|
20220616: It was previously possible to flash an rp2040 in bootloader
|
||||||
|
mode by running `make flash FLASH_DEVICE=first`. The equivalent
|
||||||
|
command is now `make flash FLASH_DEVICE=2e8a:0003`.
|
||||||
|
|
||||||
|
20220612: The rp2040 micro-controller now has a workaround for the
|
||||||
|
"rp2040-e5" USB errata. This should make initial USB connections more
|
||||||
|
reliable. However, it may result in a change in behavior for the
|
||||||
|
gpio15 pin. It is unlikely the gpio15 behavior change will be
|
||||||
|
noticeable.
|
||||||
|
|
||||||
20220407: The temperature_fan `pid_integral_max` config option has
|
20220407: The temperature_fan `pid_integral_max` config option has
|
||||||
been removed (it was deprecated on 20210612).
|
been removed (it was deprecated on 20210612).
|
||||||
|
|
||||||
|
|
|
@ -1154,9 +1154,9 @@ home_xy_position:
|
||||||
# than z_hop, then this will lift the head to a height of z_hop. If
|
# than z_hop, then this will lift the head to a height of z_hop. If
|
||||||
# the Z axis is not already homed the head is lifted by z_hop.
|
# the Z axis is not already homed the head is lifted by z_hop.
|
||||||
# The default is to not implement Z hop.
|
# The default is to not implement Z hop.
|
||||||
#z_hop_speed: 20.0
|
#z_hop_speed: 15.0
|
||||||
# Speed (in mm/s) at which the Z axis is lifted prior to homing. The
|
# Speed (in mm/s) at which the Z axis is lifted prior to homing. The
|
||||||
# default is 20mm/s.
|
# default is 15 mm/s.
|
||||||
#move_to_previous: False
|
#move_to_previous: False
|
||||||
# When set to True, the X and Y axes are reset to their previous
|
# When set to True, the X and Y axes are reset to their previous
|
||||||
# positions after Z axis homing. The default is False.
|
# positions after Z axis homing. The default is False.
|
||||||
|
@ -1334,6 +1334,9 @@ path:
|
||||||
# are not supported). One may point this to OctoPrint's upload
|
# are not supported). One may point this to OctoPrint's upload
|
||||||
# directory (generally ~/.octoprint/uploads/ ). This parameter must
|
# directory (generally ~/.octoprint/uploads/ ). This parameter must
|
||||||
# be provided.
|
# be provided.
|
||||||
|
#on_error_gcode:
|
||||||
|
# A list of G-Code commands to execute when an error is reported.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### [sdcard_loop]
|
### [sdcard_loop]
|
||||||
|
@ -1432,6 +1435,20 @@ Enable the "M118" and "RESPOND" extended
|
||||||
# override the "default_type".
|
# override the "default_type".
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### [exclude_object]
|
||||||
|
Enables support to exclude or cancel individual objects during the printing
|
||||||
|
process.
|
||||||
|
|
||||||
|
See the [exclude objects guide](Exclude_Object.md) and
|
||||||
|
[command reference](G-Codes.md#excludeobject)
|
||||||
|
for additional information. See the
|
||||||
|
[sample-macros.cfg](../config/sample-macros.cfg) file for a
|
||||||
|
Marlin/RepRapFirmware compatible M486 G-Code macro.
|
||||||
|
|
||||||
|
```
|
||||||
|
[exclude_object]
|
||||||
|
```
|
||||||
|
|
||||||
## Resonance compensation
|
## Resonance compensation
|
||||||
|
|
||||||
### [input_shaper]
|
### [input_shaper]
|
||||||
|
@ -1508,6 +1525,24 @@ cs_pin:
|
||||||
# measurements.
|
# measurements.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### [mpu9250]
|
||||||
|
|
||||||
|
Support for mpu9250 and mpu6050 accelerometers (one may define any
|
||||||
|
number of sections with an "mpu9250" prefix).
|
||||||
|
|
||||||
|
```
|
||||||
|
[mpu9250 my_accelerometer]
|
||||||
|
#i2c_address:
|
||||||
|
# Default is 104 (0x68).
|
||||||
|
#i2c_mcu:
|
||||||
|
#i2c_bus:
|
||||||
|
#i2c_speed: 400000
|
||||||
|
# See the "common I2C settings" section for a description of the
|
||||||
|
# above parameters. The default "i2c_speed" is 400000.
|
||||||
|
#axes_map: x, y, z
|
||||||
|
# See the "adxl345" section for information on this parameter.
|
||||||
|
```
|
||||||
|
|
||||||
### [resonance_tester]
|
### [resonance_tester]
|
||||||
|
|
||||||
Support for resonance testing and automatic input shaper calibration.
|
Support for resonance testing and automatic input shaper calibration.
|
||||||
|
@ -2109,7 +2144,7 @@ temperature sensors that are reported via the M105 command.
|
||||||
|
|
||||||
Klipper includes definitions for many types of temperature sensors.
|
Klipper includes definitions for many types of temperature sensors.
|
||||||
These sensors may be used in any config section that requires a
|
These sensors may be used in any config section that requires a
|
||||||
temperature sensor (such as an `[extruder]` or `[heated_bed]`
|
temperature sensor (such as an `[extruder]` or `[heater_bed]`
|
||||||
section).
|
section).
|
||||||
|
|
||||||
### Common thermistors
|
### Common thermistors
|
||||||
|
@ -4463,6 +4498,22 @@ SPI bus.
|
||||||
The following parameters are generally available for devices using an
|
The following parameters are generally available for devices using an
|
||||||
I2C bus.
|
I2C bus.
|
||||||
|
|
||||||
|
Note that Klipper's current micro-controller support for i2c is
|
||||||
|
generally not tolerant to line noise. Unexpected errors on the i2c
|
||||||
|
wires may result in Klipper raising a run-time error. Klipper's
|
||||||
|
support for error recovery varies between each micro-controller type.
|
||||||
|
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. The Klipper "linux" micro-controller supports a
|
||||||
|
400000 speed, 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 supports a rate of 400000 via the
|
||||||
|
`i2c_speed` parameter. All other Klipper micro-controllers use a
|
||||||
|
100000 rate and ignore the `i2c_speed` parameter.
|
||||||
|
|
||||||
```
|
```
|
||||||
#i2c_address:
|
#i2c_address:
|
||||||
# The i2c address of the device. This must specified as a decimal
|
# The i2c address of the device. This must specified as a decimal
|
||||||
|
@ -4476,8 +4527,9 @@ I2C bus.
|
||||||
# the type of micro-controller.
|
# the type of micro-controller.
|
||||||
#i2c_speed:
|
#i2c_speed:
|
||||||
# The I2C speed (in Hz) to use when communicating with the device.
|
# The I2C speed (in Hz) to use when communicating with the device.
|
||||||
# On some micro-controllers changing this value has no effect. The
|
# The Klipper implementation on most micro-controllers is hard-coded
|
||||||
# default is 100000.
|
# to 100000 and changing this value has no effect. The default is
|
||||||
|
# 100000.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Common UART settings
|
### Common UART settings
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
# Exclude Objects
|
||||||
|
|
||||||
|
The `[exclude_object]` module allows Klipper to exclude objects while a print is
|
||||||
|
in progress. To enable this feature include an [exclude_object config
|
||||||
|
section](Config_Reference.md#exclude_object) (also see the [command
|
||||||
|
reference](G-Codes.md#exclude-object) and
|
||||||
|
[sample-macros.cfg](../config/sample-macros.cfg) file for a
|
||||||
|
Marlin/RepRapFirmware compatible M486 G-Code macro.)
|
||||||
|
|
||||||
|
Unlike other 3D printer firmware options, a printer running Klipper utilizes a
|
||||||
|
suite of components and users have many options to choose from. Therefore, in
|
||||||
|
order to provide a a consistent user experience, the `[exclude_object]` module
|
||||||
|
will establish a contract or API of sorts. The contract covers the contents of
|
||||||
|
the gcode file, how the internal state of the module is controlled, and how that
|
||||||
|
state is provided to clients.
|
||||||
|
|
||||||
|
## Workflow Overview
|
||||||
|
A typical workflow for printing a file might look like this:
|
||||||
|
1. Slicing is completed and the file is uploaded for printing. During the
|
||||||
|
upload, the file is processed and `[exclude_object]` markers are added to
|
||||||
|
the file. Alternately, slicers may be configured to prepare object exclusion
|
||||||
|
markers natively, or in it's own pre-processing step.
|
||||||
|
2. When printing starts, Klipper will reset the `[exclude_object]`
|
||||||
|
[status](Status_Reference.md#exclude_object).
|
||||||
|
3. When Klipper processes the `EXCLUDE_OBJECT_DEFINE` block, it will update the
|
||||||
|
status with the known objects and pass it on to clients.
|
||||||
|
4. The client may use that information to present a UI to the user so that
|
||||||
|
progress can be tracked. Klipper will update the status to include the
|
||||||
|
currently printing object which the client can use for display purposes.
|
||||||
|
5. If the user requests that an object be cancelled, the client will issue an
|
||||||
|
`EXCLUDE_OBJECT NAME=<name>` command to Klipper.
|
||||||
|
6. When Klipper process the command, it will add the object to the list of
|
||||||
|
excluded objects and update the status for the client.
|
||||||
|
7. The client will receive the updated status from Klipper and can use that
|
||||||
|
information to reflect the object's status in the UI.
|
||||||
|
8. When printing finishes, the `[exclude_object]` status will continue to be
|
||||||
|
available until another action resets it.
|
||||||
|
|
||||||
|
## The GCode File
|
||||||
|
The specialized gcode processing needed to support excluding objects does not
|
||||||
|
fit into Klipper's core design goals. Therefore, this module requires that the
|
||||||
|
file is processed before being sent to Klipper for printing. Using a
|
||||||
|
post-process script in the slicer or having middleware process the file on
|
||||||
|
upload are two possibilities for preparing the file for Klipper. A reference
|
||||||
|
post-processing script is available both as an executable and a python library,
|
||||||
|
see
|
||||||
|
[cancelobject-preprocessor](https://github.com/kageurufu/cancelobject-preprocessor).
|
||||||
|
|
||||||
|
### Object Definitions
|
||||||
|
|
||||||
|
The `EXCLUDE_OBJECT_DEFINE` command is used to provide a summary of each object
|
||||||
|
in the gcode file to be printed. Provides a summary of an object in the file.
|
||||||
|
Objects don't need to be defined in order to be referenced by other commands.
|
||||||
|
The primary purpose of this command is to provide information to the UI without
|
||||||
|
needing to parse the entire gcode file.
|
||||||
|
|
||||||
|
Object definitions are named, to allow users to easily select an object to be
|
||||||
|
excluded, and additional metadata may be provided to allow for graphical
|
||||||
|
cancellation displays. Currently defined metadata includes a `CENTER` X,Y
|
||||||
|
coordinate, and a `POLYGON` list of X,Y points representing a minimal outline of
|
||||||
|
the object. This could be a simple bounding box, or a complicated hull for
|
||||||
|
showing more detailed visualizations of the printed objects. Especially when
|
||||||
|
gcode files include multiple parts with overlapping bounding regions, center
|
||||||
|
points become hard to visually distinguish. `POLYGONS` must be a json-compatible
|
||||||
|
array of point `[X,Y]` tuples without whitespace. Additional parameters will be
|
||||||
|
saved as strings in the object definition and provided in status updates.
|
||||||
|
|
||||||
|
`EXCLUDE_OBJECT_DEFINE NAME=calibration_pyramid CENTER=50,50
|
||||||
|
POLYGON=[[40,40],[50,60],[60,40]]`
|
||||||
|
|
||||||
|
All available G-Code commands are documented in the [G-Code
|
||||||
|
Reference](./G-Codes.md#excludeobject)
|
||||||
|
|
||||||
|
## Status Information
|
||||||
|
The state of this module is provided to clients by the [exclude_object
|
||||||
|
status](Status_Reference.md#exclude_object).
|
||||||
|
|
||||||
|
The status is reset when:
|
||||||
|
- The Klipper firmware is restarted.
|
||||||
|
- There is a reset of the `[virtual_sdcard]`. Notably, this is reset by Klipper
|
||||||
|
at the start of a print.
|
||||||
|
- When an `EXCLUDE_OBJECT_DEFINE RESET=1` command is issued.
|
||||||
|
|
||||||
|
The list of defined objects is represented in the `exclude_object.objects`
|
||||||
|
status field. In a well defined gcode file, this will be done with
|
||||||
|
`EXCLUDE_OBJECT_DEFINE` commands at the beginning of the file. This will
|
||||||
|
provide clients with object names and coordinates so the UI can provide a
|
||||||
|
graphical representation of the objects if desired.
|
||||||
|
|
||||||
|
As the print progresses, the `exclude_object.current_object` status field will
|
||||||
|
be updated as Klipper processes `EXCLUDE_OBJECT_START` and `EXCLUDE_OBJECT_END`
|
||||||
|
commands. The `current_object` field will be set even if the object has been
|
||||||
|
excluded. Undefined objects marked with a `EXCLUDE_OBJECT_START` will be added
|
||||||
|
to the known objects to assist in UI hinting, without any additional metadata.
|
||||||
|
|
||||||
|
As `EXCLUDE_OBJECT` commands are issued, the list of excluded objects is
|
||||||
|
provided in the `exclude_object.excluded_objects` array. Since Klipper looks
|
||||||
|
ahead to process upcoming gcode, there may be a delay between when the command
|
||||||
|
is issued and when the status is updated.
|
29
docs/FAQ.md
29
docs/FAQ.md
|
@ -1,34 +1,9 @@
|
||||||
# Frequently Asked Questions
|
# Frequently Asked Questions
|
||||||
|
|
||||||
1. [How can I donate to the project?](#how-can-i-donate-to-the-project)
|
|
||||||
2. [How do I calculate the rotation_distance config parameter?](#how-do-i-calculate-the-rotation_distance-config-parameter)
|
|
||||||
3. [Where's my serial port?](#wheres-my-serial-port)
|
|
||||||
4. [When the micro-controller restarts the device changes to /dev/ttyUSB1](#when-the-micro-controller-restarts-the-device-changes-to-devttyusb1)
|
|
||||||
5. [The "make flash" command doesn't work](#the-make-flash-command-doesnt-work)
|
|
||||||
6. [How do I change the serial baud rate?](#how-do-i-change-the-serial-baud-rate)
|
|
||||||
7. [Can I run Klipper on something other than a Raspberry Pi 3?](#can-i-run-klipper-on-something-other-than-a-raspberry-pi-3)
|
|
||||||
8. [Can I run multiple instances of Klipper on the same host machine?](#can-i-run-multiple-instances-of-klipper-on-the-same-host-machine)
|
|
||||||
9. [Do I have to use OctoPrint?](#do-i-have-to-use-octoprint)
|
|
||||||
10. [Why can't I move the stepper before homing the printer?](#why-cant-i-move-the-stepper-before-homing-the-printer)
|
|
||||||
11. [Why is the Z position_endstop set to 0.5 in the default configs?](#why-is-the-z-position_endstop-set-to-05-in-the-default-configs)
|
|
||||||
12. [I converted my config from Marlin and the X/Y axes work fine, but I just get a screeching noise when homing the Z axis](#i-converted-my-config-from-marlin-and-the-xy-axes-work-fine-but-i-just-get-a-screeching-noise-when-homing-the-z-axis)
|
|
||||||
13. [My TMC motor driver turns off in the middle of a print](#my-tmc-motor-driver-turns-off-in-the-middle-of-a-print)
|
|
||||||
14. [I keep getting random "Lost communication with MCU" errors](#i-keep-getting-random-lost-communication-with-mcu-errors)
|
|
||||||
15. [My Raspberry Pi keeps rebooting during prints](#my-raspberry-pi-keeps-rebooting-during-prints)
|
|
||||||
16. [When I set `restart_method=command` my AVR device just hangs on a restart](#when-i-set-restart_methodcommand-my-avr-device-just-hangs-on-a-restart)
|
|
||||||
17. [Will the heaters be left on if the Raspberry Pi crashes?](#will-the-heaters-be-left-on-if-the-raspberry-pi-crashes)
|
|
||||||
18. [How do I convert a Marlin pin number to a Klipper pin name?](#how-do-i-convert-a-marlin-pin-number-to-a-klipper-pin-name)
|
|
||||||
19. [Do I have to wire my device to a specific type of micro-controller pin?](#do-i-have-to-wire-my-device-to-a-specific-type-of-micro-controller-pin)
|
|
||||||
20. [How do I cancel an M109/M190 "wait for temperature" request?](#how-do-i-cancel-an-m109m190-wait-for-temperature-request)
|
|
||||||
21. [Can I find out whether the printer has lost steps?](#can-i-find-out-whether-the-printer-has-lost-steps)
|
|
||||||
22. [Why does Klipper report errors? I lost my print!](#why-does-klipper-report-errors-i-lost-my-print)
|
|
||||||
23. [How do I upgrade to the latest software?](#how-do-i-upgrade-to-the-latest-software)
|
|
||||||
24. [How do I uninstall klipper?](#how-do-i-uninstall-klipper)
|
|
||||||
|
|
||||||
## How can I donate to the project?
|
## How can I donate to the project?
|
||||||
|
|
||||||
Thanks. Kevin has a Patreon page at:
|
Thank you for your support. See the [Sponsors page](Sponsors.md) for
|
||||||
[https://www.patreon.com/koconnor](https://www.patreon.com/koconnor)
|
information.
|
||||||
|
|
||||||
## How do I calculate the rotation_distance config parameter?
|
## How do I calculate the rotation_distance config parameter?
|
||||||
|
|
||||||
|
|
|
@ -295,6 +295,11 @@ provides the following standard G-Code commands:
|
||||||
- Display Message: `M117 <message>`
|
- Display Message: `M117 <message>`
|
||||||
- Set build percentage: `M73 P<percent>`
|
- Set build percentage: `M73 P<percent>`
|
||||||
|
|
||||||
|
Also provided is the following extended G-Code command:
|
||||||
|
- `SET_DISPLAY_TEXT MSG=<message>`: Performs the equivalent of M117,
|
||||||
|
setting the supplied `MSG` as the current display message. If
|
||||||
|
`MSG` is omitted the display will be cleared.
|
||||||
|
|
||||||
### [dual_carriage]
|
### [dual_carriage]
|
||||||
|
|
||||||
The following command is available when the
|
The following command is available when the
|
||||||
|
@ -320,6 +325,57 @@ parameter is provided it arranges for the given endstop phase setting
|
||||||
to be written to the config file (in conjunction with the SAVE_CONFIG
|
to be written to the config file (in conjunction with the SAVE_CONFIG
|
||||||
command).
|
command).
|
||||||
|
|
||||||
|
### [exclude_object]
|
||||||
|
|
||||||
|
The following commands are available when an
|
||||||
|
[exclude_object config section](Config_Reference.md#exclude_object) is
|
||||||
|
enabled (also see the [exclude object guide](Exclude_Object.md)):
|
||||||
|
|
||||||
|
#### `EXCLUDE_OBJECT`
|
||||||
|
`EXCLUDE_OBJECT [NAME=object_name] [CURRENT=1] [RESET=1]`:
|
||||||
|
With no parameters, this will return a list of all currently excluded objects.
|
||||||
|
|
||||||
|
When the `NAME` parameter is given, the named object will be excluded from
|
||||||
|
printing.
|
||||||
|
|
||||||
|
When the `CURRENT` parameter is given, the current object will be excluded from
|
||||||
|
printing.
|
||||||
|
|
||||||
|
When the `RESET` parameter is given, the list of excluded objects will be
|
||||||
|
cleared. Additionally including `NAME` will only reset the named object. This
|
||||||
|
**can** cause print failures, if layers were already skipped.
|
||||||
|
|
||||||
|
#### `EXCLUDE_OBJECT_DEFINE`
|
||||||
|
`EXCLUDE_OBJECT_DEFINE [NAME=object_name [CENTER=X,Y] [POLYGON=[[x,y],...]]
|
||||||
|
[RESET=1] [JSON=1]`:
|
||||||
|
Provides a summary of an object in the file.
|
||||||
|
|
||||||
|
With no parameters provided, this will list the defined objects known to
|
||||||
|
Klipper. Returns a list of strings, unless the `JSON` parameter is given,
|
||||||
|
when it will return object details in json format.
|
||||||
|
|
||||||
|
When the `NAME` parameter is included, this defines an object to be excluded.
|
||||||
|
|
||||||
|
- `NAME`: This parameter is required. It is the identifier used by other
|
||||||
|
commands in this module.
|
||||||
|
- `CENTER`: An X,Y coordinate for the object.
|
||||||
|
- `POLYGON`: An array of X,Y coordinates that provide an outline for the
|
||||||
|
object.
|
||||||
|
|
||||||
|
When the `RESET` parameter is provided, all defined objects will be cleared, and
|
||||||
|
the `[exclude_object]` module will be reset.
|
||||||
|
|
||||||
|
#### `EXCLUDE_OBJECT_START`
|
||||||
|
`EXCLUDE_OBJECT_START NAME=object_name`:
|
||||||
|
This command takes a `NAME` parameter and denotes the start of the gcode for an
|
||||||
|
object on the current layer.
|
||||||
|
|
||||||
|
#### `EXCLUDE_OBJECT_END`
|
||||||
|
`EXCLUDE_OBJECT_END [NAME=object_name]`:
|
||||||
|
Denotes the end of the object's gcode for the layer. It is paired with
|
||||||
|
`EXCLUDE_OBJECT_START`. A `NAME` parameter is optional, and will only warn when
|
||||||
|
the provided name does not match the current object.
|
||||||
|
|
||||||
### [extruder]
|
### [extruder]
|
||||||
|
|
||||||
The following commands are available if an
|
The following commands are available if an
|
||||||
|
@ -896,23 +952,28 @@ all enabled accelerometer chips.
|
||||||
#### TEST_RESONANCES
|
#### TEST_RESONANCES
|
||||||
`TEST_RESONANCES AXIS=<axis> OUTPUT=<resonances,raw_data>
|
`TEST_RESONANCES AXIS=<axis> OUTPUT=<resonances,raw_data>
|
||||||
[NAME=<name>] [FREQ_START=<min_freq>] [FREQ_END=<max_freq>]
|
[NAME=<name>] [FREQ_START=<min_freq>] [FREQ_END=<max_freq>]
|
||||||
[HZ_PER_SEC=<hz_per_sec>] [INPUT_SHAPING=[<0:1>]]`: Runs the resonance
|
[HZ_PER_SEC=<hz_per_sec>] [CHIPS=<adxl345_chip_name>]
|
||||||
|
[POINT=x,y,z] [INPUT_SHAPING=[<0:1>]]`: Runs the resonance
|
||||||
test in all configured probe points for the requested "axis" and
|
test in all configured probe points for the requested "axis" and
|
||||||
measures the acceleration using the accelerometer chips configured for
|
measures the acceleration using the accelerometer chips configured for
|
||||||
the respective axis. "axis" can either be X or Y, or specify an
|
the respective axis. "axis" can either be X or Y, or specify an
|
||||||
arbitrary direction as `AXIS=dx,dy`, where dx and dy are floating
|
arbitrary direction as `AXIS=dx,dy`, where dx and dy are floating
|
||||||
point numbers defining a direction vector (e.g. `AXIS=X`, `AXIS=Y`, or
|
point numbers defining a direction vector (e.g. `AXIS=X`, `AXIS=Y`, or
|
||||||
`AXIS=1,-1` to define a diagonal direction). Note that `AXIS=dx,dy`
|
`AXIS=1,-1` to define a diagonal direction). Note that `AXIS=dx,dy`
|
||||||
and `AXIS=-dx,-dy` is equivalent. If `INPUT_SHAPING=0` or not set
|
and `AXIS=-dx,-dy` is equivalent. `adxl345_chip_name` can be one or
|
||||||
(default), disables input shaping for the resonance testing, because
|
more configured adxl345 chip,delimited with comma, for example
|
||||||
|
`CHIPS="adxl345, adxl345 rpi"`. Note that `adxl345` can be omitted from
|
||||||
|
named adxl345 chips. If POINT is specified it will override the point(s)
|
||||||
|
configured in `[resonance_tester]`. If `INPUT_SHAPING=0` or not set(default),
|
||||||
|
disables input shaping for the resonance testing, because
|
||||||
it is not valid to run the resonance testing with the input shaper
|
it is not valid to run the resonance testing with the input shaper
|
||||||
enabled. `OUTPUT` parameter is a comma-separated list of which outputs
|
enabled. `OUTPUT` parameter is a comma-separated list of which outputs
|
||||||
will be written. If `raw_data` is requested, then the raw
|
will be written. If `raw_data` is requested, then the raw
|
||||||
accelerometer data is written into a file or a series of files
|
accelerometer data is written into a file or a series of files
|
||||||
`/tmp/raw_data_<axis>_[<point>_]<name>.csv` with (`<point>_` part of
|
`/tmp/raw_data_<axis>_[<chip_name>_][<point>_]<name>.csv` with
|
||||||
the name generated only if more than 1 probe point is configured). If
|
(`<point>_` part of the name generated only if more than 1 probe point
|
||||||
`resonances` is specified, the frequency response is calculated
|
is configured or POINT is specified). If `resonances` is specified, the
|
||||||
(across all probe points) and written into
|
frequency response is calculated (across all probe points) and written into
|
||||||
`/tmp/resonances_<axis>_<name>.csv` file. If unset, OUTPUT defaults to
|
`/tmp/resonances_<axis>_<name>.csv` file. If unset, OUTPUT defaults to
|
||||||
`resonances`, and NAME defaults to the current time in
|
`resonances`, and NAME defaults to the current time in
|
||||||
"YYYYMMDD_HHMMSS" format.
|
"YYYYMMDD_HHMMSS" format.
|
||||||
|
@ -949,6 +1010,8 @@ The following additional commands are also available.
|
||||||
configured default prefix (or `echo: ` if no prefix is configured).
|
configured default prefix (or `echo: ` if no prefix is configured).
|
||||||
- `RESPOND TYPE=echo MSG="<message>"`: echo the message prepended with
|
- `RESPOND TYPE=echo MSG="<message>"`: echo the message prepended with
|
||||||
`echo: `.
|
`echo: `.
|
||||||
|
- `RESPOND TYPE=echo_no_space MSG="<message>"`: echo the message prepended with
|
||||||
|
`echo:` without a space between prefix and message, helpful for compatibility with some octoprint plugins that expect very specific formatting.
|
||||||
- `RESPOND TYPE=command MSG="<message>"`: echo the message prepended
|
- `RESPOND TYPE=command MSG="<message>"`: echo the message prepended
|
||||||
with `// `. OctoPrint can be configured to respond to these messages
|
with `// `. OctoPrint can be configured to respond to these messages
|
||||||
(e.g. `RESPOND TYPE=command MSG=action:pause`).
|
(e.g. `RESPOND TYPE=command MSG=action:pause`).
|
||||||
|
@ -979,7 +1042,7 @@ is enabled (also see the
|
||||||
[manual level guide](Manual_Level.md#adjusting-bed-leveling-screws-using-the-bed-probe)).
|
[manual level guide](Manual_Level.md#adjusting-bed-leveling-screws-using-the-bed-probe)).
|
||||||
|
|
||||||
#### SCREWS_TILT_CALCULATE
|
#### SCREWS_TILT_CALCULATE
|
||||||
`SCREWS_TILT_CALCULATE [DIRECTION=CW|CCW]
|
`SCREWS_TILT_CALCULATE [DIRECTION=CW|CCW] [MAX_DEVIATION=<value>]
|
||||||
[<probe_parameter>=<value>]`: This command will invoke the bed screws
|
[<probe_parameter>=<value>]`: This command will invoke the bed screws
|
||||||
adjustment tool. It will command the nozzle to different locations (as
|
adjustment tool. It will command the nozzle to different locations (as
|
||||||
defined in the config file) probing the z height and calculate the
|
defined in the config file) probing the z height and calculate the
|
||||||
|
@ -987,7 +1050,9 @@ number of knob turns to adjust the bed level. If DIRECTION is
|
||||||
specified, the knob turns will all be in the same direction, clockwise
|
specified, the knob turns will all be in the same direction, clockwise
|
||||||
(CW) or counterclockwise (CCW). See the PROBE command for details on
|
(CW) or counterclockwise (CCW). See the PROBE command for details on
|
||||||
the optional probe parameters. IMPORTANT: You MUST always do a G28
|
the optional probe parameters. IMPORTANT: You MUST always do a G28
|
||||||
before using this command.
|
before using this command. If MAX_DEVIATION is specified, the command
|
||||||
|
will raise a gcode error if any difference in the screw height
|
||||||
|
relative to the base screw height is greater than the value provided.
|
||||||
|
|
||||||
### [sdcard_loop]
|
### [sdcard_loop]
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,18 @@ and **will not work**. The recommended connection scheme:
|
||||||
| SDA | 19 | GPIO10 (SPI0_MOSI) |
|
| SDA | 19 | GPIO10 (SPI0_MOSI) |
|
||||||
| SCL | 23 | GPIO11 (SPI0_SCLK) |
|
| SCL | 23 | GPIO11 (SPI0_SCLK) |
|
||||||
|
|
||||||
|
An alternative to the ADXL345 is the MPU-9250 (or MPU-6050). This
|
||||||
|
accelerometer has been tested to work over I2C on the RPi at 400kbaud.
|
||||||
|
Recommended connection scheme for I2C:
|
||||||
|
|
||||||
|
| MPU-9250 pin | RPi pin | RPi pin name |
|
||||||
|
|:--:|:--:|:--:|
|
||||||
|
| 3V3 (or VCC) | 01 | 3.3v DC power |
|
||||||
|
| GND | 09 | Ground |
|
||||||
|
| SDA | 03 | GPIO02 (SDA1) |
|
||||||
|
| SCL | 05 | GPIO03 (SCL1) |
|
||||||
|
|
||||||
|
|
||||||
Fritzing wiring diagrams for some of the ADXL345 boards:
|
Fritzing wiring diagrams for some of the ADXL345 boards:
|
||||||
|
|
||||||
![ADXL345-Rpi](img/adxl345-fritzing.png)
|
![ADXL345-Rpi](img/adxl345-fritzing.png)
|
||||||
|
@ -64,21 +76,21 @@ the system that may damage the electronics.
|
||||||
### Software installation
|
### Software installation
|
||||||
|
|
||||||
Note that resonance measurements and shaper auto-calibration require additional
|
Note that resonance measurements and shaper auto-calibration require additional
|
||||||
software dependencies not installed by default. First, you will have to run on
|
software dependencies not installed by default. First, run on your Raspberry Pi
|
||||||
your Raspberry Pi the following command:
|
the following commands:
|
||||||
|
```
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install python3-numpy python3-matplotlib libatlas-base-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, in order to install NumPy in the Klipper environment, run the command:
|
||||||
```
|
```
|
||||||
~/klippy-env/bin/pip install -v numpy
|
~/klippy-env/bin/pip install -v numpy
|
||||||
```
|
```
|
||||||
to install `numpy` package. Note that, depending on the performance of the
|
Note that, depending on the performance of the CPU, it may take *a lot*
|
||||||
CPU, it may take *a lot* of time, up to 10-20 minutes. Be patient and wait
|
of time, up to 10-20 minutes. Be patient and wait for the completion of
|
||||||
for the completion of the installation. On some occasions, if the board has
|
the installation. On some occasions, if the board has too little RAM
|
||||||
too little RAM, the installation may fail and you will need to enable swap.
|
the installation may fail and you will need to enable swap.
|
||||||
|
|
||||||
Next, run the following commands to install the additional dependencies:
|
|
||||||
```
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install python3-numpy python3-matplotlib
|
|
||||||
```
|
|
||||||
|
|
||||||
Afterwards, check and follow the instructions in the
|
Afterwards, check and follow the instructions in the
|
||||||
[RPi Microcontroller document](RPi_microcontroller.md) to setup the
|
[RPi Microcontroller document](RPi_microcontroller.md) to setup the
|
||||||
|
@ -87,7 +99,7 @@ Afterwards, check and follow the instructions in the
|
||||||
Make sure the Linux SPI driver is enabled by running `sudo
|
Make sure the Linux SPI driver is enabled by running `sudo
|
||||||
raspi-config` and enabling SPI under the "Interfacing options" menu.
|
raspi-config` and enabling SPI under the "Interfacing options" menu.
|
||||||
|
|
||||||
Add the following to the printer.cfg file:
|
For the ADXL345, add the following to the printer.cfg file:
|
||||||
```
|
```
|
||||||
[mcu rpi]
|
[mcu rpi]
|
||||||
serial: /tmp/klipper_host_mcu
|
serial: /tmp/klipper_host_mcu
|
||||||
|
@ -103,6 +115,23 @@ probe_points:
|
||||||
It is advised to start with 1 probe point, in the middle of the print bed,
|
It is advised to start with 1 probe point, in the middle of the print bed,
|
||||||
slightly above it.
|
slightly above it.
|
||||||
|
|
||||||
|
For the MPU-9250, make sure the Linux I2C driver is enabled and the baud rate is
|
||||||
|
set to 400000 (see [Enabling I2C](RPi_microcontroller.md#optional-enabling-i2c)
|
||||||
|
section for more details). Then, add the following to the printer.cfg:
|
||||||
|
```
|
||||||
|
[mcu rpi]
|
||||||
|
serial: /tmp/klipper_host_mcu
|
||||||
|
|
||||||
|
[mpu9250]
|
||||||
|
i2c_mcu: rpi
|
||||||
|
i2c_bus: i2c.1
|
||||||
|
|
||||||
|
[resonance_tester]
|
||||||
|
accel_chip: mpu9250
|
||||||
|
probe_points:
|
||||||
|
100, 100, 20 # an example
|
||||||
|
```
|
||||||
|
|
||||||
Restart Klipper via the `RESTART` command.
|
Restart Klipper via the `RESTART` command.
|
||||||
|
|
||||||
## Measuring the resonances
|
## Measuring the resonances
|
||||||
|
|
|
@ -54,6 +54,8 @@ communication with the Klipper developers.
|
||||||
perfectly square.
|
perfectly square.
|
||||||
- [PWM tools](Using_PWM_Tools.md): Guide on how to use PWM controlled
|
- [PWM tools](Using_PWM_Tools.md): Guide on how to use PWM controlled
|
||||||
tools such as lasers or spindles.
|
tools such as lasers or spindles.
|
||||||
|
- [Exclude Object](Exclude_Object.md): The guide to the Exclude Objecs
|
||||||
|
implementation.
|
||||||
|
|
||||||
## Developer Documentation
|
## Developer Documentation
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,15 @@ Make sure the Linux SPI driver is enabled by running
|
||||||
`sudo raspi-config` and enabling SPI under the "Interfacing options"
|
`sudo raspi-config` and enabling SPI under the "Interfacing options"
|
||||||
menu.
|
menu.
|
||||||
|
|
||||||
|
## Optional: Enabling I2C
|
||||||
|
|
||||||
|
Make sure the Linux I2C driver is enabled by running `sudo raspi-config`
|
||||||
|
and enabling I2C under the "Interfacing options" menu.
|
||||||
|
If planning to use I2C for the MPU accelerometer, it is also required
|
||||||
|
to set the baud rate to 400000 by: adding/uncommenting
|
||||||
|
`dtparam=i2c_arm=on,i2c_arm_baudrate=400000` in `/boot/config.txt`
|
||||||
|
(or `/boot/firmware/config.txt` in some distros).
|
||||||
|
|
||||||
## Optional: Identify the correct gpiochip
|
## Optional: Identify the correct gpiochip
|
||||||
|
|
||||||
On Raspberry Pi and on many clones the pins exposed on the GPIO belong
|
On Raspberry Pi and on many clones the pins exposed on the GPIO belong
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Sponsors
|
||||||
|
|
||||||
|
Klipper is Free Software. We depend on the generous support from
|
||||||
|
sponsors. Please consider sponsoring Klipper or supporting our
|
||||||
|
sponsors.
|
||||||
|
|
||||||
|
## BIGTREETECH
|
||||||
|
|
||||||
|
[<img src="./img/sponsors/BTT_BTT.png" width="200" />](https://bigtree-tech.com/collections/all-products)
|
||||||
|
|
||||||
|
BIGTREETECH is the official mainboard sponsor of Klipper. BIGTREETECH
|
||||||
|
is committed to developing innovative and competitive products to
|
||||||
|
serve the 3D printing community better. Follow them on
|
||||||
|
[Facebook](https://www.facebook.com/BIGTREETECH) or
|
||||||
|
[Twitter](https://twitter.com/BigTreeTech).
|
||||||
|
|
||||||
|
## Klipper Developers
|
||||||
|
|
||||||
|
### Kevin O'Connor
|
||||||
|
|
||||||
|
Kevin is the original author and current maintainer of Klipper. Donate
|
||||||
|
at: [https://ko-fi.com/koconnor](https://ko-fi.com/koconnor) or
|
||||||
|
[https://www.patreon.com/koconnor](https://www.patreon.com/koconnor)
|
||||||
|
|
||||||
|
### Eric Callahan
|
||||||
|
|
||||||
|
Eric is the author of bed_mesh, spi_flash, and several other Klipper
|
||||||
|
modules. Eric has a donations page at:
|
||||||
|
[https://ko-fi.com/arksine](https://ko-fi.com/arksine)
|
||||||
|
|
||||||
|
## Related Klipper Projects
|
||||||
|
|
||||||
|
Klipper is frequently used with other Free Software. Consider using or
|
||||||
|
supporting these projects.
|
||||||
|
|
||||||
|
* [Moonraker](https://github.com/Arksine/moonraker)
|
||||||
|
* [Mainsail](https://github.com/mainsail-crew/mainsail)
|
||||||
|
* [Fluidd](https://github.com/fluidd-core/fluidd)
|
||||||
|
* [OctoPrint](https://octoprint.org/)
|
||||||
|
* [KlipperScreen](https://github.com/jordanruthe/KlipperScreen)
|
|
@ -28,6 +28,17 @@ The following information is available in the
|
||||||
- `profiles`: The set of currently defined profiles as setup
|
- `profiles`: The set of currently defined profiles as setup
|
||||||
using BED_MESH_PROFILE.
|
using BED_MESH_PROFILE.
|
||||||
|
|
||||||
|
## bed_screws
|
||||||
|
|
||||||
|
The following information is available in the
|
||||||
|
`Config_Reference.md#bed_screws` object:
|
||||||
|
- `is_active`: Returns True if the bed screws adjustment tool is currently
|
||||||
|
active.
|
||||||
|
- `state`: The bed screws adjustment tool state. It is one of
|
||||||
|
the following strings: "adjust", "fine".
|
||||||
|
- `current_screw`: The index for the current screw being adjusted.
|
||||||
|
- `accepted_screws`: The number of accepted screws.
|
||||||
|
|
||||||
## configfile
|
## configfile
|
||||||
|
|
||||||
The following information is available in the `configfile` object
|
The following information is available in the `configfile` object
|
||||||
|
@ -41,6 +52,8 @@ The following information is available in the `configfile` object
|
||||||
here.) All values are returned as strings.
|
here.) All values are returned as strings.
|
||||||
- `save_config_pending`: Returns true if there are updates that a
|
- `save_config_pending`: Returns true if there are updates that a
|
||||||
`SAVE_CONFIG` command may persist to disk.
|
`SAVE_CONFIG` command may persist to disk.
|
||||||
|
- `save_config_pending_items`: Contains the sections and options that
|
||||||
|
were changed and would be persisted by a `SAVE_CONFIG`.
|
||||||
- `warnings`: A list of warnings about config options. Each entry in
|
- `warnings`: A list of warnings about config options. Each entry in
|
||||||
the list will be a dictionary containing a `type` and `message`
|
the list will be a dictionary containing a `type` and `message`
|
||||||
field (both strings). Additional fields may be available depending
|
field (both strings). Additional fields may be available depending
|
||||||
|
@ -69,6 +82,44 @@ The following information is available in the
|
||||||
forward direction minus the total number of steps taken in the
|
forward direction minus the total number of steps taken in the
|
||||||
reverse direction since the micro-controller was last restarted.
|
reverse direction since the micro-controller was last restarted.
|
||||||
|
|
||||||
|
## exclude_object
|
||||||
|
|
||||||
|
The following information is available in the
|
||||||
|
[exclude_object](Exclude_Object.md) object:
|
||||||
|
|
||||||
|
- `objects`: An array of the known objects as provided by the
|
||||||
|
`EXCLUDE_OBJECT_DEFINE` command. This is the same information provided by
|
||||||
|
the `EXCLUDE_OBJECT VERBOSE=1` command. The `center` and `polygon` fields will
|
||||||
|
only be present if provided in the original `EXCLUDE_OBJECT_DEFINE`
|
||||||
|
|
||||||
|
Here is a JSON sample:
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"polygon": [
|
||||||
|
[ 156.25, 146.2511675 ],
|
||||||
|
[ 156.25, 153.7488325 ],
|
||||||
|
[ 163.75, 153.7488325 ],
|
||||||
|
[ 163.75, 146.2511675 ]
|
||||||
|
],
|
||||||
|
"name": "CYLINDER_2_STL_ID_2_COPY_0",
|
||||||
|
"center": [ 160, 150 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"polygon": [
|
||||||
|
[ 146.25, 146.2511675 ],
|
||||||
|
[ 146.25, 153.7488325 ],
|
||||||
|
[ 153.75, 153.7488325 ],
|
||||||
|
[ 153.75, 146.2511675 ]
|
||||||
|
],
|
||||||
|
"name": "CYLINDER_2_STL_ID_1_COPY_0",
|
||||||
|
"center": [ 150, 150 ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
- `excluded_objects`: An array of strings listing the names of excluded objects.
|
||||||
|
- `current_object`: The name of the object currently being printed.
|
||||||
|
|
||||||
## fan
|
## fan
|
||||||
|
|
||||||
The following information is available in
|
The following information is available in
|
||||||
|
@ -204,6 +255,17 @@ The following information is available for each `[led led_name]`,
|
||||||
chain could be accessed at
|
chain could be accessed at
|
||||||
`printer["neopixel <config_name>"].color_data[1][2]`.
|
`printer["neopixel <config_name>"].color_data[1][2]`.
|
||||||
|
|
||||||
|
## manual_probe
|
||||||
|
|
||||||
|
The following information is available in the
|
||||||
|
`manual_probe` object:
|
||||||
|
- `is_active`: Returns True if a manual probing helper script is currently
|
||||||
|
active.
|
||||||
|
- `z_position`: The current height of the nozzle (as the printer currently
|
||||||
|
understands it).
|
||||||
|
- `z_position_lower`: Last probe attempt just lower than the current height.
|
||||||
|
- `z_position_upper`: Last probe attempt just greater than the current height.
|
||||||
|
|
||||||
## mcu
|
## mcu
|
||||||
|
|
||||||
The following information is available in
|
The following information is available in
|
||||||
|
@ -376,6 +438,8 @@ The following information is available in the `toolhead` object
|
||||||
- `axis_minimum`, `axis_maximum`: The axis travel limits (mm) after
|
- `axis_minimum`, `axis_maximum`: The axis travel limits (mm) after
|
||||||
homing. It is possible to access the x, y, z components of this
|
homing. It is possible to access the x, y, z components of this
|
||||||
limit value (eg, `axis_minimum.x`, `axis_maximum.z`).
|
limit value (eg, `axis_minimum.x`, `axis_maximum.z`).
|
||||||
|
- For Delta printers the `cone_start_z` is the max z height at
|
||||||
|
maximum radius (`printer.toolhead.cone_start_z`).
|
||||||
- `max_velocity`, `max_accel`, `max_accel_to_decel`,
|
- `max_velocity`, `max_accel`, `max_accel_to_decel`,
|
||||||
`square_corner_velocity`: The current printing limits that are in
|
`square_corner_velocity`: The current printing limits that are in
|
||||||
effect. This may differ from the config file settings if a
|
effect. This may differ from the config file settings if a
|
||||||
|
|
|
@ -27,6 +27,16 @@ while IFS="," read dirname langsite langdesc langsearch; do
|
||||||
new_docs_dir="${WORK_DIR}lang/${langsite}/docs/"
|
new_docs_dir="${WORK_DIR}lang/${langsite}/docs/"
|
||||||
locale_dir="${TRANS_DIR}/docs/locales/${dirname}"
|
locale_dir="${TRANS_DIR}/docs/locales/${dirname}"
|
||||||
|
|
||||||
|
# read toc
|
||||||
|
title=$(sed -n '1p' ${locale_dir}/Navigation.md)
|
||||||
|
installation_and_configuration=$(sed -n '3p' ${locale_dir}/Navigation.md)
|
||||||
|
configuration_reference=$(sed -n '5p' ${locale_dir}/Navigation.md)
|
||||||
|
bed_level=$(sed -n '7p' ${locale_dir}/Navigation.md)
|
||||||
|
resonance_compensation=$(sed -n '9p' ${locale_dir}/Navigation.md)
|
||||||
|
command_template=$(sed -n '11p' ${locale_dir}/Navigation.md)
|
||||||
|
developer_documentation=$(sed -n '13p' ${locale_dir}/Navigation.md)
|
||||||
|
device_specific_documents=$(sed -n '15p' ${locale_dir}/Navigation.md)
|
||||||
|
|
||||||
# Copy markdown files to new_docs_dir
|
# Copy markdown files to new_docs_dir
|
||||||
echo "Copying $dirname to $langsite"
|
echo "Copying $dirname to $langsite"
|
||||||
mkdir -p "${new_docs_dir}"
|
mkdir -p "${new_docs_dir}"
|
||||||
|
@ -56,6 +66,16 @@ while IFS="," read dirname langsite langdesc langsearch; do
|
||||||
echo "replace site language"
|
echo "replace site language"
|
||||||
sed -i "s%^ language: en$% language: ${langsite}%" "${new_mkdocs_file}"
|
sed -i "s%^ language: en$% language: ${langsite}%" "${new_mkdocs_file}"
|
||||||
|
|
||||||
|
echo "replace toc"
|
||||||
|
sed -i "s%Klipper documentation$%${title}%" "${new_mkdocs_file}"
|
||||||
|
sed -i "s%Installation and Configuration:$%${installation_and_configuration}:%" "${new_mkdocs_file}"
|
||||||
|
sed -i "s%Configuration Reference:$%${configuration_reference}:%" "${new_mkdocs_file}"
|
||||||
|
sed -i "s%Bed Level:$%${bed_level}:%" "${new_mkdocs_file}"
|
||||||
|
sed -i "s%Resonance Compensation:$%${resonance_compensation}:%" "${new_mkdocs_file}"
|
||||||
|
sed -i "s%Command templates:$%${command_template}:%" "${new_mkdocs_file}"
|
||||||
|
sed -i "s%Developer Documentation:$%${developer_documentation}:%" "${new_mkdocs_file}"
|
||||||
|
sed -i "s%Device Specific Documents:$%${device_specific_documents}:%" "${new_mkdocs_file}"
|
||||||
|
|
||||||
# Build site
|
# Build site
|
||||||
echo "building site for ${langsite}"
|
echo "building site for ${langsite}"
|
||||||
mkdir -p "${PWD}/site/${langsite}/"
|
mkdir -p "${PWD}/site/${langsite}/"
|
||||||
|
|
|
@ -7,3 +7,4 @@ mkdocs-exclude==1.0.2
|
||||||
mdx-truly-sane-lists==1.2
|
mdx-truly-sane-lists==1.2
|
||||||
mdx-breakless-lists==1.0.1
|
mdx-breakless-lists==1.0.1
|
||||||
py-gfm==1.0.2
|
py-gfm==1.0.2
|
||||||
|
markdown==3.3.7
|
||||||
|
|
|
@ -113,6 +113,7 @@ nav:
|
||||||
- Multi_MCU_Homing.md
|
- Multi_MCU_Homing.md
|
||||||
- Slicers.md
|
- Slicers.md
|
||||||
- Skew_Correction.md
|
- Skew_Correction.md
|
||||||
|
- Exclude_Object.md
|
||||||
- Using_PWM_Tools.md
|
- Using_PWM_Tools.md
|
||||||
- Developer Documentation:
|
- Developer Documentation:
|
||||||
- Code_Overview.md
|
- Code_Overview.md
|
||||||
|
@ -134,3 +135,4 @@ nav:
|
||||||
- CANBUS.md
|
- CANBUS.md
|
||||||
- TSL1401CL_Filament_Width_Sensor.md
|
- TSL1401CL_Filament_Width_Sensor.md
|
||||||
- Hall_Filament_Width_Sensor.md
|
- Hall_Filament_Width_Sensor.md
|
||||||
|
- Sponsors.md
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
|
@ -15,3 +15,4 @@ To begin using Klipper start by [installing](Installation.md) it.
|
||||||
|
|
||||||
Klipper is Free Software. Read the [documentation](Overview.md) or
|
Klipper is Free Software. Read the [documentation](Overview.md) or
|
||||||
view [the Klipper code on github](https://github.com/Klipper3d/klipper).
|
view [the Klipper code on github](https://github.com/Klipper3d/klipper).
|
||||||
|
We depend on the generous support from our [sponsors](Sponsors.md).
|
||||||
|
|
|
@ -168,8 +168,8 @@ defs_serialqueue = """
|
||||||
, uint64_t notify_id);
|
, uint64_t notify_id);
|
||||||
void serialqueue_pull(struct serialqueue *sq
|
void serialqueue_pull(struct serialqueue *sq
|
||||||
, struct pull_queue_message *pqm);
|
, struct pull_queue_message *pqm);
|
||||||
void serialqueue_set_baud_adjust(struct serialqueue *sq
|
void serialqueue_set_wire_frequency(struct serialqueue *sq
|
||||||
, double baud_adjust);
|
, double frequency);
|
||||||
void serialqueue_set_receive_window(struct serialqueue *sq
|
void serialqueue_set_receive_window(struct serialqueue *sq
|
||||||
, int receive_window);
|
, int receive_window);
|
||||||
void serialqueue_set_clock_est(struct serialqueue *sq, double est_freq
|
void serialqueue_set_clock_est(struct serialqueue *sq, double est_freq
|
||||||
|
|
|
@ -49,7 +49,7 @@ struct serialqueue {
|
||||||
int receive_waiting;
|
int receive_waiting;
|
||||||
// Baud / clock tracking
|
// Baud / clock tracking
|
||||||
int receive_window;
|
int receive_window;
|
||||||
double baud_adjust, idle_time;
|
double bittime_adjust, idle_time;
|
||||||
struct clock_estimate ce;
|
struct clock_estimate ce;
|
||||||
double last_receive_sent_time;
|
double last_receive_sent_time;
|
||||||
// Retransmit support
|
// Retransmit support
|
||||||
|
@ -136,6 +136,23 @@ kick_bg_thread(struct serialqueue *sq)
|
||||||
report_errno("pipe write", ret);
|
report_errno("pipe write", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Minimum number of bits in a canbus message
|
||||||
|
#define CANBUS_PACKET_BITS ((1 + 11 + 3 + 4) + (16 + 2 + 7 + 3))
|
||||||
|
#define CANBUS_IFS_BITS 4
|
||||||
|
|
||||||
|
// Determine minimum time needed to transmit a given number of bytes
|
||||||
|
static double
|
||||||
|
calculate_bittime(struct serialqueue *sq, uint32_t bytes)
|
||||||
|
{
|
||||||
|
if (sq->serial_fd_type == SQT_CAN) {
|
||||||
|
uint32_t pkts = DIV_ROUND_UP(bytes, 8);
|
||||||
|
uint32_t bits = bytes * 8 + pkts * CANBUS_PACKET_BITS - CANBUS_IFS_BITS;
|
||||||
|
return sq->bittime_adjust * bits;
|
||||||
|
} else {
|
||||||
|
return sq->bittime_adjust * bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update internal state when the receive sequence increases
|
// Update internal state when the receive sequence increases
|
||||||
static void
|
static void
|
||||||
update_receive_seq(struct serialqueue *sq, double eventtime, uint64_t rseq)
|
update_receive_seq(struct serialqueue *sq, double eventtime, uint64_t rseq)
|
||||||
|
@ -192,7 +209,7 @@ update_receive_seq(struct serialqueue *sq, double eventtime, uint64_t rseq)
|
||||||
} else {
|
} else {
|
||||||
struct queue_message *sent = list_first_entry(
|
struct queue_message *sent = list_first_entry(
|
||||||
&sq->sent_queue, struct queue_message, node);
|
&sq->sent_queue, struct queue_message, node);
|
||||||
double nr = eventtime + sq->rto + sent->len * sq->baud_adjust;
|
double nr = eventtime + sq->rto + calculate_bittime(sq, sent->len);
|
||||||
pollreactor_update_timer(sq->pr, SQPT_RETRANSMIT, nr);
|
pollreactor_update_timer(sq->pr, SQPT_RETRANSMIT, nr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,7 +268,7 @@ handle_message(struct serialqueue *sq, double eventtime, int len)
|
||||||
qm->sent_time = (rseq > sq->retransmit_seq
|
qm->sent_time = (rseq > sq->retransmit_seq
|
||||||
? sq->last_receive_sent_time : 0.);
|
? sq->last_receive_sent_time : 0.);
|
||||||
qm->receive_time = get_monotonic(); // must be time post read()
|
qm->receive_time = get_monotonic(); // must be time post read()
|
||||||
qm->receive_time -= sq->baud_adjust * len;
|
qm->receive_time -= calculate_bittime(sq, len);
|
||||||
list_add_tail(&qm->node, &sq->receive_queue);
|
list_add_tail(&qm->node, &sq->receive_queue);
|
||||||
must_wake = 1;
|
must_wake = 1;
|
||||||
}
|
}
|
||||||
|
@ -407,8 +424,8 @@ retransmit_event(struct serialqueue *sq, double eventtime)
|
||||||
}
|
}
|
||||||
sq->retransmit_seq = sq->send_seq;
|
sq->retransmit_seq = sq->send_seq;
|
||||||
sq->rtt_sample_seq = 0;
|
sq->rtt_sample_seq = 0;
|
||||||
sq->idle_time = eventtime + buflen * sq->baud_adjust;
|
sq->idle_time = eventtime + calculate_bittime(sq, buflen);
|
||||||
double waketime = eventtime + first_buflen * sq->baud_adjust + sq->rto;
|
double waketime = eventtime + sq->rto + calculate_bittime(sq, first_buflen);
|
||||||
|
|
||||||
pthread_mutex_unlock(&sq->lock);
|
pthread_mutex_unlock(&sq->lock);
|
||||||
return waketime;
|
return waketime;
|
||||||
|
@ -416,7 +433,8 @@ retransmit_event(struct serialqueue *sq, double eventtime)
|
||||||
|
|
||||||
// Construct a block of data to be sent to the serial port
|
// Construct a block of data to be sent to the serial port
|
||||||
static int
|
static int
|
||||||
build_and_send_command(struct serialqueue *sq, uint8_t *buf, double eventtime)
|
build_and_send_command(struct serialqueue *sq, uint8_t *buf, int pending
|
||||||
|
, double eventtime)
|
||||||
{
|
{
|
||||||
int len = MESSAGE_HEADER_SIZE;
|
int len = MESSAGE_HEADER_SIZE;
|
||||||
while (sq->ready_bytes) {
|
while (sq->ready_bytes) {
|
||||||
|
@ -463,17 +481,15 @@ build_and_send_command(struct serialqueue *sq, uint8_t *buf, double eventtime)
|
||||||
buf[len - MESSAGE_TRAILER_SYNC] = MESSAGE_SYNC;
|
buf[len - MESSAGE_TRAILER_SYNC] = MESSAGE_SYNC;
|
||||||
|
|
||||||
// Store message block
|
// Store message block
|
||||||
if (eventtime > sq->idle_time)
|
double idletime = eventtime > sq->idle_time ? eventtime : sq->idle_time;
|
||||||
sq->idle_time = eventtime;
|
idletime += calculate_bittime(sq, pending + len);
|
||||||
sq->idle_time += len * sq->baud_adjust;
|
|
||||||
struct queue_message *out = message_alloc();
|
struct queue_message *out = message_alloc();
|
||||||
memcpy(out->msg, buf, len);
|
memcpy(out->msg, buf, len);
|
||||||
out->len = len;
|
out->len = len;
|
||||||
out->sent_time = eventtime;
|
out->sent_time = eventtime;
|
||||||
out->receive_time = sq->idle_time;
|
out->receive_time = idletime;
|
||||||
if (list_empty(&sq->sent_queue))
|
if (list_empty(&sq->sent_queue))
|
||||||
pollreactor_update_timer(sq->pr, SQPT_RETRANSMIT
|
pollreactor_update_timer(sq->pr, SQPT_RETRANSMIT, idletime + sq->rto);
|
||||||
, sq->idle_time + sq->rto);
|
|
||||||
if (!sq->rtt_sample_seq)
|
if (!sq->rtt_sample_seq)
|
||||||
sq->rtt_sample_seq = sq->send_seq;
|
sq->rtt_sample_seq = sq->send_seq;
|
||||||
sq->send_seq++;
|
sq->send_seq++;
|
||||||
|
@ -484,7 +500,7 @@ build_and_send_command(struct serialqueue *sq, uint8_t *buf, double eventtime)
|
||||||
|
|
||||||
// Determine the time the next serial data should be sent
|
// Determine the time the next serial data should be sent
|
||||||
static double
|
static double
|
||||||
check_send_command(struct serialqueue *sq, double eventtime)
|
check_send_command(struct serialqueue *sq, int pending, double eventtime)
|
||||||
{
|
{
|
||||||
if (sq->send_seq - sq->receive_seq >= MAX_PENDING_BLOCKS
|
if (sq->send_seq - sq->receive_seq >= MAX_PENDING_BLOCKS
|
||||||
&& sq->receive_seq != (uint64_t)-1)
|
&& sq->receive_seq != (uint64_t)-1)
|
||||||
|
@ -501,7 +517,7 @@ check_send_command(struct serialqueue *sq, double eventtime)
|
||||||
|
|
||||||
// Check for stalled messages now ready
|
// Check for stalled messages now ready
|
||||||
double idletime = eventtime > sq->idle_time ? eventtime : sq->idle_time;
|
double idletime = eventtime > sq->idle_time ? eventtime : sq->idle_time;
|
||||||
idletime += MESSAGE_MIN * sq->baud_adjust;
|
idletime += calculate_bittime(sq, pending + MESSAGE_MIN);
|
||||||
uint64_t ack_clock = clock_from_time(&sq->ce, idletime);
|
uint64_t ack_clock = clock_from_time(&sq->ce, idletime);
|
||||||
uint64_t min_stalled_clock = MAX_CLOCK, min_ready_clock = MAX_CLOCK;
|
uint64_t min_stalled_clock = MAX_CLOCK, min_ready_clock = MAX_CLOCK;
|
||||||
struct command_queue *cq;
|
struct command_queue *cq;
|
||||||
|
@ -525,9 +541,10 @@ check_send_command(struct serialqueue *sq, double eventtime)
|
||||||
struct queue_message *qm = list_first_entry(
|
struct queue_message *qm = list_first_entry(
|
||||||
&cq->ready_queue, struct queue_message, node);
|
&cq->ready_queue, struct queue_message, node);
|
||||||
uint64_t req_clock = qm->req_clock;
|
uint64_t req_clock = qm->req_clock;
|
||||||
|
double bgtime = pending ? idletime : sq->idle_time;
|
||||||
double bgoffset = MIN_REQTIME_DELTA + MIN_BACKGROUND_DELTA;
|
double bgoffset = MIN_REQTIME_DELTA + MIN_BACKGROUND_DELTA;
|
||||||
if (req_clock == BACKGROUND_PRIORITY_CLOCK)
|
if (req_clock == BACKGROUND_PRIORITY_CLOCK)
|
||||||
req_clock = clock_from_time(&sq->ce, sq->idle_time + bgoffset);
|
req_clock = clock_from_time(&sq->ce, bgtime + bgoffset);
|
||||||
if (req_clock < min_ready_clock)
|
if (req_clock < min_ready_clock)
|
||||||
min_ready_clock = req_clock;
|
min_ready_clock = req_clock;
|
||||||
}
|
}
|
||||||
|
@ -561,18 +578,21 @@ command_event(struct serialqueue *sq, double eventtime)
|
||||||
int buflen = 0;
|
int buflen = 0;
|
||||||
double waketime;
|
double waketime;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
waketime = check_send_command(sq, eventtime);
|
waketime = check_send_command(sq, buflen, eventtime);
|
||||||
if (waketime != PR_NOW || buflen + MESSAGE_MAX > sizeof(buf)) {
|
if (waketime != PR_NOW || buflen + MESSAGE_MAX > sizeof(buf)) {
|
||||||
if (buflen) {
|
if (buflen) {
|
||||||
// Write message blocks
|
// Write message blocks
|
||||||
do_write(sq, buf, buflen);
|
do_write(sq, buf, buflen);
|
||||||
sq->bytes_write += buflen;
|
sq->bytes_write += buflen;
|
||||||
|
double idletime = (eventtime > sq->idle_time
|
||||||
|
? eventtime : sq->idle_time);
|
||||||
|
sq->idle_time = idletime + calculate_bittime(sq, buflen);
|
||||||
buflen = 0;
|
buflen = 0;
|
||||||
}
|
}
|
||||||
if (waketime != PR_NOW)
|
if (waketime != PR_NOW)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
buflen += build_and_send_command(sq, &buf[buflen], eventtime);
|
buflen += build_and_send_command(sq, &buf[buflen], buflen, eventtime);
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&sq->lock);
|
pthread_mutex_unlock(&sq->lock);
|
||||||
return waketime;
|
return waketime;
|
||||||
|
@ -847,10 +867,15 @@ exit:
|
||||||
}
|
}
|
||||||
|
|
||||||
void __visible
|
void __visible
|
||||||
serialqueue_set_baud_adjust(struct serialqueue *sq, double baud_adjust)
|
serialqueue_set_wire_frequency(struct serialqueue *sq, double frequency)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&sq->lock);
|
pthread_mutex_lock(&sq->lock);
|
||||||
sq->baud_adjust = baud_adjust;
|
if (sq->serial_fd_type == SQT_CAN) {
|
||||||
|
sq->bittime_adjust = 1. / frequency;
|
||||||
|
} else {
|
||||||
|
// An 8N1 serial line is 10 bits per byte (1 start, 8 data, 1 stop)
|
||||||
|
sq->bittime_adjust = 10. / frequency;
|
||||||
|
}
|
||||||
pthread_mutex_unlock(&sq->lock);
|
pthread_mutex_unlock(&sq->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ void serialqueue_send(struct serialqueue *sq, struct command_queue *cq
|
||||||
, uint8_t *msg, int len, uint64_t min_clock
|
, uint8_t *msg, int len, uint64_t min_clock
|
||||||
, uint64_t req_clock, uint64_t notify_id);
|
, uint64_t req_clock, uint64_t notify_id);
|
||||||
void serialqueue_pull(struct serialqueue *sq, struct pull_queue_message *pqm);
|
void serialqueue_pull(struct serialqueue *sq, struct pull_queue_message *pqm);
|
||||||
void serialqueue_set_baud_adjust(struct serialqueue *sq, double baud_adjust);
|
void serialqueue_set_wire_frequency(struct serialqueue *sq, double frequency);
|
||||||
void serialqueue_set_receive_window(struct serialqueue *sq, int receive_window);
|
void serialqueue_set_receive_window(struct serialqueue *sq, int receive_window);
|
||||||
void serialqueue_set_clock_est(struct serialqueue *sq, double est_freq
|
void serialqueue_set_clock_est(struct serialqueue *sq, double est_freq
|
||||||
, double conv_time, uint64_t conv_clock
|
, double conv_time, uint64_t conv_clock
|
||||||
|
|
|
@ -140,6 +140,7 @@ class PrinterConfig:
|
||||||
self.autosave = None
|
self.autosave = None
|
||||||
self.deprecated = {}
|
self.deprecated = {}
|
||||||
self.status_raw_config = {}
|
self.status_raw_config = {}
|
||||||
|
self.status_save_pending = {}
|
||||||
self.status_settings = {}
|
self.status_settings = {}
|
||||||
self.status_warnings = []
|
self.status_warnings = []
|
||||||
self.save_config_pending = False
|
self.save_config_pending = False
|
||||||
|
@ -331,18 +332,36 @@ class PrinterConfig:
|
||||||
return {'config': self.status_raw_config,
|
return {'config': self.status_raw_config,
|
||||||
'settings': self.status_settings,
|
'settings': self.status_settings,
|
||||||
'warnings': self.status_warnings,
|
'warnings': self.status_warnings,
|
||||||
'save_config_pending': self.save_config_pending}
|
'save_config_pending': self.save_config_pending,
|
||||||
|
'save_config_pending_items': self.status_save_pending}
|
||||||
# Autosave functions
|
# Autosave functions
|
||||||
def set(self, section, option, value):
|
def set(self, section, option, value):
|
||||||
if not self.autosave.fileconfig.has_section(section):
|
if not self.autosave.fileconfig.has_section(section):
|
||||||
self.autosave.fileconfig.add_section(section)
|
self.autosave.fileconfig.add_section(section)
|
||||||
svalue = str(value)
|
svalue = str(value)
|
||||||
self.autosave.fileconfig.set(section, option, svalue)
|
self.autosave.fileconfig.set(section, option, svalue)
|
||||||
|
pending = dict(self.status_save_pending)
|
||||||
|
if not section in pending or pending[section] is None:
|
||||||
|
pending[section] = {}
|
||||||
|
else:
|
||||||
|
pending[section] = dict(pending[section])
|
||||||
|
pending[section][option] = svalue
|
||||||
|
self.status_save_pending = pending
|
||||||
self.save_config_pending = True
|
self.save_config_pending = True
|
||||||
logging.info("save_config: set [%s] %s = %s", section, option, svalue)
|
logging.info("save_config: set [%s] %s = %s", section, option, svalue)
|
||||||
def remove_section(self, section):
|
def remove_section(self, section):
|
||||||
self.autosave.fileconfig.remove_section(section)
|
if self.autosave.fileconfig.has_section(section):
|
||||||
self.save_config_pending = True
|
self.autosave.fileconfig.remove_section(section)
|
||||||
|
pending = dict(self.status_save_pending)
|
||||||
|
pending[section] = None
|
||||||
|
self.status_save_pending = pending
|
||||||
|
self.save_config_pending = True
|
||||||
|
elif (section in self.status_save_pending and
|
||||||
|
self.status_save_pending[section] is not None):
|
||||||
|
pending = dict(self.status_save_pending)
|
||||||
|
del pending[section]
|
||||||
|
self.status_save_pending = pending
|
||||||
|
self.save_config_pending = True
|
||||||
def _disallow_include_conflicts(self, regular_data, cfgname, gcode):
|
def _disallow_include_conflicts(self, regular_data, cfgname, gcode):
|
||||||
config = self._build_config_wrapper(regular_data, cfgname)
|
config = self._build_config_wrapper(regular_data, cfgname)
|
||||||
for section in self.autosave.fileconfig.sections():
|
for section in self.autosave.fileconfig.sections():
|
||||||
|
|
|
@ -11,11 +11,12 @@ help_txt = """
|
||||||
This is a debugging console for the Klipper micro-controller.
|
This is a debugging console for the Klipper micro-controller.
|
||||||
In addition to mcu commands, the following artificial commands are
|
In addition to mcu commands, the following artificial commands are
|
||||||
available:
|
available:
|
||||||
PINS : Load pin name aliases (eg, "PINS arduino")
|
|
||||||
DELAY : Send a command at a clock time (eg, "DELAY 9999 get_uptime")
|
DELAY : Send a command at a clock time (eg, "DELAY 9999 get_uptime")
|
||||||
FLOOD : Send a command many times (eg, "FLOOD 22 .01 get_uptime")
|
FLOOD : Send a command many times (eg, "FLOOD 22 .01 get_uptime")
|
||||||
SUPPRESS : Suppress a response message (eg, "SUPPRESS analog_in_state 4")
|
SUPPRESS : Suppress a response message (eg, "SUPPRESS analog_in_state 4")
|
||||||
SET : Create a local variable (eg, "SET myvar 123.4")
|
SET : Create a local variable (eg, "SET myvar 123.4")
|
||||||
|
DUMP : Dump memory (eg, "DUMP 0x12345678 100 32")
|
||||||
|
FILEDUMP : Dump to file (eg, "FILEDUMP data.bin 0x12345678 100 32")
|
||||||
STATS : Report serial statistics
|
STATS : Report serial statistics
|
||||||
LIST : List available mcu commands, local commands, and local variables
|
LIST : List available mcu commands, local commands, and local variables
|
||||||
HELP : Show this text
|
HELP : Show this text
|
||||||
|
@ -48,6 +49,7 @@ class KeyboardReader:
|
||||||
reactor.register_callback(self.connect)
|
reactor.register_callback(self.connect)
|
||||||
self.local_commands = {
|
self.local_commands = {
|
||||||
"SET": self.command_SET,
|
"SET": self.command_SET,
|
||||||
|
"DUMP": self.command_DUMP, "FILEDUMP": self.command_FILEDUMP,
|
||||||
"DELAY": self.command_DELAY, "FLOOD": self.command_FLOOD,
|
"DELAY": self.command_DELAY, "FLOOD": self.command_FLOOD,
|
||||||
"SUPPRESS": self.command_SUPPRESS, "STATS": self.command_STATS,
|
"SUPPRESS": self.command_SUPPRESS, "STATS": self.command_STATS,
|
||||||
"LIST": self.command_LIST, "HELP": self.command_HELP,
|
"LIST": self.command_LIST, "HELP": self.command_HELP,
|
||||||
|
@ -98,6 +100,55 @@ class KeyboardReader:
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
self.eval_globals[parts[1]] = val
|
self.eval_globals[parts[1]] = val
|
||||||
|
def command_DUMP(self, parts, filename=None):
|
||||||
|
# Extract command args
|
||||||
|
try:
|
||||||
|
addr = int(parts[1], 0)
|
||||||
|
count = int(parts[2], 0)
|
||||||
|
order = [2, 0, 1, 0][(addr | count) & 3]
|
||||||
|
if len(parts) > 3:
|
||||||
|
order = {'32': 2, '16': 1, '8': 0}[parts[3]]
|
||||||
|
except ValueError as e:
|
||||||
|
self.output("Error: %s" % (str(e),))
|
||||||
|
return
|
||||||
|
bsize = 1 << order
|
||||||
|
# Query data from mcu
|
||||||
|
vals = []
|
||||||
|
for i in range((count + bsize - 1) >> order):
|
||||||
|
caddr = addr + (i << order)
|
||||||
|
cmd = "debug_read order=%d addr=%d" % (order, caddr)
|
||||||
|
params = self.ser.send_with_response(cmd, "debug_result")
|
||||||
|
vals.append(params['val'])
|
||||||
|
# Report data
|
||||||
|
if filename is None and order == 2:
|
||||||
|
# Common 32bit hex dump
|
||||||
|
for i in range((len(vals) + 3) // 4):
|
||||||
|
p = i * 4
|
||||||
|
hexvals = " ".join(["%08x" % (v,) for v in vals[p:p+4]])
|
||||||
|
self.output("%08x %s" % (addr + p * 4, hexvals))
|
||||||
|
return
|
||||||
|
# Convert to byte format
|
||||||
|
data = bytearray()
|
||||||
|
for val in vals:
|
||||||
|
for b in range(bsize):
|
||||||
|
data.append((val >> (8 * b)) & 0xff)
|
||||||
|
data = data[:count]
|
||||||
|
if filename is not None:
|
||||||
|
f = open(filename, 'wb')
|
||||||
|
f.write(data)
|
||||||
|
f.close()
|
||||||
|
self.output("Wrote %d bytes to '%s'" % (len(data), filename))
|
||||||
|
return
|
||||||
|
for i in range((count + 15) // 16):
|
||||||
|
p = i * 16
|
||||||
|
paddr = addr + p
|
||||||
|
d = data[p:p+16]
|
||||||
|
hexbytes = " ".join(["%02x" % (v,) for v in d])
|
||||||
|
pb = "".join([chr(v) if v >= 0x20 and v < 0x7f else '.' for v in d])
|
||||||
|
o = "%08x %-47s |%s|" % (paddr, hexbytes, pb)
|
||||||
|
self.output("%s %s" % (o[:34], o[34:]))
|
||||||
|
def command_FILEDUMP(self, parts):
|
||||||
|
self.command_DUMP(parts[1:], filename=parts[1])
|
||||||
def command_DELAY(self, parts):
|
def command_DELAY(self, parts):
|
||||||
try:
|
try:
|
||||||
val = int(parts[1])
|
val = int(parts[1])
|
||||||
|
|
|
@ -30,7 +30,7 @@ Accel_Measurement = collections.namedtuple(
|
||||||
'Accel_Measurement', ('time', 'accel_x', 'accel_y', 'accel_z'))
|
'Accel_Measurement', ('time', 'accel_x', 'accel_y', 'accel_z'))
|
||||||
|
|
||||||
# Helper class to obtain measurements
|
# Helper class to obtain measurements
|
||||||
class ADXL345QueryHelper:
|
class AccelQueryHelper:
|
||||||
def __init__(self, printer, cconn):
|
def __init__(self, printer, cconn):
|
||||||
self.printer = printer
|
self.printer = printer
|
||||||
self.cconn = cconn
|
self.cconn = cconn
|
||||||
|
@ -101,15 +101,18 @@ class ADXL345QueryHelper:
|
||||||
write_proc.start()
|
write_proc.start()
|
||||||
|
|
||||||
# Helper class for G-Code commands
|
# Helper class for G-Code commands
|
||||||
class ADXLCommandHelper:
|
class AccelCommandHelper:
|
||||||
def __init__(self, config, chip):
|
def __init__(self, config, chip):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
self.chip = chip
|
self.chip = chip
|
||||||
self.bg_client = None
|
self.bg_client = None
|
||||||
self.name = config.get_name().split()[-1]
|
name_parts = config.get_name().split()
|
||||||
|
self.base_name = name_parts[0]
|
||||||
|
self.name = name_parts[-1]
|
||||||
self.register_commands(self.name)
|
self.register_commands(self.name)
|
||||||
if self.name == "adxl345":
|
if len(name_parts) == 1:
|
||||||
self.register_commands(None)
|
if self.name == "adxl345" or not config.has_section("adxl345"):
|
||||||
|
self.register_commands(None)
|
||||||
def register_commands(self, name):
|
def register_commands(self, name):
|
||||||
# Register commands
|
# Register commands
|
||||||
gcode = self.printer.lookup_object('gcode')
|
gcode = self.printer.lookup_object('gcode')
|
||||||
|
@ -130,20 +133,20 @@ class ADXLCommandHelper:
|
||||||
if self.bg_client is None:
|
if self.bg_client is None:
|
||||||
# Start measurements
|
# Start measurements
|
||||||
self.bg_client = self.chip.start_internal_client()
|
self.bg_client = self.chip.start_internal_client()
|
||||||
gcmd.respond_info("adxl345 measurements started")
|
gcmd.respond_info("accelerometer measurements started")
|
||||||
return
|
return
|
||||||
# End measurements
|
# End measurements
|
||||||
name = gcmd.get("NAME", time.strftime("%Y%m%d_%H%M%S"))
|
name = gcmd.get("NAME", time.strftime("%Y%m%d_%H%M%S"))
|
||||||
if not name.replace('-', '').replace('_', '').isalnum():
|
if not name.replace('-', '').replace('_', '').isalnum():
|
||||||
raise gcmd.error("Invalid adxl345 NAME parameter")
|
raise gcmd.error("Invalid NAME parameter")
|
||||||
bg_client = self.bg_client
|
bg_client = self.bg_client
|
||||||
self.bg_client = None
|
self.bg_client = None
|
||||||
bg_client.finish_measurements()
|
bg_client.finish_measurements()
|
||||||
# Write data to file
|
# Write data to file
|
||||||
if self.name == "adxl345":
|
if self.base_name == self.name:
|
||||||
filename = "/tmp/adxl345-%s.csv" % (name,)
|
filename = "/tmp/%s-%s.csv" % (self.base_name, name)
|
||||||
else:
|
else:
|
||||||
filename = "/tmp/adxl345-%s-%s.csv" % (self.name, name,)
|
filename = "/tmp/%s-%s-%s.csv" % (self.base_name, self.name, name)
|
||||||
bg_client.write_to_file(filename)
|
bg_client.write_to_file(filename)
|
||||||
gcmd.respond_info("Writing raw accelerometer data to %s file"
|
gcmd.respond_info("Writing raw accelerometer data to %s file"
|
||||||
% (filename,))
|
% (filename,))
|
||||||
|
@ -154,18 +157,18 @@ class ADXLCommandHelper:
|
||||||
aclient.finish_measurements()
|
aclient.finish_measurements()
|
||||||
values = aclient.get_samples()
|
values = aclient.get_samples()
|
||||||
if not values:
|
if not values:
|
||||||
raise gcmd.error("No adxl345 measurements found")
|
raise gcmd.error("No accelerometer measurements found")
|
||||||
_, accel_x, accel_y, accel_z = values[-1]
|
_, accel_x, accel_y, accel_z = values[-1]
|
||||||
gcmd.respond_info("adxl345 values (x, y, z): %.6f, %.6f, %.6f"
|
gcmd.respond_info("accelerometer values (x, y, z): %.6f, %.6f, %.6f"
|
||||||
% (accel_x, accel_y, accel_z))
|
% (accel_x, accel_y, accel_z))
|
||||||
cmd_ACCELEROMETER_DEBUG_READ_help = "Query adxl345 register (for debugging)"
|
cmd_ACCELEROMETER_DEBUG_READ_help = "Query register (for debugging)"
|
||||||
def cmd_ACCELEROMETER_DEBUG_READ(self, gcmd):
|
def cmd_ACCELEROMETER_DEBUG_READ(self, gcmd):
|
||||||
reg = gcmd.get("REG", minval=29, maxval=57, parser=lambda x: int(x, 0))
|
reg = gcmd.get("REG", minval=0, maxval=126, parser=lambda x: int(x, 0))
|
||||||
val = self.chip.read_reg(reg)
|
val = self.chip.read_reg(reg)
|
||||||
gcmd.respond_info("ADXL345 REG[0x%x] = 0x%x" % (reg, val))
|
gcmd.respond_info("Accelerometer REG[0x%x] = 0x%x" % (reg, val))
|
||||||
cmd_ACCELEROMETER_DEBUG_WRITE_help = "Set adxl345 register (for debugging)"
|
cmd_ACCELEROMETER_DEBUG_WRITE_help = "Set register (for debugging)"
|
||||||
def cmd_ACCELEROMETER_DEBUG_WRITE(self, gcmd):
|
def cmd_ACCELEROMETER_DEBUG_WRITE(self, gcmd):
|
||||||
reg = gcmd.get("REG", minval=29, maxval=57, parser=lambda x: int(x, 0))
|
reg = gcmd.get("REG", minval=0, maxval=126, parser=lambda x: int(x, 0))
|
||||||
val = gcmd.get("VAL", minval=0, maxval=255, parser=lambda x: int(x, 0))
|
val = gcmd.get("VAL", minval=0, maxval=255, parser=lambda x: int(x, 0))
|
||||||
self.chip.set_reg(reg, val)
|
self.chip.set_reg(reg, val)
|
||||||
|
|
||||||
|
@ -226,7 +229,7 @@ SAMPLES_PER_BLOCK = 10
|
||||||
class ADXL345:
|
class ADXL345:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
ADXLCommandHelper(config, self)
|
AccelCommandHelper(config, self)
|
||||||
self.query_rate = 0
|
self.query_rate = 0
|
||||||
am = {'x': (0, SCALE), 'y': (1, SCALE), 'z': (2, SCALE),
|
am = {'x': (0, SCALE), 'y': (1, SCALE), 'z': (2, SCALE),
|
||||||
'-x': (0, -SCALE), '-y': (1, -SCALE), '-z': (2, -SCALE)}
|
'-x': (0, -SCALE), '-y': (1, -SCALE), '-z': (2, -SCALE)}
|
||||||
|
@ -432,7 +435,7 @@ class ADXL345:
|
||||||
web_request.send({'header': hdr})
|
web_request.send({'header': hdr})
|
||||||
def start_internal_client(self):
|
def start_internal_client(self):
|
||||||
cconn = self.api_dump.add_internal_client()
|
cconn = self.api_dump.add_internal_client()
|
||||||
return ADXL345QueryHelper(self.printer, cconn)
|
return AccelQueryHelper(self.printer, cconn)
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
return ADXL345(config)
|
return ADXL345(config)
|
||||||
|
|
|
@ -124,6 +124,8 @@ class BedMesh:
|
||||||
# Register transform
|
# Register transform
|
||||||
gcode_move = self.printer.load_object(config, 'gcode_move')
|
gcode_move = self.printer.load_object(config, 'gcode_move')
|
||||||
gcode_move.set_move_transform(self)
|
gcode_move.set_move_transform(self)
|
||||||
|
# initialize status dict
|
||||||
|
self.update_status()
|
||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
self.toolhead = self.printer.lookup_object('toolhead')
|
self.toolhead = self.printer.lookup_object('toolhead')
|
||||||
self.bmc.print_generated_points(logging.info)
|
self.bmc.print_generated_points(logging.info)
|
||||||
|
@ -162,6 +164,7 @@ class BedMesh:
|
||||||
# cache the current position before a transform takes place
|
# cache the current position before a transform takes place
|
||||||
gcode_move = self.printer.lookup_object('gcode_move')
|
gcode_move = self.printer.lookup_object('gcode_move')
|
||||||
gcode_move.reset_last_position()
|
gcode_move.reset_last_position()
|
||||||
|
self.update_status()
|
||||||
def get_z_factor(self, z_pos):
|
def get_z_factor(self, z_pos):
|
||||||
if z_pos >= self.fade_end:
|
if z_pos >= self.fade_end:
|
||||||
return 0.
|
return 0.
|
||||||
|
@ -216,7 +219,9 @@ class BedMesh:
|
||||||
"Mesh Leveling: Error splitting move ")
|
"Mesh Leveling: Error splitting move ")
|
||||||
self.last_position[:] = newpos
|
self.last_position[:] = newpos
|
||||||
def get_status(self, eventtime=None):
|
def get_status(self, eventtime=None):
|
||||||
status = {
|
return self.status
|
||||||
|
def update_status(self):
|
||||||
|
self.status = {
|
||||||
"profile_name": "",
|
"profile_name": "",
|
||||||
"mesh_min": (0., 0.),
|
"mesh_min": (0., 0.),
|
||||||
"mesh_max": (0., 0.),
|
"mesh_max": (0., 0.),
|
||||||
|
@ -230,12 +235,11 @@ class BedMesh:
|
||||||
mesh_max = (params['max_x'], params['max_y'])
|
mesh_max = (params['max_x'], params['max_y'])
|
||||||
probed_matrix = self.z_mesh.get_probed_matrix()
|
probed_matrix = self.z_mesh.get_probed_matrix()
|
||||||
mesh_matrix = self.z_mesh.get_mesh_matrix()
|
mesh_matrix = self.z_mesh.get_mesh_matrix()
|
||||||
status['profile_name'] = self.pmgr.get_current_profile()
|
self.status['profile_name'] = self.pmgr.get_current_profile()
|
||||||
status['mesh_min'] = mesh_min
|
self.status['mesh_min'] = mesh_min
|
||||||
status['mesh_max'] = mesh_max
|
self.status['mesh_max'] = mesh_max
|
||||||
status['probed_matrix'] = probed_matrix
|
self.status['probed_matrix'] = probed_matrix
|
||||||
status['mesh_matrix'] = mesh_matrix
|
self.status['mesh_matrix'] = mesh_matrix
|
||||||
return status
|
|
||||||
def get_mesh(self):
|
def get_mesh(self):
|
||||||
return self.z_mesh
|
return self.z_mesh
|
||||||
cmd_BED_MESH_OUTPUT_help = "Retrieve interpolated grid of probed z-points"
|
cmd_BED_MESH_OUTPUT_help = "Retrieve interpolated grid of probed z-points"
|
||||||
|
@ -1180,6 +1184,7 @@ class ProfileManager:
|
||||||
profile['mesh_params'] = collections.OrderedDict(mesh_params)
|
profile['mesh_params'] = collections.OrderedDict(mesh_params)
|
||||||
self.profiles = profiles
|
self.profiles = profiles
|
||||||
self.current_profile = prof_name
|
self.current_profile = prof_name
|
||||||
|
self.bedmesh.update_status()
|
||||||
self.gcode.respond_info(
|
self.gcode.respond_info(
|
||||||
"Bed Mesh state has been saved to profile [%s]\n"
|
"Bed Mesh state has been saved to profile [%s]\n"
|
||||||
"for the current session. The SAVE_CONFIG command will\n"
|
"for the current session. The SAVE_CONFIG command will\n"
|
||||||
|
@ -1206,6 +1211,7 @@ class ProfileManager:
|
||||||
profiles = dict(self.profiles)
|
profiles = dict(self.profiles)
|
||||||
del profiles[prof_name]
|
del profiles[prof_name]
|
||||||
self.profiles = profiles
|
self.profiles = profiles
|
||||||
|
self.bedmesh.update_status()
|
||||||
self.gcode.respond_info(
|
self.gcode.respond_info(
|
||||||
"Profile [%s] removed from storage for this session.\n"
|
"Profile [%s] removed from storage for this session.\n"
|
||||||
"The SAVE_CONFIG command will update the printer\n"
|
"The SAVE_CONFIG command will update the printer\n"
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
class BedScrews:
|
class BedScrews:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
self.state = None
|
self.reset()
|
||||||
self.current_screw = 0
|
|
||||||
self.accepted_screws = 0
|
|
||||||
self.number_of_screws = 0
|
self.number_of_screws = 0
|
||||||
# Read config
|
# Read config
|
||||||
screws = []
|
screws = []
|
||||||
|
@ -39,6 +37,10 @@ class BedScrews:
|
||||||
self.gcode.register_command("BED_SCREWS_ADJUST",
|
self.gcode.register_command("BED_SCREWS_ADJUST",
|
||||||
self.cmd_BED_SCREWS_ADJUST,
|
self.cmd_BED_SCREWS_ADJUST,
|
||||||
desc=self.cmd_BED_SCREWS_ADJUST_help)
|
desc=self.cmd_BED_SCREWS_ADJUST_help)
|
||||||
|
def reset(self):
|
||||||
|
self.state = None
|
||||||
|
self.current_screw = 0
|
||||||
|
self.accepted_screws = 0
|
||||||
def move(self, coord, speed):
|
def move(self, coord, speed):
|
||||||
self.printer.lookup_object('toolhead').manual_move(coord, speed)
|
self.printer.lookup_object('toolhead').manual_move(coord, speed)
|
||||||
def move_to_screw(self, state, screw):
|
def move_to_screw(self, state, screw):
|
||||||
|
@ -64,6 +66,13 @@ class BedScrews:
|
||||||
self.gcode.register_command('ACCEPT', None)
|
self.gcode.register_command('ACCEPT', None)
|
||||||
self.gcode.register_command('ADJUSTED', None)
|
self.gcode.register_command('ADJUSTED', None)
|
||||||
self.gcode.register_command('ABORT', None)
|
self.gcode.register_command('ABORT', None)
|
||||||
|
def get_status(self, eventtime):
|
||||||
|
return {
|
||||||
|
'is_active': self.state is not None,
|
||||||
|
'state': self.state,
|
||||||
|
'current_screw': self.current_screw,
|
||||||
|
'accepted_screws': self.accepted_screws
|
||||||
|
}
|
||||||
cmd_BED_SCREWS_ADJUST_help = "Tool to help adjust bed leveling screws"
|
cmd_BED_SCREWS_ADJUST_help = "Tool to help adjust bed leveling screws"
|
||||||
def cmd_BED_SCREWS_ADJUST(self, gcmd):
|
def cmd_BED_SCREWS_ADJUST(self, gcmd):
|
||||||
if self.state is not None:
|
if self.state is not None:
|
||||||
|
@ -92,7 +101,7 @@ class BedScrews:
|
||||||
self.move_to_screw('fine', 0)
|
self.move_to_screw('fine', 0)
|
||||||
return
|
return
|
||||||
# Done
|
# Done
|
||||||
self.state = None
|
self.reset()
|
||||||
self.move((None, None, self.horizontal_move_z), self.lift_speed)
|
self.move((None, None, self.horizontal_move_z), self.lift_speed)
|
||||||
gcmd.respond_info("Bed screws tool completed successfully")
|
gcmd.respond_info("Bed screws tool completed successfully")
|
||||||
cmd_ADJUSTED_help = "Accept bed screw position after notable adjustment"
|
cmd_ADJUSTED_help = "Accept bed screw position after notable adjustment"
|
||||||
|
@ -103,7 +112,7 @@ class BedScrews:
|
||||||
cmd_ABORT_help = "Abort bed screws tool"
|
cmd_ABORT_help = "Abort bed screws tool"
|
||||||
def cmd_ABORT(self, gcmd):
|
def cmd_ABORT(self, gcmd):
|
||||||
self.unregister_commands()
|
self.unregister_commands()
|
||||||
self.state = None
|
self.reset()
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
return BedScrews(config)
|
return BedScrews(config)
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
NODEID_FIRST = 4
|
||||||
|
|
||||||
class PrinterCANBus:
|
class PrinterCANBus:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
|
@ -11,7 +13,7 @@ class PrinterCANBus:
|
||||||
def add_uuid(self, config, canbus_uuid, canbus_iface):
|
def add_uuid(self, config, canbus_uuid, canbus_iface):
|
||||||
if canbus_uuid in self.ids:
|
if canbus_uuid in self.ids:
|
||||||
raise config.error("Duplicate canbus_uuid")
|
raise config.error("Duplicate canbus_uuid")
|
||||||
new_id = len(self.ids)
|
new_id = len(self.ids) + NODEID_FIRST
|
||||||
self.ids[canbus_uuid] = new_id
|
self.ids[canbus_uuid] = new_id
|
||||||
return new_id
|
return new_id
|
||||||
def get_nodeid(self, canbus_uuid):
|
def get_nodeid(self, canbus_uuid):
|
||||||
|
|
|
@ -45,7 +45,7 @@ def measurements_to_distances(measured_params, delta_params):
|
||||||
od - opw
|
od - opw
|
||||||
for od, opw in zip(mp['OUTER_DISTS'], mp['OUTER_PILLAR_WIDTHS']) ]
|
for od, opw in zip(mp['OUTER_DISTS'], mp['OUTER_PILLAR_WIDTHS']) ]
|
||||||
# Convert angles in degrees to an XY multiplier
|
# Convert angles in degrees to an XY multiplier
|
||||||
obj_angles = map(math.radians, MeasureAngles)
|
obj_angles = list(map(math.radians, MeasureAngles))
|
||||||
xy_angles = list(zip(map(math.cos, obj_angles), map(math.sin, obj_angles)))
|
xy_angles = list(zip(map(math.cos, obj_angles), map(math.sin, obj_angles)))
|
||||||
# Calculate stable positions for center measurements
|
# Calculate stable positions for center measurements
|
||||||
inner_ridge = MeasureRidgeRadius * scale
|
inner_ridge = MeasureRidgeRadius * scale
|
||||||
|
|
|
@ -16,6 +16,9 @@ class DisplayStatus:
|
||||||
gcode = self.printer.lookup_object('gcode')
|
gcode = self.printer.lookup_object('gcode')
|
||||||
gcode.register_command('M73', self.cmd_M73)
|
gcode.register_command('M73', self.cmd_M73)
|
||||||
gcode.register_command('M117', self.cmd_M117)
|
gcode.register_command('M117', self.cmd_M117)
|
||||||
|
gcode.register_command(
|
||||||
|
'SET_DISPLAY_TEXT', self.cmd_SET_DISPLAY_TEXT,
|
||||||
|
desc=self.cmd_SET_DISPLAY_TEXT_help)
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
progress = self.progress
|
progress = self.progress
|
||||||
if progress is not None and eventtime > self.expire_progress:
|
if progress is not None and eventtime > self.expire_progress:
|
||||||
|
@ -39,6 +42,9 @@ class DisplayStatus:
|
||||||
def cmd_M117(self, gcmd):
|
def cmd_M117(self, gcmd):
|
||||||
msg = gcmd.get_raw_command_parameters() or None
|
msg = gcmd.get_raw_command_parameters() or None
|
||||||
self.message = msg
|
self.message = msg
|
||||||
|
cmd_SET_DISPLAY_TEXT_help = "Set or clear the display message"
|
||||||
|
def cmd_SET_DISPLAY_TEXT(self, gcmd):
|
||||||
|
self.message = gcmd.get("MSG", None)
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
return DisplayStatus(config)
|
return DisplayStatus(config)
|
||||||
|
|
|
@ -10,6 +10,7 @@ DS18_REPORT_TIME = 3.0
|
||||||
# Temperature can be sampled at any time but conversion time is ~750ms, so
|
# Temperature can be sampled at any time but conversion time is ~750ms, so
|
||||||
# setting the time too low will not make the reports come faster.
|
# setting the time too low will not make the reports come faster.
|
||||||
DS18_MIN_REPORT_TIME = 1.0
|
DS18_MIN_REPORT_TIME = 1.0
|
||||||
|
DS18_MAX_CONSECUTIVE_ERRORS = 4
|
||||||
|
|
||||||
class DS18B20:
|
class DS18B20:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
@ -31,8 +32,9 @@ class DS18B20:
|
||||||
|
|
||||||
def _build_config(self):
|
def _build_config(self):
|
||||||
sid = "".join(["%02x" % (x,) for x in self.sensor_id])
|
sid = "".join(["%02x" % (x,) for x in self.sensor_id])
|
||||||
self._mcu.add_config_cmd("config_ds18b20 oid=%d serial=%s"
|
self._mcu.add_config_cmd(
|
||||||
% (self.oid, sid))
|
"config_ds18b20 oid=%d serial=%s max_error_count=%d"
|
||||||
|
% (self.oid, sid, DS18_MAX_CONSECUTIVE_ERRORS))
|
||||||
|
|
||||||
clock = self._mcu.get_query_slot(self.oid)
|
clock = self._mcu.get_query_slot(self.oid)
|
||||||
self._report_clock = self._mcu.seconds_to_clock(self.report_time)
|
self._report_clock = self._mcu.seconds_to_clock(self.report_time)
|
||||||
|
@ -44,10 +46,10 @@ class DS18B20:
|
||||||
def _handle_ds18b20_response(self, params):
|
def _handle_ds18b20_response(self, params):
|
||||||
temp = params['value'] / 1000.0
|
temp = params['value'] / 1000.0
|
||||||
|
|
||||||
if temp < self.min_temp or temp > self.max_temp:
|
if params["fault"]:
|
||||||
self.printer.invoke_shutdown(
|
logging.info("ds18b20 reports fault %d (temp=%0.1f)",
|
||||||
"DS18B20 temperature %0.1f outside range of %0.1f:%.01f"
|
params["fault"], temp)
|
||||||
% (temp, self.min_temp, self.max_temp))
|
return
|
||||||
|
|
||||||
next_clock = self._mcu.clock32_to_clock64(params['next_clock'])
|
next_clock = self._mcu.clock32_to_clock64(params['next_clock'])
|
||||||
last_read_clock = next_clock - self._report_clock
|
last_read_clock = next_clock - self._report_clock
|
||||||
|
|
|
@ -0,0 +1,302 @@
|
||||||
|
# Exclude moves toward and inside objects
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019 Eric Callahan <arksine.code@gmail.com>
|
||||||
|
# Copyright (C) 2021 Troy Jacobson <troy.d.jacobson@gmail.com>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
|
class ExcludeObject:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
self.gcode = self.printer.lookup_object('gcode')
|
||||||
|
self.gcode_move = self.printer.load_object(config, 'gcode_move')
|
||||||
|
self.printer.register_event_handler('klippy:connect',
|
||||||
|
self._handle_connect)
|
||||||
|
self.printer.register_event_handler("virtual_sdcard:reset_file",
|
||||||
|
self._reset_file)
|
||||||
|
self.next_transform = None
|
||||||
|
self.last_position_extruded = [0., 0., 0., 0.]
|
||||||
|
self.last_position_excluded = [0., 0., 0., 0.]
|
||||||
|
|
||||||
|
self._reset_state()
|
||||||
|
self.gcode.register_command(
|
||||||
|
'EXCLUDE_OBJECT_START', self.cmd_EXCLUDE_OBJECT_START,
|
||||||
|
desc=self.cmd_EXCLUDE_OBJECT_START_help)
|
||||||
|
self.gcode.register_command(
|
||||||
|
'EXCLUDE_OBJECT_END', self.cmd_EXCLUDE_OBJECT_END,
|
||||||
|
desc=self.cmd_EXCLUDE_OBJECT_END_help)
|
||||||
|
self.gcode.register_command(
|
||||||
|
'EXCLUDE_OBJECT', self.cmd_EXCLUDE_OBJECT,
|
||||||
|
desc=self.cmd_EXCLUDE_OBJECT_help)
|
||||||
|
self.gcode.register_command(
|
||||||
|
'EXCLUDE_OBJECT_DEFINE', self.cmd_EXCLUDE_OBJECT_DEFINE,
|
||||||
|
desc=self.cmd_EXCLUDE_OBJECT_DEFINE_help)
|
||||||
|
|
||||||
|
def _register_transform(self):
|
||||||
|
if self.next_transform is None:
|
||||||
|
tuning_tower = self.printer.lookup_object('tuning_tower')
|
||||||
|
if tuning_tower.is_active():
|
||||||
|
logging.info('The ExcludeObject move transform is not being '
|
||||||
|
'loaded due to Tuning tower being Active')
|
||||||
|
return
|
||||||
|
|
||||||
|
self.next_transform = self.gcode_move.set_move_transform(self,
|
||||||
|
force=True)
|
||||||
|
self.extrusion_offsets = {}
|
||||||
|
self.max_position_extruded = 0
|
||||||
|
self.max_position_excluded = 0
|
||||||
|
self.extruder_adj = 0
|
||||||
|
self.initial_extrusion_moves = 5
|
||||||
|
self.last_position = [0., 0., 0., 0.]
|
||||||
|
|
||||||
|
self.get_position()
|
||||||
|
self.last_position_extruded[:] = self.last_position
|
||||||
|
self.last_position_excluded[:] = self.last_position
|
||||||
|
|
||||||
|
def _handle_connect(self):
|
||||||
|
self.toolhead = self.printer.lookup_object('toolhead')
|
||||||
|
|
||||||
|
def _unregister_transform(self):
|
||||||
|
if self.next_transform:
|
||||||
|
tuning_tower = self.printer.lookup_object('tuning_tower')
|
||||||
|
if tuning_tower.is_active():
|
||||||
|
logging.error('The Exclude Object move transform was not '
|
||||||
|
'unregistered because it is not at the head of the '
|
||||||
|
'transform chain.')
|
||||||
|
return
|
||||||
|
|
||||||
|
self.gcode_move.set_move_transform(self.next_transform, force=True)
|
||||||
|
self.next_transform = None
|
||||||
|
self.gcode_move.reset_last_position()
|
||||||
|
|
||||||
|
def _reset_state(self):
|
||||||
|
self.objects = []
|
||||||
|
self.excluded_objects = []
|
||||||
|
self.current_object = None
|
||||||
|
self.in_excluded_region = False
|
||||||
|
|
||||||
|
def _reset_file(self):
|
||||||
|
self._reset_state()
|
||||||
|
self._unregister_transform()
|
||||||
|
|
||||||
|
def _get_extrusion_offsets(self):
|
||||||
|
offset = self.extrusion_offsets.get(
|
||||||
|
self.toolhead.get_extruder().get_name())
|
||||||
|
if offset is None:
|
||||||
|
offset = [0., 0., 0., 0.]
|
||||||
|
self.extrusion_offsets[self.toolhead.get_extruder().get_name()] = \
|
||||||
|
offset
|
||||||
|
return offset
|
||||||
|
|
||||||
|
def get_position(self):
|
||||||
|
offset = self._get_extrusion_offsets()
|
||||||
|
pos = self.next_transform.get_position()
|
||||||
|
for i in range(4):
|
||||||
|
self.last_position[i] = pos[i] + offset[i]
|
||||||
|
return list(self.last_position)
|
||||||
|
|
||||||
|
def _normal_move(self, newpos, speed):
|
||||||
|
offset = self._get_extrusion_offsets()
|
||||||
|
|
||||||
|
if self.initial_extrusion_moves > 0 and \
|
||||||
|
self.last_position[3] != newpos[3]:
|
||||||
|
# Since the transform is not loaded until there is a request to
|
||||||
|
# exclude an object, the transform needs to track a few extrusions
|
||||||
|
# to get the state of the extruder
|
||||||
|
self.initial_extrusion_moves -= 1
|
||||||
|
|
||||||
|
self.last_position[:] = newpos
|
||||||
|
self.last_position_extruded[:] = self.last_position
|
||||||
|
self.max_position_extruded = max(self.max_position_extruded, newpos[3])
|
||||||
|
|
||||||
|
# These next few conditionals handle the moves immediately after leaving
|
||||||
|
# and excluded object. The toolhead is at the end of the last printed
|
||||||
|
# object and the gcode is at the end of the last excluded object.
|
||||||
|
#
|
||||||
|
# Ideally, there will be Z and E moves right away to adjust any offsets
|
||||||
|
# before moving away from the last position. Any remaining corrections
|
||||||
|
# will be made on the firs XY move.
|
||||||
|
if (offset[0] != 0 or offset[1] != 0) and \
|
||||||
|
(newpos[0] != self.last_position_excluded[0] or \
|
||||||
|
newpos[1] != self.last_position_excluded[1]):
|
||||||
|
offset[0] = 0
|
||||||
|
offset[1] = 0
|
||||||
|
offset[2] = 0
|
||||||
|
offset[3] += self.extruder_adj
|
||||||
|
self.extruder_adj = 0
|
||||||
|
|
||||||
|
if offset[2] != 0 and newpos[2] != self.last_position_excluded[2]:
|
||||||
|
offset[2] = 0
|
||||||
|
|
||||||
|
if self.extruder_adj != 0 and \
|
||||||
|
newpos[3] != self.last_position_excluded[3]:
|
||||||
|
offset[3] += self.extruder_adj
|
||||||
|
self.extruder_adj = 0
|
||||||
|
|
||||||
|
tx_pos = newpos[:]
|
||||||
|
for i in range(4):
|
||||||
|
tx_pos[i] = newpos[i] - offset[i]
|
||||||
|
self.next_transform.move(tx_pos, speed)
|
||||||
|
|
||||||
|
def _ignore_move(self, newpos, speed):
|
||||||
|
offset = self._get_extrusion_offsets()
|
||||||
|
for i in range(3):
|
||||||
|
offset[i] = newpos[i] - self.last_position_extruded[i]
|
||||||
|
offset[3] = offset[3] + newpos[3] - self.last_position[3]
|
||||||
|
self.last_position[:] = newpos
|
||||||
|
self.last_position_excluded[:] =self.last_position
|
||||||
|
self.max_position_excluded = max(self.max_position_excluded, newpos[3])
|
||||||
|
|
||||||
|
def _move_into_excluded_region(self, newpos, speed):
|
||||||
|
self.in_excluded_region = True
|
||||||
|
self._ignore_move(newpos, speed)
|
||||||
|
|
||||||
|
def _move_from_excluded_region(self, newpos, speed):
|
||||||
|
self.in_excluded_region = False
|
||||||
|
|
||||||
|
# This adjustment value is used to compensate for any retraction
|
||||||
|
# differences between the last object printed and excluded one.
|
||||||
|
self.extruder_adj = self.max_position_excluded \
|
||||||
|
- self.last_position_excluded[3] \
|
||||||
|
- (self.max_position_extruded - self.last_position_extruded[3])
|
||||||
|
self._normal_move(newpos, speed)
|
||||||
|
|
||||||
|
def _test_in_excluded_region(self):
|
||||||
|
# Inside cancelled object
|
||||||
|
return self.current_object in self.excluded_objects \
|
||||||
|
and self.initial_extrusion_moves == 0
|
||||||
|
|
||||||
|
def get_status(self, eventtime=None):
|
||||||
|
status = {
|
||||||
|
"objects": self.objects,
|
||||||
|
"excluded_objects": self.excluded_objects,
|
||||||
|
"current_object": self.current_object
|
||||||
|
}
|
||||||
|
return status
|
||||||
|
|
||||||
|
def move(self, newpos, speed):
|
||||||
|
move_in_excluded_region = self._test_in_excluded_region()
|
||||||
|
self.last_speed = speed
|
||||||
|
|
||||||
|
if move_in_excluded_region:
|
||||||
|
if self.in_excluded_region:
|
||||||
|
self._ignore_move(newpos, speed)
|
||||||
|
else:
|
||||||
|
self._move_into_excluded_region(newpos, speed)
|
||||||
|
else:
|
||||||
|
if self.in_excluded_region:
|
||||||
|
self._move_from_excluded_region(newpos, speed)
|
||||||
|
else:
|
||||||
|
self._normal_move(newpos, speed)
|
||||||
|
|
||||||
|
cmd_EXCLUDE_OBJECT_START_help = "Marks the beginning the current object" \
|
||||||
|
" as labeled"
|
||||||
|
def cmd_EXCLUDE_OBJECT_START(self, gcmd):
|
||||||
|
name = gcmd.get('NAME').upper()
|
||||||
|
if not any(obj["name"] == name for obj in self.objects):
|
||||||
|
self._add_object_definition({"name": name})
|
||||||
|
self.current_object = name
|
||||||
|
self.was_excluded_at_start = self._test_in_excluded_region()
|
||||||
|
|
||||||
|
cmd_EXCLUDE_OBJECT_END_help = "Marks the end the current object"
|
||||||
|
def cmd_EXCLUDE_OBJECT_END(self, gcmd):
|
||||||
|
if self.current_object == None and self.next_transform:
|
||||||
|
gcmd.respond_info("EXCLUDE_OBJECT_END called, but no object is"
|
||||||
|
" currently active")
|
||||||
|
return
|
||||||
|
name = gcmd.get('NAME', default=None)
|
||||||
|
if name != None and name.upper() != self.current_object:
|
||||||
|
gcmd.respond_info("EXCLUDE_OBJECT_END NAME=%s does not match the"
|
||||||
|
" current object NAME=%s" %
|
||||||
|
(name.upper(), self.current_object))
|
||||||
|
|
||||||
|
self.current_object = None
|
||||||
|
|
||||||
|
cmd_EXCLUDE_OBJECT_help = "Cancel moves inside a specified objects"
|
||||||
|
def cmd_EXCLUDE_OBJECT(self, gcmd):
|
||||||
|
reset = gcmd.get('RESET', None)
|
||||||
|
current = gcmd.get('CURRENT', None)
|
||||||
|
name = gcmd.get('NAME', '').upper()
|
||||||
|
|
||||||
|
if reset:
|
||||||
|
if name:
|
||||||
|
self._unexclude_object(name)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.excluded_objects = []
|
||||||
|
|
||||||
|
elif name:
|
||||||
|
if name.upper() not in self.excluded_objects:
|
||||||
|
self._exclude_object(name.upper())
|
||||||
|
|
||||||
|
elif current:
|
||||||
|
if not self.current_object:
|
||||||
|
gcmd.respond_error('There is no current object to cancel')
|
||||||
|
|
||||||
|
else:
|
||||||
|
self._exclude_object(self.current_object)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self._list_excluded_objects(gcmd)
|
||||||
|
|
||||||
|
cmd_EXCLUDE_OBJECT_DEFINE_help = "Provides a summary of an object"
|
||||||
|
def cmd_EXCLUDE_OBJECT_DEFINE(self, gcmd):
|
||||||
|
reset = gcmd.get('RESET', None)
|
||||||
|
name = gcmd.get('NAME', '').upper()
|
||||||
|
|
||||||
|
if reset:
|
||||||
|
self._reset_file()
|
||||||
|
|
||||||
|
elif name:
|
||||||
|
parameters = gcmd.get_command_parameters().copy()
|
||||||
|
parameters.pop('NAME')
|
||||||
|
center = parameters.pop('CENTER', None)
|
||||||
|
polygon = parameters.pop('POLYGON', None)
|
||||||
|
|
||||||
|
obj = {"name": name.upper()}
|
||||||
|
obj.update(parameters)
|
||||||
|
|
||||||
|
if center != None:
|
||||||
|
obj['center'] = json.loads('[%s]' % center)
|
||||||
|
|
||||||
|
if polygon != None:
|
||||||
|
obj['polygon'] = json.loads(polygon)
|
||||||
|
|
||||||
|
self._add_object_definition(obj)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self._list_objects(gcmd)
|
||||||
|
|
||||||
|
def _add_object_definition(self, definition):
|
||||||
|
self.objects = sorted(self.objects + [definition],
|
||||||
|
key=lambda o: o["name"])
|
||||||
|
|
||||||
|
def _exclude_object(self, name):
|
||||||
|
self._register_transform()
|
||||||
|
self.gcode.respond_info('Excluding object {}'.format(name.upper()))
|
||||||
|
if name not in self.excluded_objects:
|
||||||
|
self.excluded_objects = sorted(self.excluded_objects + [name])
|
||||||
|
|
||||||
|
def _unexclude_object(self, name):
|
||||||
|
self.gcode.respond_info('Unexcluding object {}'.format(name.upper()))
|
||||||
|
if name in self.excluded_objects:
|
||||||
|
excluded_objects = list(self.excluded_objects)
|
||||||
|
excluded_objects.remove(name)
|
||||||
|
self.excluded_objects = sorted(excluded_objects)
|
||||||
|
|
||||||
|
def _list_objects(self, gcmd):
|
||||||
|
if gcmd.get('JSON', None) is not None:
|
||||||
|
object_list = json.dumps(self.objects)
|
||||||
|
else:
|
||||||
|
object_list = " ".join(obj['name'] for obj in self.objects)
|
||||||
|
gcmd.respond_info('Known objects: {}'.format(object_list))
|
||||||
|
|
||||||
|
def _list_excluded_objects(self, gcmd):
|
||||||
|
object_list = " ".join(self.excluded_objects)
|
||||||
|
gcmd.respond_info('Excluded objects: {}'.format(object_list))
|
||||||
|
|
||||||
|
def load_config(config):
|
||||||
|
return ExcludeObject(config)
|
|
@ -24,9 +24,19 @@ class ManualProbe:
|
||||||
'Z_OFFSET_APPLY_ENDSTOP',
|
'Z_OFFSET_APPLY_ENDSTOP',
|
||||||
self.cmd_Z_OFFSET_APPLY_ENDSTOP,
|
self.cmd_Z_OFFSET_APPLY_ENDSTOP,
|
||||||
desc=self.cmd_Z_OFFSET_APPLY_ENDSTOP_help)
|
desc=self.cmd_Z_OFFSET_APPLY_ENDSTOP_help)
|
||||||
|
self.reset_status()
|
||||||
def manual_probe_finalize(self, kin_pos):
|
def manual_probe_finalize(self, kin_pos):
|
||||||
if kin_pos is not None:
|
if kin_pos is not None:
|
||||||
self.gcode.respond_info("Z position is %.3f" % (kin_pos[2],))
|
self.gcode.respond_info("Z position is %.3f" % (kin_pos[2],))
|
||||||
|
def reset_status(self):
|
||||||
|
self.status = {
|
||||||
|
'is_active': False,
|
||||||
|
'z_position': None,
|
||||||
|
'z_position_lower': None,
|
||||||
|
'z_position_upper': None
|
||||||
|
}
|
||||||
|
def get_status(self, eventtime):
|
||||||
|
return self.status
|
||||||
cmd_MANUAL_PROBE_help = "Start manual probe helper script"
|
cmd_MANUAL_PROBE_help = "Start manual probe helper script"
|
||||||
def cmd_MANUAL_PROBE(self, gcmd):
|
def cmd_MANUAL_PROBE(self, gcmd):
|
||||||
ManualProbeHelper(self.printer, gcmd, self.manual_probe_finalize)
|
ManualProbeHelper(self.printer, gcmd, self.manual_probe_finalize)
|
||||||
|
@ -78,6 +88,7 @@ class ManualProbeHelper:
|
||||||
self.finalize_callback = finalize_callback
|
self.finalize_callback = finalize_callback
|
||||||
self.gcode = self.printer.lookup_object('gcode')
|
self.gcode = self.printer.lookup_object('gcode')
|
||||||
self.toolhead = self.printer.lookup_object('toolhead')
|
self.toolhead = self.printer.lookup_object('toolhead')
|
||||||
|
self.manual_probe = self.printer.lookup_object('manual_probe')
|
||||||
self.speed = gcmd.get_float("SPEED", 5.)
|
self.speed = gcmd.get_float("SPEED", 5.)
|
||||||
self.past_positions = []
|
self.past_positions = []
|
||||||
self.last_toolhead_pos = self.last_kinematics_pos = None
|
self.last_toolhead_pos = self.last_kinematics_pos = None
|
||||||
|
@ -130,11 +141,20 @@ class ManualProbeHelper:
|
||||||
prev_pos = next_pos - 1
|
prev_pos = next_pos - 1
|
||||||
if next_pos < len(pp) and pp[next_pos] == z_pos:
|
if next_pos < len(pp) and pp[next_pos] == z_pos:
|
||||||
next_pos += 1
|
next_pos += 1
|
||||||
|
prev_pos_val = next_pos_val = None
|
||||||
prev_str = next_str = "??????"
|
prev_str = next_str = "??????"
|
||||||
if prev_pos >= 0:
|
if prev_pos >= 0:
|
||||||
prev_str = "%.3f" % (pp[prev_pos],)
|
prev_pos_val = pp[prev_pos]
|
||||||
|
prev_str = "%.3f" % (prev_pos_val,)
|
||||||
if next_pos < len(pp):
|
if next_pos < len(pp):
|
||||||
next_str = "%.3f" % (pp[next_pos],)
|
next_pos_val = pp[next_pos]
|
||||||
|
next_str = "%.3f" % (next_pos_val,)
|
||||||
|
self.manual_probe.status = {
|
||||||
|
'is_active': True,
|
||||||
|
'z_position': z_pos,
|
||||||
|
'z_position_lower': prev_pos_val,
|
||||||
|
'z_position_upper': next_pos_val,
|
||||||
|
}
|
||||||
# Find recent positions
|
# Find recent positions
|
||||||
self.gcode.respond_info("Z position: %s --> %.3f <-- %s"
|
self.gcode.respond_info("Z position: %s --> %.3f <-- %s"
|
||||||
% (prev_str, z_pos, next_str))
|
% (prev_str, z_pos, next_str))
|
||||||
|
@ -183,6 +203,7 @@ class ManualProbeHelper:
|
||||||
self.move_z(next_z_pos)
|
self.move_z(next_z_pos)
|
||||||
self.report_z_status(next_z_pos != z_pos, z_pos)
|
self.report_z_status(next_z_pos != z_pos, z_pos)
|
||||||
def finalize(self, success):
|
def finalize(self, success):
|
||||||
|
self.manual_probe.reset_status()
|
||||||
self.gcode.register_command('ACCEPT', None)
|
self.gcode.register_command('ACCEPT', None)
|
||||||
self.gcode.register_command('NEXT', None)
|
self.gcode.register_command('NEXT', None)
|
||||||
self.gcode.register_command('ABORT', None)
|
self.gcode.register_command('ABORT', None)
|
||||||
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
# Support for reading acceleration data from an mpu9250 chip
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022 Harry Beyel <harry3b9@gmail.com>
|
||||||
|
# Copyright (C) 2020-2021 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
import logging, time, collections, threading, multiprocessing, os
|
||||||
|
from . import bus, motion_report, adxl345
|
||||||
|
|
||||||
|
MPU9250_ADDR = 0x68
|
||||||
|
|
||||||
|
MPU9250_DEV_ID = 0x73
|
||||||
|
MPU6050_DEV_ID = 0x68
|
||||||
|
|
||||||
|
# MPU9250 registers
|
||||||
|
REG_DEVID = 0x75
|
||||||
|
REG_FIFO_EN = 0x23
|
||||||
|
REG_SMPLRT_DIV = 0x19
|
||||||
|
REG_CONFIG = 0x1A
|
||||||
|
REG_ACCEL_CONFIG = 0x1C
|
||||||
|
REG_ACCEL_CONFIG2 = 0x1D
|
||||||
|
REG_USER_CTRL = 0x6A
|
||||||
|
REG_PWR_MGMT_1 = 0x6B
|
||||||
|
REG_PWR_MGMT_2 = 0x6C
|
||||||
|
|
||||||
|
SAMPLE_RATE_DIVS = { 4000:0x00 }
|
||||||
|
|
||||||
|
SET_CONFIG = 0x01 # FIFO mode 'stream' style
|
||||||
|
SET_ACCEL_CONFIG = 0x10 # 8g full scale
|
||||||
|
SET_ACCEL_CONFIG2 = 0x08 # 1046Hz BW, 0.503ms delay 4kHz sample rate
|
||||||
|
SET_PWR_MGMT_1_WAKE = 0x00
|
||||||
|
SET_PWR_MGMT_1_SLEEP= 0x40
|
||||||
|
SET_PWR_MGMT_2_ACCEL_ON = 0x07
|
||||||
|
SET_PWR_MGMT_2_OFF = 0x3F
|
||||||
|
|
||||||
|
FREEFALL_ACCEL = 9.80665 * 1000.
|
||||||
|
# SCALE = 1/4096 g/LSB @8g scale * Earth gravity in mm/s**2
|
||||||
|
SCALE = 0.000244140625 * FREEFALL_ACCEL
|
||||||
|
|
||||||
|
FIFO_SIZE = 512
|
||||||
|
|
||||||
|
Accel_Measurement = collections.namedtuple(
|
||||||
|
'Accel_Measurement', ('time', 'accel_x', 'accel_y', 'accel_z'))
|
||||||
|
|
||||||
|
MIN_MSG_TIME = 0.100
|
||||||
|
|
||||||
|
BYTES_PER_SAMPLE = 6
|
||||||
|
SAMPLES_PER_BLOCK = 8
|
||||||
|
|
||||||
|
# Printer class that controls MPU9250 chip
|
||||||
|
class MPU9250:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
adxl345.AccelCommandHelper(config, self)
|
||||||
|
self.query_rate = 0
|
||||||
|
am = {'x': (0, SCALE), 'y': (1, SCALE), 'z': (2, SCALE),
|
||||||
|
'-x': (0, -SCALE), '-y': (1, -SCALE), '-z': (2, -SCALE)}
|
||||||
|
axes_map = config.getlist('axes_map', ('x','y','z'), count=3)
|
||||||
|
if any([a not in am for a in axes_map]):
|
||||||
|
raise config.error("Invalid mpu9250 axes_map parameter")
|
||||||
|
self.axes_map = [am[a.strip()] for a in axes_map]
|
||||||
|
self.data_rate = config.getint('rate', 4000)
|
||||||
|
if self.data_rate not in SAMPLE_RATE_DIVS:
|
||||||
|
raise config.error("Invalid rate parameter: %d" % (self.data_rate,))
|
||||||
|
# Measurement storage (accessed from background thread)
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.raw_samples = []
|
||||||
|
# Setup mcu sensor_mpu9250 bulk query code
|
||||||
|
self.i2c = bus.MCU_I2C_from_config(config,
|
||||||
|
default_addr=MPU9250_ADDR,
|
||||||
|
default_speed=400000)
|
||||||
|
self.mcu = mcu = self.i2c.get_mcu()
|
||||||
|
self.oid = oid = mcu.create_oid()
|
||||||
|
self.query_mpu9250_cmd = self.query_mpu9250_end_cmd = None
|
||||||
|
self.query_mpu9250_status_cmd = None
|
||||||
|
mcu.register_config_callback(self._build_config)
|
||||||
|
mcu.register_response(self._handle_mpu9250_data, "mpu9250_data", oid)
|
||||||
|
# Clock tracking
|
||||||
|
self.last_sequence = self.max_query_duration = 0
|
||||||
|
self.last_limit_count = self.last_error_count = 0
|
||||||
|
self.clock_sync = adxl345.ClockSyncRegression(self.mcu, 640)
|
||||||
|
# API server endpoints
|
||||||
|
self.api_dump = motion_report.APIDumpHelper(
|
||||||
|
self.printer, self._api_update, self._api_startstop, 0.100)
|
||||||
|
self.name = config.get_name().split()[-1]
|
||||||
|
wh = self.printer.lookup_object('webhooks')
|
||||||
|
wh.register_mux_endpoint("mpu9250/dump_mpu9250", "sensor", self.name,
|
||||||
|
self._handle_dump_mpu9250)
|
||||||
|
def _build_config(self):
|
||||||
|
cmdqueue = self.i2c.get_command_queue()
|
||||||
|
self.mcu.add_config_cmd("config_mpu9250 oid=%d i2c_oid=%d"
|
||||||
|
% (self.oid, self.i2c.get_oid()))
|
||||||
|
self.mcu.add_config_cmd("query_mpu9250 oid=%d clock=0 rest_ticks=0"
|
||||||
|
% (self.oid,), on_restart=True)
|
||||||
|
self.query_mpu9250_cmd = self.mcu.lookup_command(
|
||||||
|
"query_mpu9250 oid=%c clock=%u rest_ticks=%u", cq=cmdqueue)
|
||||||
|
self.query_mpu9250_end_cmd = self.mcu.lookup_query_command(
|
||||||
|
"query_mpu9250 oid=%c clock=%u rest_ticks=%u",
|
||||||
|
"mpu9250_status oid=%c clock=%u query_ticks=%u next_sequence=%hu"
|
||||||
|
" buffered=%c fifo=%u limit_count=%hu", oid=self.oid, cq=cmdqueue)
|
||||||
|
self.query_mpu9250_status_cmd = self.mcu.lookup_query_command(
|
||||||
|
"query_mpu9250_status oid=%c",
|
||||||
|
"mpu9250_status oid=%c clock=%u query_ticks=%u next_sequence=%hu"
|
||||||
|
" buffered=%c fifo=%u limit_count=%hu", oid=self.oid, cq=cmdqueue)
|
||||||
|
def read_reg(self, reg):
|
||||||
|
params = self.i2c.i2c_read([reg], 1)
|
||||||
|
return bytearray(params['response'])[0]
|
||||||
|
|
||||||
|
def set_reg(self, reg, val, minclock=0):
|
||||||
|
self.i2c.i2c_write([reg, val & 0xFF], minclock=minclock)
|
||||||
|
|
||||||
|
# Measurement collection
|
||||||
|
def is_measuring(self):
|
||||||
|
return self.query_rate > 0
|
||||||
|
def _handle_mpu9250_data(self, params):
|
||||||
|
with self.lock:
|
||||||
|
self.raw_samples.append(params)
|
||||||
|
def _extract_samples(self, raw_samples):
|
||||||
|
# Load variables to optimize inner loop below
|
||||||
|
(x_pos, x_scale), (y_pos, y_scale), (z_pos, z_scale) = self.axes_map
|
||||||
|
last_sequence = self.last_sequence
|
||||||
|
time_base, chip_base, inv_freq = self.clock_sync.get_time_translation()
|
||||||
|
# Process every message in 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 -= (seq_diff & 0x8000) << 1
|
||||||
|
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):
|
||||||
|
d_xyz = d[i*BYTES_PER_SAMPLE:(i+1)*BYTES_PER_SAMPLE]
|
||||||
|
xhigh, xlow, yhigh, ylow, zhigh, zlow = d_xyz
|
||||||
|
# Merge and perform twos-complement
|
||||||
|
rx = ((xhigh << 8) | xlow) - ((xhigh & 0x80) << 9)
|
||||||
|
ry = ((yhigh << 8) | ylow) - ((yhigh & 0x80) << 9)
|
||||||
|
rz = ((zhigh << 8) | zlow) - ((zhigh & 0x80) << 9)
|
||||||
|
|
||||||
|
raw_xyz = (rx, ry, rz)
|
||||||
|
x = round(raw_xyz[x_pos] * x_scale, 6)
|
||||||
|
y = round(raw_xyz[y_pos] * y_scale, 6)
|
||||||
|
z = round(raw_xyz[z_pos] * z_scale, 6)
|
||||||
|
ptime = round(time_base + (msg_cdiff + i) * inv_freq, 6)
|
||||||
|
samples[count] = (ptime, x, y, z)
|
||||||
|
count += 1
|
||||||
|
self.clock_sync.set_last_chip_clock(seq * SAMPLES_PER_BLOCK + i)
|
||||||
|
del samples[count:]
|
||||||
|
return samples
|
||||||
|
|
||||||
|
def _update_clock(self, minclock=0):
|
||||||
|
# Query current state
|
||||||
|
for retry in range(5):
|
||||||
|
params = self.query_mpu9250_status_cmd.send([self.oid],
|
||||||
|
minclock=minclock)
|
||||||
|
fifo = params['fifo'] & 0x1fff
|
||||||
|
if fifo <= FIFO_SIZE:
|
||||||
|
break
|
||||||
|
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
|
||||||
|
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
|
||||||
|
duration = params['query_ticks']
|
||||||
|
if duration > self.max_query_duration:
|
||||||
|
# Skip measurement as a high query time could skew clock tracking
|
||||||
|
self.max_query_duration = max(2 * self.max_query_duration,
|
||||||
|
self.mcu.seconds_to_clock(.000005))
|
||||||
|
return
|
||||||
|
self.max_query_duration = 2 * duration
|
||||||
|
msg_count = (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
|
||||||
|
# of mpu9250 hw processing time.
|
||||||
|
chip_clock = msg_count + 1
|
||||||
|
self.clock_sync.update(mcu_clock + duration // 2, chip_clock)
|
||||||
|
def _start_measurements(self):
|
||||||
|
if self.is_measuring():
|
||||||
|
return
|
||||||
|
# In case of miswiring, testing MPU9250 device ID prevents treating
|
||||||
|
# noise or wrong signal as a correctly initialized device
|
||||||
|
dev_id = self.read_reg(REG_DEVID)
|
||||||
|
if dev_id != MPU9250_DEV_ID and dev_id != MPU6050_DEV_ID:
|
||||||
|
raise self.printer.command_error(
|
||||||
|
"Invalid mpu9250/mpu6050 id (got %x).\n"
|
||||||
|
"This is generally indicative of connection problems\n"
|
||||||
|
"(e.g. faulty wiring) or a faulty chip."
|
||||||
|
% (dev_id))
|
||||||
|
# Setup chip in requested query rate
|
||||||
|
self.set_reg(REG_PWR_MGMT_1, SET_PWR_MGMT_1_WAKE)
|
||||||
|
self.set_reg(REG_PWR_MGMT_2, SET_PWR_MGMT_2_ACCEL_ON)
|
||||||
|
time.sleep(20. / 1000) # wait for accelerometer chip wake up
|
||||||
|
self.set_reg(REG_SMPLRT_DIV, SAMPLE_RATE_DIVS[self.data_rate])
|
||||||
|
self.set_reg(REG_CONFIG, SET_CONFIG)
|
||||||
|
self.set_reg(REG_ACCEL_CONFIG, SET_ACCEL_CONFIG)
|
||||||
|
self.set_reg(REG_ACCEL_CONFIG2, SET_ACCEL_CONFIG2)
|
||||||
|
|
||||||
|
# Setup samples
|
||||||
|
with self.lock:
|
||||||
|
self.raw_samples = []
|
||||||
|
# Start bulk reading
|
||||||
|
systime = self.printer.get_reactor().monotonic()
|
||||||
|
print_time = self.mcu.estimated_print_time(systime) + MIN_MSG_TIME
|
||||||
|
reqclock = self.mcu.print_time_to_clock(print_time)
|
||||||
|
rest_ticks = self.mcu.seconds_to_clock(1. / self.data_rate)
|
||||||
|
self.query_rate = self.data_rate
|
||||||
|
self.query_mpu9250_cmd.send([self.oid, reqclock, rest_ticks],
|
||||||
|
reqclock=reqclock)
|
||||||
|
logging.info("MPU9250 starting '%s' measurements", self.name)
|
||||||
|
# Initialize clock tracking
|
||||||
|
self.last_sequence = 0
|
||||||
|
self.last_limit_count = self.last_error_count = 0
|
||||||
|
self.clock_sync.reset(reqclock, 0)
|
||||||
|
self.max_query_duration = 1 << 31
|
||||||
|
self._update_clock(minclock=reqclock)
|
||||||
|
self.max_query_duration = 1 << 31
|
||||||
|
def _finish_measurements(self):
|
||||||
|
if not self.is_measuring():
|
||||||
|
return
|
||||||
|
# Halt bulk reading
|
||||||
|
params = self.query_mpu9250_end_cmd.send([self.oid, 0, 0])
|
||||||
|
self.query_rate = 0
|
||||||
|
with self.lock:
|
||||||
|
self.raw_samples = []
|
||||||
|
logging.info("MPU9250 finished '%s' measurements", self.name)
|
||||||
|
self.set_reg(REG_PWR_MGMT_1, SET_PWR_MGMT_1_SLEEP)
|
||||||
|
self.set_reg(REG_PWR_MGMT_2, SET_PWR_MGMT_2_OFF)
|
||||||
|
|
||||||
|
# API interface
|
||||||
|
def _api_update(self, eventtime):
|
||||||
|
self._update_clock()
|
||||||
|
with self.lock:
|
||||||
|
raw_samples = self.raw_samples
|
||||||
|
self.raw_samples = []
|
||||||
|
if not raw_samples:
|
||||||
|
return {}
|
||||||
|
samples = self._extract_samples(raw_samples)
|
||||||
|
if not samples:
|
||||||
|
return {}
|
||||||
|
return {'data': samples, 'errors': self.last_error_count,
|
||||||
|
'overflows': self.last_limit_count}
|
||||||
|
def _api_startstop(self, is_start):
|
||||||
|
if is_start:
|
||||||
|
self._start_measurements()
|
||||||
|
else:
|
||||||
|
self._finish_measurements()
|
||||||
|
def _handle_dump_mpu9250(self, web_request):
|
||||||
|
self.api_dump.add_client(web_request)
|
||||||
|
hdr = ('time', 'x_acceleration', 'y_acceleration', 'z_acceleration')
|
||||||
|
web_request.send({'header': hdr})
|
||||||
|
def start_internal_client(self):
|
||||||
|
cconn = self.api_dump.add_internal_client()
|
||||||
|
return adxl345.AccelQueryHelper(self.printer, cconn)
|
||||||
|
|
||||||
|
def load_config(config):
|
||||||
|
return MPU9250(config)
|
||||||
|
|
||||||
|
def load_config_prefix(config):
|
||||||
|
return MPU9250(config)
|
|
@ -544,13 +544,15 @@ class Palette2:
|
||||||
self.cmd_Disconnect()
|
self.cmd_Disconnect()
|
||||||
return self.reactor.NEVER
|
return self.reactor.NEVER
|
||||||
if len(raw_bytes):
|
if len(raw_bytes):
|
||||||
text_buffer = self.read_buffer + str(raw_bytes.decode())
|
new_buffer = str(raw_bytes.decode(encoding='UTF-8',
|
||||||
|
errors='ignore'))
|
||||||
|
text_buffer = self.read_buffer + new_buffer
|
||||||
while True:
|
while True:
|
||||||
i = text_buffer.find("\n")
|
i = text_buffer.find("\n")
|
||||||
if i >= 0:
|
if i >= 0:
|
||||||
line = text_buffer[0:i+1]
|
line = text_buffer[0:i + 1]
|
||||||
self.read_queue.put(line.strip())
|
self.read_queue.put(line.strip())
|
||||||
text_buffer = text_buffer[i+1:]
|
text_buffer = text_buffer[i + 1:]
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
self.read_buffer = text_buffer
|
self.read_buffer = text_buffer
|
||||||
|
@ -566,7 +568,7 @@ class Palette2:
|
||||||
|
|
||||||
heartbeat_strings = [COMMAND_HEARTBEAT, "Connection Okay"]
|
heartbeat_strings = [COMMAND_HEARTBEAT, "Connection Okay"]
|
||||||
if not any(x in text_line for x in heartbeat_strings):
|
if not any(x in text_line for x in heartbeat_strings):
|
||||||
logging.debug("%0.3f P2 -> : %s" %(eventtime, text_line))
|
logging.debug("%0.3f P2 -> : %s" % (eventtime, text_line))
|
||||||
|
|
||||||
# Received a heartbeat from the device
|
# Received a heartbeat from the device
|
||||||
if text_line == COMMAND_HEARTBEAT:
|
if text_line == COMMAND_HEARTBEAT:
|
||||||
|
@ -621,7 +623,7 @@ class Palette2:
|
||||||
idle_time = est_print_time - print_time
|
idle_time = est_print_time - print_time
|
||||||
if not lookahead_empty or idle_time < 0.5:
|
if not lookahead_empty or idle_time < 0.5:
|
||||||
return eventtime + \
|
return eventtime + \
|
||||||
max(0., min(1., print_time - est_print_time))
|
max(0., min(1., print_time - est_print_time))
|
||||||
|
|
||||||
extrude = abs(self.remaining_load_length)
|
extrude = abs(self.remaining_load_length)
|
||||||
extrude = min(50, extrude / 2)
|
extrude = min(50, extrude / 2)
|
||||||
|
@ -646,5 +648,6 @@ class Palette2:
|
||||||
status["ping"] = self.omega_pings[-1]
|
status["ping"] = self.omega_pings[-1]
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
return Palette2(config)
|
return Palette2(config)
|
||||||
|
|
|
@ -147,15 +147,21 @@ class ResonanceTester:
|
||||||
(chip_axis, self.printer.lookup_object(chip_name))
|
(chip_axis, self.printer.lookup_object(chip_name))
|
||||||
for chip_axis, chip_name in self.accel_chip_names]
|
for chip_axis, chip_name in self.accel_chip_names]
|
||||||
|
|
||||||
def _run_test(self, gcmd, axes, helper, raw_name_suffix=None):
|
def _run_test(self, gcmd, axes, helper, raw_name_suffix=None,
|
||||||
|
accel_chips=None, test_point=None):
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
toolhead = self.printer.lookup_object('toolhead')
|
||||||
calibration_data = {axis: None for axis in axes}
|
calibration_data = {axis: None for axis in axes}
|
||||||
|
|
||||||
self.test.prepare_test(gcmd)
|
self.test.prepare_test(gcmd)
|
||||||
test_points = self.test.get_start_test_points()
|
|
||||||
|
if test_point is not None:
|
||||||
|
test_points = [test_point]
|
||||||
|
else:
|
||||||
|
test_points = self.test.get_start_test_points()
|
||||||
|
|
||||||
for point in test_points:
|
for point in test_points:
|
||||||
toolhead.manual_move(point, self.move_speed)
|
toolhead.manual_move(point, self.move_speed)
|
||||||
if len(test_points) > 1:
|
if len(test_points) > 1 or test_point is not None:
|
||||||
gcmd.respond_info(
|
gcmd.respond_info(
|
||||||
"Probing point (%.3f, %.3f, %.3f)" % tuple(point))
|
"Probing point (%.3f, %.3f, %.3f)" % tuple(point))
|
||||||
for axis in axes:
|
for axis in axes:
|
||||||
|
@ -165,29 +171,36 @@ class ResonanceTester:
|
||||||
gcmd.respond_info("Testing axis %s" % axis.get_name())
|
gcmd.respond_info("Testing axis %s" % axis.get_name())
|
||||||
|
|
||||||
raw_values = []
|
raw_values = []
|
||||||
for chip_axis, chip in self.accel_chips:
|
if accel_chips is None:
|
||||||
if axis.matches(chip_axis):
|
for chip_axis, chip in self.accel_chips:
|
||||||
|
if axis.matches(chip_axis):
|
||||||
|
aclient = chip.start_internal_client()
|
||||||
|
raw_values.append((chip_axis, aclient, chip.name))
|
||||||
|
else:
|
||||||
|
for chip in accel_chips:
|
||||||
aclient = chip.start_internal_client()
|
aclient = chip.start_internal_client()
|
||||||
raw_values.append((chip_axis, aclient))
|
raw_values.append((axis, aclient, chip.name))
|
||||||
|
|
||||||
# Generate moves
|
# Generate moves
|
||||||
self.test.run_test(axis, gcmd)
|
self.test.run_test(axis, gcmd)
|
||||||
for chip_axis, aclient in raw_values:
|
for chip_axis, aclient, chip_name in raw_values:
|
||||||
aclient.finish_measurements()
|
aclient.finish_measurements()
|
||||||
if raw_name_suffix is not None:
|
if raw_name_suffix is not None:
|
||||||
raw_name = self.get_filename(
|
raw_name = self.get_filename(
|
||||||
'raw_data', raw_name_suffix, axis,
|
'raw_data', raw_name_suffix, axis,
|
||||||
point if len(test_points) > 1 else None)
|
point if len(test_points) > 1 else None,
|
||||||
|
chip_name if accel_chips is not None else None,)
|
||||||
aclient.write_to_file(raw_name)
|
aclient.write_to_file(raw_name)
|
||||||
gcmd.respond_info(
|
gcmd.respond_info(
|
||||||
"Writing raw accelerometer data to "
|
"Writing raw accelerometer data to "
|
||||||
"%s file" % (raw_name,))
|
"%s file" % (raw_name,))
|
||||||
if helper is None:
|
if helper is None:
|
||||||
continue
|
continue
|
||||||
for chip_axis, aclient in raw_values:
|
for chip_axis, aclient, chip_name in raw_values:
|
||||||
if not aclient.has_valid_samples():
|
if not aclient.has_valid_samples():
|
||||||
raise gcmd.error(
|
raise gcmd.error(
|
||||||
"%s-axis accelerometer measured no data" % (
|
"accelerometer '%s' measured no data" % (
|
||||||
chip_axis,))
|
chip_name,))
|
||||||
new_data = helper.process_accelerometer_data(aclient)
|
new_data = helper.process_accelerometer_data(aclient)
|
||||||
if calibration_data[axis] is None:
|
if calibration_data[axis] is None:
|
||||||
calibration_data[axis] = new_data
|
calibration_data[axis] = new_data
|
||||||
|
@ -198,6 +211,28 @@ class ResonanceTester:
|
||||||
def cmd_TEST_RESONANCES(self, gcmd):
|
def cmd_TEST_RESONANCES(self, gcmd):
|
||||||
# Parse parameters
|
# Parse parameters
|
||||||
axis = _parse_axis(gcmd, gcmd.get("AXIS").lower())
|
axis = _parse_axis(gcmd, gcmd.get("AXIS").lower())
|
||||||
|
accel_chips = gcmd.get("CHIPS", None)
|
||||||
|
test_point = gcmd.get("POINT", None)
|
||||||
|
|
||||||
|
if test_point:
|
||||||
|
test_coords = test_point.split(',')
|
||||||
|
if len(test_coords) != 3:
|
||||||
|
raise gcmd.error("Invalid POINT parameter, must be 'x,y,z'")
|
||||||
|
try:
|
||||||
|
test_point = [float(p.strip()) for p in test_coords]
|
||||||
|
except ValueError:
|
||||||
|
raise gcmd.error("Invalid POINT parameter, must be 'x,y,z'"
|
||||||
|
" where x, y and z are valid floating point numbers")
|
||||||
|
|
||||||
|
if accel_chips:
|
||||||
|
parsed_chips = []
|
||||||
|
for chip_name in accel_chips.split(','):
|
||||||
|
if "adxl345" in chip_name:
|
||||||
|
chip_lookup_name = chip_name.strip()
|
||||||
|
else:
|
||||||
|
chip_lookup_name = "adxl345 " + chip_name.strip();
|
||||||
|
chip = self.printer.lookup_object(chip_lookup_name)
|
||||||
|
parsed_chips.append(chip)
|
||||||
|
|
||||||
outputs = gcmd.get("OUTPUT", "resonances").lower().split(',')
|
outputs = gcmd.get("OUTPUT", "resonances").lower().split(',')
|
||||||
for output in outputs:
|
for output in outputs:
|
||||||
|
@ -221,10 +256,13 @@ class ResonanceTester:
|
||||||
|
|
||||||
data = self._run_test(
|
data = self._run_test(
|
||||||
gcmd, [axis], helper,
|
gcmd, [axis], helper,
|
||||||
raw_name_suffix=name_suffix if raw_output else None)[axis]
|
raw_name_suffix=name_suffix if raw_output else None,
|
||||||
|
accel_chips=parsed_chips if accel_chips else None,
|
||||||
|
test_point=test_point)[axis]
|
||||||
if csv_output:
|
if csv_output:
|
||||||
csv_name = self.save_calibration_data('resonances', name_suffix,
|
csv_name = self.save_calibration_data('resonances', name_suffix,
|
||||||
helper, axis, data)
|
helper, axis, data,
|
||||||
|
point=test_point)
|
||||||
gcmd.respond_info(
|
gcmd.respond_info(
|
||||||
"Resonances data written to %s file" % (csv_name,))
|
"Resonances data written to %s file" % (csv_name,))
|
||||||
cmd_SHAPER_CALIBRATE_help = (
|
cmd_SHAPER_CALIBRATE_help = (
|
||||||
|
@ -287,7 +325,8 @@ class ResonanceTester:
|
||||||
for chip_axis, aclient in raw_values:
|
for chip_axis, aclient in raw_values:
|
||||||
if not aclient.has_valid_samples():
|
if not aclient.has_valid_samples():
|
||||||
raise gcmd.error(
|
raise gcmd.error(
|
||||||
"%s-axis accelerometer measured no data" % (chip_axis,))
|
"%s-axis accelerometer measured no data" % (
|
||||||
|
chip_axis,))
|
||||||
data = helper.process_accelerometer_data(aclient)
|
data = helper.process_accelerometer_data(aclient)
|
||||||
vx = data.psd_x.mean()
|
vx = data.psd_x.mean()
|
||||||
vy = data.psd_y.mean()
|
vy = data.psd_y.mean()
|
||||||
|
@ -299,18 +338,22 @@ class ResonanceTester:
|
||||||
def is_valid_name_suffix(self, name_suffix):
|
def is_valid_name_suffix(self, name_suffix):
|
||||||
return name_suffix.replace('-', '').replace('_', '').isalnum()
|
return name_suffix.replace('-', '').replace('_', '').isalnum()
|
||||||
|
|
||||||
def get_filename(self, base, name_suffix, axis=None, point=None):
|
def get_filename(self, base, name_suffix, axis=None,
|
||||||
|
point=None, chip_name=None):
|
||||||
name = base
|
name = base
|
||||||
if axis:
|
if axis:
|
||||||
name += '_' + axis.get_name()
|
name += '_' + axis.get_name()
|
||||||
|
if chip_name:
|
||||||
|
name += '_' + chip_name.replace(" ", "_")
|
||||||
if point:
|
if point:
|
||||||
name += "_%.3f_%.3f_%.3f" % (point[0], point[1], point[2])
|
name += "_%.3f_%.3f_%.3f" % (point[0], point[1], point[2])
|
||||||
name += '_' + name_suffix
|
name += '_' + name_suffix
|
||||||
return os.path.join("/tmp", name + ".csv")
|
return os.path.join("/tmp", name + ".csv")
|
||||||
|
|
||||||
def save_calibration_data(self, base_name, name_suffix, shaper_calibrate,
|
def save_calibration_data(self, base_name, name_suffix, shaper_calibrate,
|
||||||
axis, calibration_data, all_shapers=None):
|
axis, calibration_data,
|
||||||
output = self.get_filename(base_name, name_suffix, axis)
|
all_shapers=None, point=None):
|
||||||
|
output = self.get_filename(base_name, name_suffix, axis, point)
|
||||||
shaper_calibrate.save_calibration_data(output, calibration_data,
|
shaper_calibrate.save_calibration_data(output, calibration_data,
|
||||||
all_shapers)
|
all_shapers)
|
||||||
return output
|
return output
|
||||||
|
|
|
@ -10,6 +10,10 @@ respond_types = {
|
||||||
'error' : '!!',
|
'error' : '!!',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
respond_types_no_space = {
|
||||||
|
'echo_no_space': 'echo:',
|
||||||
|
}
|
||||||
|
|
||||||
class HostResponder:
|
class HostResponder:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
|
@ -26,19 +30,26 @@ class HostResponder:
|
||||||
gcmd.respond_raw("%s %s" % (self.default_prefix, msg))
|
gcmd.respond_raw("%s %s" % (self.default_prefix, msg))
|
||||||
cmd_RESPOND_help = ("Echo the message prepended with a prefix")
|
cmd_RESPOND_help = ("Echo the message prepended with a prefix")
|
||||||
def cmd_RESPOND(self, gcmd):
|
def cmd_RESPOND(self, gcmd):
|
||||||
|
no_space = False
|
||||||
respond_type = gcmd.get('TYPE', None)
|
respond_type = gcmd.get('TYPE', None)
|
||||||
prefix = self.default_prefix
|
prefix = self.default_prefix
|
||||||
if(respond_type != None):
|
if(respond_type != None):
|
||||||
respond_type = respond_type.lower()
|
respond_type = respond_type.lower()
|
||||||
if(respond_type in respond_types):
|
if(respond_type in respond_types):
|
||||||
prefix = respond_types[respond_type]
|
prefix = respond_types[respond_type]
|
||||||
|
elif(respond_type in respond_types_no_space):
|
||||||
|
prefix = respond_types_no_space[respond_type]
|
||||||
|
no_space = True
|
||||||
else:
|
else:
|
||||||
raise gcmd.error(
|
raise gcmd.error(
|
||||||
"RESPOND TYPE '%s' is invalid. Must be one"
|
"RESPOND TYPE '%s' is invalid. Must be one"
|
||||||
" of 'echo', 'command', or 'error'" % (respond_type,))
|
" of 'echo', 'command', or 'error'" % (respond_type,))
|
||||||
prefix = gcmd.get('PREFIX', prefix)
|
prefix = gcmd.get('PREFIX', prefix)
|
||||||
msg = gcmd.get('MSG', '')
|
msg = gcmd.get('MSG', '')
|
||||||
gcmd.respond_raw("%s %s" % (prefix, msg))
|
if(no_space):
|
||||||
|
gcmd.respond_raw("%s%s" % (prefix, msg))
|
||||||
|
else:
|
||||||
|
gcmd.respond_raw("%s %s" % (prefix, msg))
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
return HostResponder(config)
|
return HostResponder(config)
|
||||||
|
|
|
@ -72,6 +72,7 @@ class PrinterTemperatureMCU:
|
||||||
('stm32f070', self.config_stm32f070),
|
('stm32f070', self.config_stm32f070),
|
||||||
('stm32f072', self.config_stm32f0x2),
|
('stm32f072', self.config_stm32f0x2),
|
||||||
('stm32g0', self.config_stm32g0),
|
('stm32g0', self.config_stm32g0),
|
||||||
|
('stm32h7', self.config_stm32h7),
|
||||||
('', self.config_unknown)]
|
('', self.config_unknown)]
|
||||||
for name, func in cfg_funcs:
|
for name, func in cfg_funcs:
|
||||||
if self.mcu_type.startswith(name):
|
if self.mcu_type.startswith(name):
|
||||||
|
@ -143,6 +144,11 @@ class PrinterTemperatureMCU:
|
||||||
cal_adc_130 = self.read16(0x1FFF75CA) * 3.0 / (3.3 * 4095.)
|
cal_adc_130 = self.read16(0x1FFF75CA) * 3.0 / (3.3 * 4095.)
|
||||||
self.slope = (130. - 30.) / (cal_adc_130 - cal_adc_30)
|
self.slope = (130. - 30.) / (cal_adc_130 - cal_adc_30)
|
||||||
self.base_temperature = self.calc_base(30., cal_adc_30)
|
self.base_temperature = self.calc_base(30., cal_adc_30)
|
||||||
|
def config_stm32h7(self):
|
||||||
|
cal_adc_30 = self.read16(0x1FF1E820) / 65535.
|
||||||
|
cal_adc_110 = self.read16(0x1FF1E840) / 65535.
|
||||||
|
self.slope = (110. - 30.) / (cal_adc_110 - cal_adc_30)
|
||||||
|
self.base_temperature = self.calc_base(30., cal_adc_30)
|
||||||
def read16(self, addr):
|
def read16(self, addr):
|
||||||
params = self.debug_read_cmd.send([1, addr])
|
params = self.debug_read_cmd.send([1, addr])
|
||||||
return params['val']
|
return params['val']
|
||||||
|
|
|
@ -99,6 +99,8 @@ class TuningTower:
|
||||||
self.gcode.respond_info("Ending tuning test mode")
|
self.gcode.respond_info("Ending tuning test mode")
|
||||||
self.gcode_move.set_move_transform(self.normal_transform, force=True)
|
self.gcode_move.set_move_transform(self.normal_transform, force=True)
|
||||||
self.normal_transform = None
|
self.normal_transform = None
|
||||||
|
def is_active(self):
|
||||||
|
return self.normal_transform is not None
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
return TuningTower(config)
|
return TuningTower(config)
|
||||||
|
|
|
@ -9,22 +9,27 @@ VALID_GCODE_EXTS = ['gcode', 'g', 'gco']
|
||||||
|
|
||||||
class VirtualSD:
|
class VirtualSD:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
printer.register_event_handler("klippy:shutdown", self.handle_shutdown)
|
self.printer.register_event_handler("klippy:shutdown",
|
||||||
|
self.handle_shutdown)
|
||||||
# sdcard state
|
# sdcard state
|
||||||
sd = config.get('path')
|
sd = config.get('path')
|
||||||
self.sdcard_dirname = os.path.normpath(os.path.expanduser(sd))
|
self.sdcard_dirname = os.path.normpath(os.path.expanduser(sd))
|
||||||
self.current_file = None
|
self.current_file = None
|
||||||
self.file_position = self.file_size = 0
|
self.file_position = self.file_size = 0
|
||||||
# Print Stat Tracking
|
# Print Stat Tracking
|
||||||
self.print_stats = printer.load_object(config, 'print_stats')
|
self.print_stats = self.printer.load_object(config, 'print_stats')
|
||||||
# Work timer
|
# Work timer
|
||||||
self.reactor = printer.get_reactor()
|
self.reactor = self.printer.get_reactor()
|
||||||
self.must_pause_work = self.cmd_from_sd = False
|
self.must_pause_work = self.cmd_from_sd = False
|
||||||
self.next_file_position = 0
|
self.next_file_position = 0
|
||||||
self.work_timer = None
|
self.work_timer = None
|
||||||
|
# Error handling
|
||||||
|
gcode_macro = self.printer.load_object(config, 'gcode_macro')
|
||||||
|
self.on_error_gcode = gcode_macro.load_template(
|
||||||
|
config, 'on_error_gcode', '')
|
||||||
# Register commands
|
# Register commands
|
||||||
self.gcode = printer.lookup_object('gcode')
|
self.gcode = self.printer.lookup_object('gcode')
|
||||||
for cmd in ['M20', 'M21', 'M23', 'M24', 'M25', 'M26', 'M27']:
|
for cmd in ['M20', 'M21', 'M23', 'M24', 'M25', 'M26', 'M27']:
|
||||||
self.gcode.register_command(cmd, getattr(self, 'cmd_' + cmd))
|
self.gcode.register_command(cmd, getattr(self, 'cmd_' + cmd))
|
||||||
for cmd in ['M28', 'M29', 'M30']:
|
for cmd in ['M28', 'M29', 'M30']:
|
||||||
|
@ -125,6 +130,7 @@ class VirtualSD:
|
||||||
self.current_file = None
|
self.current_file = None
|
||||||
self.file_position = self.file_size = 0.
|
self.file_position = self.file_size = 0.
|
||||||
self.print_stats.reset()
|
self.print_stats.reset()
|
||||||
|
self.printer.send_event("virtual_sdcard:reset_file")
|
||||||
cmd_SDCARD_RESET_FILE_help = "Clears a loaded SD File. Stops the print "\
|
cmd_SDCARD_RESET_FILE_help = "Clears a loaded SD File. Stops the print "\
|
||||||
"if necessary"
|
"if necessary"
|
||||||
def cmd_SDCARD_RESET_FILE(self, gcmd):
|
def cmd_SDCARD_RESET_FILE(self, gcmd):
|
||||||
|
@ -258,6 +264,10 @@ class VirtualSD:
|
||||||
self.gcode.run_script(line)
|
self.gcode.run_script(line)
|
||||||
except self.gcode.error as e:
|
except self.gcode.error as e:
|
||||||
error_message = str(e)
|
error_message = str(e)
|
||||||
|
try:
|
||||||
|
self.gcode.run_script(self.on_error_gcode.render())
|
||||||
|
except:
|
||||||
|
logging.exception("virtual_sdcard on_error")
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
logging.exception("virtual_sdcard dispatch")
|
logging.exception("virtual_sdcard dispatch")
|
||||||
|
|
|
@ -150,6 +150,7 @@ class DeltaKinematics:
|
||||||
'homed_axes': '' if self.need_home else 'xyz',
|
'homed_axes': '' if self.need_home else 'xyz',
|
||||||
'axis_minimum': self.axes_min,
|
'axis_minimum': self.axes_min,
|
||||||
'axis_maximum': self.axes_max,
|
'axis_maximum': self.axes_max,
|
||||||
|
'cone_start_z': self.limit_z,
|
||||||
}
|
}
|
||||||
def get_calibration(self):
|
def get_calibration(self):
|
||||||
endstops = [rail.get_homing_info().position_endstop
|
endstops = [rail.get_homing_info().position_endstop
|
||||||
|
|
|
@ -231,8 +231,7 @@ class Printer:
|
||||||
run_result = self.run_result
|
run_result = self.run_result
|
||||||
try:
|
try:
|
||||||
if run_result == 'firmware_restart':
|
if run_result == 'firmware_restart':
|
||||||
for n, m in self.lookup_objects(module='mcu'):
|
self.send_event("klippy:firmware_restart")
|
||||||
m.microcontroller_restart()
|
|
||||||
self.send_event("klippy:disconnect")
|
self.send_event("klippy:disconnect")
|
||||||
except:
|
except:
|
||||||
logging.exception("Unhandled exception during post run")
|
logging.exception("Unhandled exception during post run")
|
||||||
|
@ -342,7 +341,7 @@ def main():
|
||||||
start_args['log_file'] = options.logfile
|
start_args['log_file'] = options.logfile
|
||||||
bglogger = queuelogger.setup_bg_logging(options.logfile, debuglevel)
|
bglogger = queuelogger.setup_bg_logging(options.logfile, debuglevel)
|
||||||
else:
|
else:
|
||||||
logging.basicConfig(level=debuglevel)
|
logging.getLogger().setLevel(debuglevel)
|
||||||
logging.info("Starting Klippy...")
|
logging.info("Starting Klippy...")
|
||||||
start_args['software_version'] = util.get_git_version()
|
start_args['software_version'] = util.get_git_version()
|
||||||
start_args['cpu_info'] = util.get_cpu_info()
|
start_args['cpu_info'] = util.get_cpu_info()
|
||||||
|
|
|
@ -563,6 +563,7 @@ class MCU:
|
||||||
self._restart_method = config.getchoice('restart_method',
|
self._restart_method = config.getchoice('restart_method',
|
||||||
rmethods, None)
|
rmethods, None)
|
||||||
self._reset_cmd = self._config_reset_cmd = None
|
self._reset_cmd = self._config_reset_cmd = None
|
||||||
|
self._is_mcu_bridge = False
|
||||||
self._emergency_stop_cmd = None
|
self._emergency_stop_cmd = None
|
||||||
self._is_shutdown = self._is_timeout = False
|
self._is_shutdown = self._is_timeout = False
|
||||||
self._shutdown_clock = 0
|
self._shutdown_clock = 0
|
||||||
|
@ -589,9 +590,11 @@ class MCU:
|
||||||
self._mcu_tick_stddev = 0.
|
self._mcu_tick_stddev = 0.
|
||||||
self._mcu_tick_awake = 0.
|
self._mcu_tick_awake = 0.
|
||||||
# Register handlers
|
# Register handlers
|
||||||
printer.register_event_handler("klippy:connect", self._connect)
|
printer.register_event_handler("klippy:firmware_restart",
|
||||||
|
self._firmware_restart)
|
||||||
printer.register_event_handler("klippy:mcu_identify",
|
printer.register_event_handler("klippy:mcu_identify",
|
||||||
self._mcu_identify)
|
self._mcu_identify)
|
||||||
|
printer.register_event_handler("klippy:connect", self._connect)
|
||||||
printer.register_event_handler("klippy:shutdown", self._shutdown)
|
printer.register_event_handler("klippy:shutdown", self._shutdown)
|
||||||
printer.register_event_handler("klippy:disconnect", self._disconnect)
|
printer.register_event_handler("klippy:disconnect", self._disconnect)
|
||||||
# Serial callbacks
|
# Serial callbacks
|
||||||
|
@ -797,6 +800,10 @@ class MCU:
|
||||||
mbaud = msgparser.get_constant('SERIAL_BAUD', None)
|
mbaud = msgparser.get_constant('SERIAL_BAUD', None)
|
||||||
if self._restart_method is None and mbaud is None and not ext_only:
|
if self._restart_method is None and mbaud is None and not ext_only:
|
||||||
self._restart_method = 'command'
|
self._restart_method = 'command'
|
||||||
|
if msgparser.get_constant('CANBUS_BRIDGE', 0):
|
||||||
|
self._is_mcu_bridge = True
|
||||||
|
self._printer.register_event_handler("klippy:firmware_restart",
|
||||||
|
self._firmware_restart_bridge)
|
||||||
version, build_versions = msgparser.get_version_info()
|
version, build_versions = msgparser.get_version_info()
|
||||||
self._get_status_info['mcu_version'] = version
|
self._get_status_info['mcu_version'] = version
|
||||||
self._get_status_info['mcu_build_versions'] = build_versions
|
self._get_status_info['mcu_build_versions'] = build_versions
|
||||||
|
@ -914,7 +921,9 @@ class MCU:
|
||||||
chelper.run_hub_ctrl(0)
|
chelper.run_hub_ctrl(0)
|
||||||
self._reactor.pause(self._reactor.monotonic() + 2.)
|
self._reactor.pause(self._reactor.monotonic() + 2.)
|
||||||
chelper.run_hub_ctrl(1)
|
chelper.run_hub_ctrl(1)
|
||||||
def microcontroller_restart(self):
|
def _firmware_restart(self, force=False):
|
||||||
|
if self._is_mcu_bridge and not force:
|
||||||
|
return
|
||||||
if self._restart_method == 'rpi_usb':
|
if self._restart_method == 'rpi_usb':
|
||||||
self._restart_rpi_usb()
|
self._restart_rpi_usb()
|
||||||
elif self._restart_method == 'command':
|
elif self._restart_method == 'command':
|
||||||
|
@ -923,6 +932,8 @@ class MCU:
|
||||||
self._restart_cheetah()
|
self._restart_cheetah()
|
||||||
else:
|
else:
|
||||||
self._restart_arduino()
|
self._restart_arduino()
|
||||||
|
def _firmware_restart_bridge(self):
|
||||||
|
self._firmware_restart(True)
|
||||||
# Misc external commands
|
# Misc external commands
|
||||||
def is_fileoutput(self):
|
def is_fileoutput(self):
|
||||||
return self._printer.get_start_args().get('debugoutput') is not None
|
return self._printer.get_start_args().get('debugoutput') is not None
|
||||||
|
|
|
@ -50,9 +50,10 @@ class ReactorCallback:
|
||||||
return self.reactor.NEVER
|
return self.reactor.NEVER
|
||||||
|
|
||||||
class ReactorFileHandler:
|
class ReactorFileHandler:
|
||||||
def __init__(self, fd, callback):
|
def __init__(self, fd, read_callback, write_callback):
|
||||||
self.fd = fd
|
self.fd = fd
|
||||||
self.callback = callback
|
self.read_callback = read_callback
|
||||||
|
self.write_callback = write_callback
|
||||||
def fileno(self):
|
def fileno(self):
|
||||||
return self.fd
|
return self.fd
|
||||||
|
|
||||||
|
@ -107,7 +108,8 @@ class SelectReactor:
|
||||||
self._pipe_fds = None
|
self._pipe_fds = None
|
||||||
self._async_queue = queue.Queue()
|
self._async_queue = queue.Queue()
|
||||||
# File descriptors
|
# File descriptors
|
||||||
self._fds = []
|
self._read_fds = []
|
||||||
|
self._write_fds = []
|
||||||
# Greenlets
|
# Greenlets
|
||||||
self._g_dispatch = None
|
self._g_dispatch = None
|
||||||
self._greenlets = []
|
self._greenlets = []
|
||||||
|
@ -236,12 +238,26 @@ class SelectReactor:
|
||||||
def mutex(self, is_locked=False):
|
def mutex(self, is_locked=False):
|
||||||
return ReactorMutex(self, is_locked)
|
return ReactorMutex(self, is_locked)
|
||||||
# File descriptors
|
# File descriptors
|
||||||
def register_fd(self, fd, callback):
|
def register_fd(self, fd, read_callback, write_callback=None):
|
||||||
file_handler = ReactorFileHandler(fd, callback)
|
file_handler = ReactorFileHandler(fd, read_callback, write_callback)
|
||||||
self._fds.append(file_handler)
|
self.set_fd_wake(file_handle, True, False)
|
||||||
return file_handler
|
return file_handler
|
||||||
def unregister_fd(self, file_handler):
|
def unregister_fd(self, file_handler):
|
||||||
self._fds.pop(self._fds.index(file_handler))
|
if file_handler in self._read_fds:
|
||||||
|
self._read_fds.pop(self._read_fds.index(file_handler))
|
||||||
|
if file_handler in self._write_fds:
|
||||||
|
self._write_fds.pop(self._write_fds.index(file_handler))
|
||||||
|
def set_fd_wake(self, file_handler, is_readable=True, is_writeable=False):
|
||||||
|
if file_hander in self._read_fds:
|
||||||
|
if not is_readable:
|
||||||
|
self._read_fds.pop(self._read_fds.index(file_handler))
|
||||||
|
elif is_readable:
|
||||||
|
self._read_fds.append(file_handler)
|
||||||
|
if file_hander in self._write_fds:
|
||||||
|
if not is_writeable:
|
||||||
|
self._write_fds.pop(self._write_fds.index(file_handler))
|
||||||
|
elif is_writeable:
|
||||||
|
self._write_fds.append(file_handler)
|
||||||
# Main loop
|
# Main loop
|
||||||
def _dispatch_loop(self):
|
def _dispatch_loop(self):
|
||||||
self._g_dispatch = g_dispatch = greenlet.getcurrent()
|
self._g_dispatch = g_dispatch = greenlet.getcurrent()
|
||||||
|
@ -250,11 +266,18 @@ class SelectReactor:
|
||||||
while self._process:
|
while self._process:
|
||||||
timeout = self._check_timers(eventtime, busy)
|
timeout = self._check_timers(eventtime, busy)
|
||||||
busy = False
|
busy = False
|
||||||
res = select.select(self._fds, [], [], timeout)
|
res = select.select(self._read_fds, self.write_fds, [], timeout)
|
||||||
eventtime = self.monotonic()
|
eventtime = self.monotonic()
|
||||||
for fd in res[0]:
|
for fd in res[0]:
|
||||||
busy = True
|
busy = True
|
||||||
fd.callback(eventtime)
|
fd.read_callback(eventtime)
|
||||||
|
if g_dispatch is not self._g_dispatch:
|
||||||
|
self._end_greenlet(g_dispatch)
|
||||||
|
eventtime = self.monotonic()
|
||||||
|
break
|
||||||
|
for fd in res[1]:
|
||||||
|
busy = True
|
||||||
|
fd.write_callback(eventtime)
|
||||||
if g_dispatch is not self._g_dispatch:
|
if g_dispatch is not self._g_dispatch:
|
||||||
self._end_greenlet(g_dispatch)
|
self._end_greenlet(g_dispatch)
|
||||||
eventtime = self.monotonic()
|
eventtime = self.monotonic()
|
||||||
|
@ -289,10 +312,10 @@ class PollReactor(SelectReactor):
|
||||||
self._poll = select.poll()
|
self._poll = select.poll()
|
||||||
self._fds = {}
|
self._fds = {}
|
||||||
# File descriptors
|
# File descriptors
|
||||||
def register_fd(self, fd, callback):
|
def register_fd(self, fd, read_callback, write_callback=None):
|
||||||
file_handler = ReactorFileHandler(fd, callback)
|
file_handler = ReactorFileHandler(fd, read_callback, write_callback)
|
||||||
fds = self._fds.copy()
|
fds = self._fds.copy()
|
||||||
fds[fd] = callback
|
fds[fd] = file_handler
|
||||||
self._fds = fds
|
self._fds = fds
|
||||||
self._poll.register(file_handler, select.POLLIN | select.POLLHUP)
|
self._poll.register(file_handler, select.POLLIN | select.POLLHUP)
|
||||||
return file_handler
|
return file_handler
|
||||||
|
@ -301,6 +324,13 @@ class PollReactor(SelectReactor):
|
||||||
fds = self._fds.copy()
|
fds = self._fds.copy()
|
||||||
del fds[file_handler.fd]
|
del fds[file_handler.fd]
|
||||||
self._fds = fds
|
self._fds = fds
|
||||||
|
def set_fd_wake(self, file_handler, is_readable=True, is_writeable=False):
|
||||||
|
flags = select.POLLHUP
|
||||||
|
if is_readable:
|
||||||
|
flags |= select.POLLIN
|
||||||
|
if is_writeable:
|
||||||
|
flags |= select.POLLOUT
|
||||||
|
self._poll.modify(file_handler, flags)
|
||||||
# Main loop
|
# Main loop
|
||||||
def _dispatch_loop(self):
|
def _dispatch_loop(self):
|
||||||
self._g_dispatch = g_dispatch = greenlet.getcurrent()
|
self._g_dispatch = g_dispatch = greenlet.getcurrent()
|
||||||
|
@ -313,11 +343,18 @@ class PollReactor(SelectReactor):
|
||||||
eventtime = self.monotonic()
|
eventtime = self.monotonic()
|
||||||
for fd, event in res:
|
for fd, event in res:
|
||||||
busy = True
|
busy = True
|
||||||
self._fds[fd](eventtime)
|
if event & (select.POLLIN | select.POLLHUP):
|
||||||
if g_dispatch is not self._g_dispatch:
|
self._fds[fd].read_callback(eventtime)
|
||||||
self._end_greenlet(g_dispatch)
|
if g_dispatch is not self._g_dispatch:
|
||||||
eventtime = self.monotonic()
|
self._end_greenlet(g_dispatch)
|
||||||
break
|
eventtime = self.monotonic()
|
||||||
|
break
|
||||||
|
if event & select.POLLOUT:
|
||||||
|
self._fds[fd].write_callback(eventtime)
|
||||||
|
if g_dispatch is not self._g_dispatch:
|
||||||
|
self._end_greenlet(g_dispatch)
|
||||||
|
eventtime = self.monotonic()
|
||||||
|
break
|
||||||
self._g_dispatch = None
|
self._g_dispatch = None
|
||||||
|
|
||||||
class EPollReactor(SelectReactor):
|
class EPollReactor(SelectReactor):
|
||||||
|
@ -326,8 +363,8 @@ class EPollReactor(SelectReactor):
|
||||||
self._epoll = select.epoll()
|
self._epoll = select.epoll()
|
||||||
self._fds = {}
|
self._fds = {}
|
||||||
# File descriptors
|
# File descriptors
|
||||||
def register_fd(self, fd, callback):
|
def register_fd(self, fd, read_callback, write_callback=None):
|
||||||
file_handler = ReactorFileHandler(fd, callback)
|
file_handler = ReactorFileHandler(fd, read_callback, write_callback)
|
||||||
fds = self._fds.copy()
|
fds = self._fds.copy()
|
||||||
fds[fd] = callback
|
fds[fd] = callback
|
||||||
self._fds = fds
|
self._fds = fds
|
||||||
|
@ -338,6 +375,13 @@ class EPollReactor(SelectReactor):
|
||||||
fds = self._fds.copy()
|
fds = self._fds.copy()
|
||||||
del fds[file_handler.fd]
|
del fds[file_handler.fd]
|
||||||
self._fds = fds
|
self._fds = fds
|
||||||
|
def set_fd_wake(self, file_handler, is_readable=True, is_writeable=False):
|
||||||
|
flags = select.POLLHUP
|
||||||
|
if is_readable:
|
||||||
|
flags |= select.EPOLLIN
|
||||||
|
if is_writeable:
|
||||||
|
flags |= select.EPOLLOUT
|
||||||
|
self._epoll.modify(file_handler, flags)
|
||||||
# Main loop
|
# Main loop
|
||||||
def _dispatch_loop(self):
|
def _dispatch_loop(self):
|
||||||
self._g_dispatch = g_dispatch = greenlet.getcurrent()
|
self._g_dispatch = g_dispatch = greenlet.getcurrent()
|
||||||
|
@ -350,11 +394,18 @@ class EPollReactor(SelectReactor):
|
||||||
eventtime = self.monotonic()
|
eventtime = self.monotonic()
|
||||||
for fd, event in res:
|
for fd, event in res:
|
||||||
busy = True
|
busy = True
|
||||||
self._fds[fd](eventtime)
|
if event & (select.EPOLLIN | select.EPOLLHUP):
|
||||||
if g_dispatch is not self._g_dispatch:
|
self._fds[fd].read_callback(eventtime)
|
||||||
self._end_greenlet(g_dispatch)
|
if g_dispatch is not self._g_dispatch:
|
||||||
eventtime = self.monotonic()
|
self._end_greenlet(g_dispatch)
|
||||||
break
|
eventtime = self.monotonic()
|
||||||
|
break
|
||||||
|
if event & select.EPOLLOUT:
|
||||||
|
self._fds[fd].write_callback(eventtime)
|
||||||
|
if g_dispatch is not self._g_dispatch:
|
||||||
|
self._end_greenlet(g_dispatch)
|
||||||
|
eventtime = self.monotonic()
|
||||||
|
break
|
||||||
self._g_dispatch = None
|
self._g_dispatch = None
|
||||||
|
|
||||||
# Use the poll based reactor if it is available
|
# Use the poll based reactor if it is available
|
||||||
|
|
|
@ -12,7 +12,6 @@ class error(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class SerialReader:
|
class SerialReader:
|
||||||
BITS_PER_BYTE = 10.
|
|
||||||
def __init__(self, reactor, warn_prefix=""):
|
def __init__(self, reactor, warn_prefix=""):
|
||||||
self.reactor = reactor
|
self.reactor = reactor
|
||||||
self.warn_prefix = warn_prefix
|
self.warn_prefix = warn_prefix
|
||||||
|
@ -97,11 +96,13 @@ class SerialReader:
|
||||||
self.msgparser = msgparser
|
self.msgparser = msgparser
|
||||||
self.register_response(self.handle_unknown, '#unknown')
|
self.register_response(self.handle_unknown, '#unknown')
|
||||||
# Setup baud adjust
|
# Setup baud adjust
|
||||||
mcu_baud = msgparser.get_constant_float('SERIAL_BAUD', None)
|
if serial_fd_type == b'c':
|
||||||
if mcu_baud is not None:
|
wire_freq = msgparser.get_constant_float('CANBUS_FREQUENCY', None)
|
||||||
baud_adjust = self.BITS_PER_BYTE / mcu_baud
|
else:
|
||||||
self.ffi_lib.serialqueue_set_baud_adjust(
|
wire_freq = msgparser.get_constant_float('SERIAL_BAUD', None)
|
||||||
self.serialqueue, baud_adjust)
|
if wire_freq is not None:
|
||||||
|
self.ffi_lib.serialqueue_set_wire_frequency(self.serialqueue,
|
||||||
|
wire_freq)
|
||||||
receive_window = msgparser.get_constant_int('RECEIVE_WINDOW', None)
|
receive_window = msgparser.get_constant_int('RECEIVE_WINDOW', None)
|
||||||
if receive_window is not None:
|
if receive_window is not None:
|
||||||
self.ffi_lib.serialqueue_set_receive_window(
|
self.ffi_lib.serialqueue_set_receive_window(
|
||||||
|
|
|
@ -162,6 +162,16 @@ class ServerSocket:
|
||||||
def pop_client(self, client_id):
|
def pop_client(self, client_id):
|
||||||
self.clients.pop(client_id, None)
|
self.clients.pop(client_id, None)
|
||||||
|
|
||||||
|
def stats(self, eventtime):
|
||||||
|
# Called once per second - check for idle clients
|
||||||
|
for client in list(self.clients.values()):
|
||||||
|
if client.is_blocking:
|
||||||
|
client.blocking_count -= 1
|
||||||
|
if client.blocking_count < 0:
|
||||||
|
logging.info("Closing unresponsive client %s", client.uid)
|
||||||
|
client.close()
|
||||||
|
return False, ""
|
||||||
|
|
||||||
class ClientConnection:
|
class ClientConnection:
|
||||||
def __init__(self, server, sock):
|
def __init__(self, server, sock):
|
||||||
self.printer = server.printer
|
self.printer = server.printer
|
||||||
|
@ -171,9 +181,10 @@ class ClientConnection:
|
||||||
self.uid = id(self)
|
self.uid = id(self)
|
||||||
self.sock = sock
|
self.sock = sock
|
||||||
self.fd_handle = self.reactor.register_fd(
|
self.fd_handle = self.reactor.register_fd(
|
||||||
self.sock.fileno(), self.process_received)
|
self.sock.fileno(), self.process_received, self._do_send)
|
||||||
self.partial_data = self.send_buffer = b""
|
self.partial_data = self.send_buffer = b""
|
||||||
self.is_sending_data = False
|
self.is_blocking = False
|
||||||
|
self.blocking_count = 0
|
||||||
self.set_client_info("?", "New connection")
|
self.set_client_info("?", "New connection")
|
||||||
self.request_log = collections.deque([], REQUEST_LOG_SIZE)
|
self.request_log = collections.deque([], REQUEST_LOG_SIZE)
|
||||||
|
|
||||||
|
@ -259,33 +270,29 @@ class ClientConnection:
|
||||||
def send(self, data):
|
def send(self, data):
|
||||||
jmsg = json.dumps(data, separators=(',', ':'))
|
jmsg = json.dumps(data, separators=(',', ':'))
|
||||||
self.send_buffer += jmsg.encode() + b"\x03"
|
self.send_buffer += jmsg.encode() + b"\x03"
|
||||||
if not self.is_sending_data:
|
if not self.is_blocking:
|
||||||
self.is_sending_data = True
|
self._do_send()
|
||||||
self.reactor.register_callback(self._do_send)
|
|
||||||
|
|
||||||
def _do_send(self, eventtime):
|
def _do_send(self, eventtime=None):
|
||||||
retries = 10
|
if self.fd_handle is None:
|
||||||
while self.send_buffer:
|
return
|
||||||
try:
|
try:
|
||||||
sent = self.sock.send(self.send_buffer)
|
sent = self.sock.send(self.send_buffer)
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
if e.errno == errno.EBADF or e.errno == errno.EPIPE \
|
if e.errno not in [errno.EAGAIN, errno.EWOULDBLOCK]:
|
||||||
or not retries:
|
logging.info("webhooks: socket write error %d" % (self.uid,))
|
||||||
sent = 0
|
|
||||||
else:
|
|
||||||
retries -= 1
|
|
||||||
waketime = self.reactor.monotonic() + .001
|
|
||||||
self.reactor.pause(waketime)
|
|
||||||
continue
|
|
||||||
retries = 10
|
|
||||||
if sent > 0:
|
|
||||||
self.send_buffer = self.send_buffer[sent:]
|
|
||||||
else:
|
|
||||||
logging.info(
|
|
||||||
"webhooks: Error sending server data, closing socket")
|
|
||||||
self.close()
|
self.close()
|
||||||
break
|
return
|
||||||
self.is_sending_data = False
|
sent = 0
|
||||||
|
if sent < len(self.send_buffer):
|
||||||
|
if not self.is_blocking:
|
||||||
|
self.reactor.set_fd_wake(self.fd_handle, False, True)
|
||||||
|
self.is_blocking = True
|
||||||
|
self.blocking_count = 5
|
||||||
|
elif self.is_blocking:
|
||||||
|
self.reactor.set_fd_wake(self.fd_handle, True, False)
|
||||||
|
self.is_blocking = False
|
||||||
|
self.send_buffer = self.send_buffer[sent:]
|
||||||
|
|
||||||
class WebHooks:
|
class WebHooks:
|
||||||
def __init__(self, printer):
|
def __init__(self, printer):
|
||||||
|
@ -375,6 +382,9 @@ class WebHooks:
|
||||||
state_message, state = self.printer.get_state_message()
|
state_message, state = self.printer.get_state_message()
|
||||||
return {'state': state, 'state_message': state_message}
|
return {'state': state, 'state_message': state_message}
|
||||||
|
|
||||||
|
def stats(self, eventtime):
|
||||||
|
return self.sconn.stats(eventtime)
|
||||||
|
|
||||||
def call_remote_method(self, method, **kwargs):
|
def call_remote_method(self, method, **kwargs):
|
||||||
if method not in self._remote_methods:
|
if method not in self._remote_methods:
|
||||||
raise self.printer.command_error(
|
raise self.printer.command_error(
|
||||||
|
|
|
@ -125,6 +125,10 @@ callbacks.
|
||||||
|
|
||||||
The canboot directory contains code from:
|
The canboot directory contains code from:
|
||||||
https://github.com/Arksine/CanBoot
|
https://github.com/Arksine/CanBoot
|
||||||
revision ae687a404d0edb2724a084f78e565dbc7dd505aa. The Python module,
|
revision 870200826561b150137913d42fd7edc6515229ff. The Python module,
|
||||||
flash_can.py, is taken from the repo's scripts directory. It may be
|
flash_can.py, is taken from the repo's scripts directory. It may be
|
||||||
used to upload firmware to devices flashed with the CanBoot bootloader.
|
used to upload firmware to devices flashed with the CanBoot bootloader.
|
||||||
|
|
||||||
|
The can2040 directory contains code from:
|
||||||
|
https://github.com/KevinOConnor/can2040
|
||||||
|
revision 9ca095c939a48391de60dd353f0cd91999bb9257.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,79 @@
|
||||||
|
#ifndef _CAN2040_H
|
||||||
|
#define _CAN2040_H
|
||||||
|
|
||||||
|
#include <stdint.h> // uint32_t
|
||||||
|
|
||||||
|
struct can2040_msg {
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t dlc;
|
||||||
|
union {
|
||||||
|
uint8_t data[8];
|
||||||
|
uint32_t data32[2];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CAN2040_ID_RTR = 1<<30,
|
||||||
|
CAN2040_ID_EFF = 1<<31,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CAN2040_NOTIFY_RX = 1<<20,
|
||||||
|
CAN2040_NOTIFY_TX = 1<<21,
|
||||||
|
CAN2040_NOTIFY_ERROR = 1<<23,
|
||||||
|
};
|
||||||
|
struct can2040;
|
||||||
|
typedef void (*can2040_rx_cb)(struct can2040 *cd, uint32_t notify
|
||||||
|
, struct can2040_msg *msg);
|
||||||
|
|
||||||
|
void can2040_setup(struct can2040 *cd, uint32_t pio_num);
|
||||||
|
void can2040_callback_config(struct can2040 *cd, can2040_rx_cb rx_cb);
|
||||||
|
void can2040_start(struct can2040 *cd, uint32_t sys_clock, uint32_t bitrate
|
||||||
|
, uint32_t gpio_rx, uint32_t gpio_tx);
|
||||||
|
void can2040_shutdown(struct can2040 *cd);
|
||||||
|
void can2040_pio_irq_handler(struct can2040 *cd);
|
||||||
|
int can2040_check_transmit(struct can2040 *cd);
|
||||||
|
int can2040_transmit(struct can2040 *cd, struct can2040_msg *msg);
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* Internal definitions
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
struct can2040_bitunstuffer {
|
||||||
|
uint32_t stuffed_bits, count_stuff;
|
||||||
|
uint32_t unstuffed_bits, count_unstuff;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct can2040_transmit {
|
||||||
|
struct can2040_msg msg;
|
||||||
|
uint32_t crc, stuffed_words, stuffed_data[5];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct can2040 {
|
||||||
|
// Setup
|
||||||
|
uint32_t pio_num;
|
||||||
|
void *pio_hw;
|
||||||
|
uint32_t gpio_rx, gpio_tx;
|
||||||
|
can2040_rx_cb rx_cb;
|
||||||
|
|
||||||
|
// Bit unstuffing
|
||||||
|
struct can2040_bitunstuffer unstuf;
|
||||||
|
uint32_t raw_bit_count;
|
||||||
|
|
||||||
|
// Input data state
|
||||||
|
uint32_t parse_state;
|
||||||
|
uint32_t parse_crc;
|
||||||
|
struct can2040_msg parse_msg;
|
||||||
|
|
||||||
|
// Reporting
|
||||||
|
uint32_t report_state;
|
||||||
|
uint32_t report_eof_key;
|
||||||
|
|
||||||
|
// Transmits
|
||||||
|
uint32_t tx_state;
|
||||||
|
uint32_t tx_pull_pos, tx_push_pos;
|
||||||
|
struct can2040_transmit tx_queue[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // can2040.h
|
|
@ -496,9 +496,70 @@ class CanSocket:
|
||||||
self._loop.remove_reader(self.cansock.fileno())
|
self._loop.remove_reader(self.cansock.fileno())
|
||||||
self.cansock.close()
|
self.cansock.close()
|
||||||
|
|
||||||
|
class SerialSocket:
|
||||||
|
def __init__(self, loop: asyncio.AbstractEventLoop):
|
||||||
|
self._loop = loop
|
||||||
|
self.serial = self.serial_error = None
|
||||||
|
self.node = CanNode(0, self)
|
||||||
|
|
||||||
|
def _handle_response(self) -> None:
|
||||||
|
try:
|
||||||
|
data = self.serial.read(4096)
|
||||||
|
except self.serial_error as e:
|
||||||
|
logging.exception("Error on serial read")
|
||||||
|
self.close()
|
||||||
|
self.node.feed_data(data)
|
||||||
|
|
||||||
|
def send(self, can_id: int, payload: bytes = b"") -> None:
|
||||||
|
try:
|
||||||
|
self.serial.write(payload)
|
||||||
|
except self.serial_error as e:
|
||||||
|
logging.exception("Error on serial write")
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
async def run(self, intf: str, baud: int, fw_path: pathlib.Path) -> None:
|
||||||
|
if not fw_path.is_file():
|
||||||
|
raise FlashCanError("Invalid firmware path '%s'" % (fw_path))
|
||||||
|
import serial
|
||||||
|
self.serial_error = serial.SerialException
|
||||||
|
try:
|
||||||
|
serial_dev = serial.Serial(baudrate=baud, timeout=0,
|
||||||
|
exclusive=True)
|
||||||
|
serial_dev.port = intf
|
||||||
|
serial_dev.open()
|
||||||
|
except (OSError, IOError, self.serial_error) as e:
|
||||||
|
raise FlashCanError("Unable to open serial port: %s" % (e,))
|
||||||
|
self.serial = serial_dev
|
||||||
|
self._loop.add_reader(self.serial.fileno(), self._handle_response)
|
||||||
|
flasher = CanFlasher(self.node, fw_path)
|
||||||
|
try:
|
||||||
|
await flasher.connect_btl()
|
||||||
|
await flasher.send_file()
|
||||||
|
await flasher.verify_file()
|
||||||
|
finally:
|
||||||
|
# always attempt to send the complete command. If
|
||||||
|
# there is an error it will exit the bootloader
|
||||||
|
# unless comms were broken
|
||||||
|
await flasher.finish()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.serial is None:
|
||||||
|
return
|
||||||
|
self._loop.remove_reader(self.serial.fileno())
|
||||||
|
self.serial.close()
|
||||||
|
self.serial = None
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Can Bootloader Flash Utility")
|
description="Can Bootloader Flash Utility")
|
||||||
|
parser.add_argument(
|
||||||
|
"-d", "--device", metavar='<serial device>',
|
||||||
|
help="Serial Device"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-b", "--baud", default=250000, metavar='<baud rate>',
|
||||||
|
help="Serial baud rate"
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-i", "--interface", default="can0", metavar='<can interface>',
|
"-i", "--interface", default="can0", metavar='<can interface>',
|
||||||
help="Can Interface"
|
help="Can Interface"
|
||||||
|
@ -522,27 +583,37 @@ def main():
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if not args.verbose:
|
if not args.verbose:
|
||||||
logging.getLogger().setLevel(logging.CRITICAL)
|
logging.getLogger().setLevel(logging.ERROR)
|
||||||
intf = args.interface
|
intf = args.interface
|
||||||
fpath = pathlib.Path(args.firmware).expanduser().resolve()
|
fpath = pathlib.Path(args.firmware).expanduser().resolve()
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
|
iscan = args.device is None
|
||||||
|
sock = None
|
||||||
try:
|
try:
|
||||||
cansock = CanSocket(loop)
|
if iscan:
|
||||||
if args.query:
|
sock = CanSocket(loop)
|
||||||
loop.run_until_complete(cansock.run_query(intf))
|
if args.query:
|
||||||
|
loop.run_until_complete(sock.run_query(intf))
|
||||||
|
else:
|
||||||
|
if args.uuid is None:
|
||||||
|
raise FlashCanError(
|
||||||
|
"The 'uuid' option must be specified to flash a device"
|
||||||
|
)
|
||||||
|
uuid = int(args.uuid, 16)
|
||||||
|
loop.run_until_complete(sock.run(intf, uuid, fpath))
|
||||||
else:
|
else:
|
||||||
if args.uuid is None:
|
if args.device is None:
|
||||||
raise FlashCanError(
|
raise FlashCanError(
|
||||||
"The 'uuid' option must be specified to flash a device"
|
"The 'device' option must be specified to flash a device"
|
||||||
)
|
)
|
||||||
uuid = int(args.uuid, 16)
|
sock = SerialSocket(loop)
|
||||||
loop.run_until_complete(cansock.run(intf, uuid, fpath))
|
loop.run_until_complete(sock.run(args.device, args.baud, fpath))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Can Flash Error")
|
logging.exception("Can Flash Error")
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
finally:
|
finally:
|
||||||
if cansock is not None:
|
if sock is not None:
|
||||||
cansock.close()
|
sock.close()
|
||||||
if args.query:
|
if args.query:
|
||||||
output_line("Query Complete")
|
output_line("Query Complete")
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
// 4-byte checksum. Therefore code size cannot exceed 252 bytes.
|
// 4-byte checksum. Therefore code size cannot exceed 252 bytes.
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#include "pico/asm_helper.S"
|
//#include "pico/asm_helper.S"
|
||||||
#include "hardware/regs/addressmap.h"
|
#include "hardware/regs/addressmap.h"
|
||||||
#include "hardware/regs/ssi.h"
|
#include "hardware/regs/ssi.h"
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,16 @@
|
||||||
|
diff --git a/lib/rp2040/boot_stage2/boot2_generic_03h.S b/lib/rp2040/boot_stage2/boot2_generic_03h.S
|
||||||
|
index a10e66abd..cc7e4fbc7 100644
|
||||||
|
--- a/lib/rp2040/boot_stage2/boot2_generic_03h.S
|
||||||
|
+++ b/lib/rp2040/boot_stage2/boot2_generic_03h.S
|
||||||
|
@@ -16,7 +16,7 @@
|
||||||
|
// 4-byte checksum. Therefore code size cannot exceed 252 bytes.
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-#include "pico/asm_helper.S"
|
||||||
|
+//#include "pico/asm_helper.S"
|
||||||
|
#include "hardware/regs/addressmap.h"
|
||||||
|
#include "hardware/regs/ssi.h"
|
||||||
|
|
||||||
diff --git a/lib/rp2040/boot_stage2/boot2_w25q080.S b/lib/rp2040/boot_stage2/boot2_w25q080.S
|
diff --git a/lib/rp2040/boot_stage2/boot2_w25q080.S b/lib/rp2040/boot_stage2/boot2_w25q080.S
|
||||||
index ad3238e2..8fb3def4 100644
|
index ad3238e2..8fb3def4 100644
|
||||||
--- a/lib/rp2040/boot_stage2/boot2_w25q080.S
|
--- a/lib/rp2040/boot_stage2/boot2_w25q080.S
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python3
|
||||||
# Tool to enter a USB bootloader and flash Klipper
|
# Tool to enter a USB bootloader and flash Klipper
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
|
# Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
@ -27,6 +27,8 @@ def enter_bootloader(device):
|
||||||
# Translate a serial device name to a stable serial name in /dev/serial/by-path/
|
# Translate a serial device name to a stable serial name in /dev/serial/by-path/
|
||||||
def translate_serial_to_tty(device):
|
def translate_serial_to_tty(device):
|
||||||
ttyname = os.path.realpath(device)
|
ttyname = os.path.realpath(device)
|
||||||
|
if not os.path.exists('/dev/serial/by-path/'):
|
||||||
|
raise error("Unable to find serial 'by-path' folder")
|
||||||
for fname in os.listdir('/dev/serial/by-path/'):
|
for fname in os.listdir('/dev/serial/by-path/'):
|
||||||
fname = '/dev/serial/by-path/' + fname
|
fname = '/dev/serial/by-path/' + fname
|
||||||
if os.path.realpath(fname) == ttyname:
|
if os.path.realpath(fname) == ttyname:
|
||||||
|
@ -71,6 +73,42 @@ def wait_path(path, alt_path=None):
|
||||||
if cur_time > end_time:
|
if cur_time > end_time:
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
CANBOOT_ID ="1d50:6177"
|
||||||
|
|
||||||
|
def detect_canboot(devpath):
|
||||||
|
usbdir = os.path.dirname(devpath)
|
||||||
|
try:
|
||||||
|
with open(os.path.join(usbdir, "idVendor")) as f:
|
||||||
|
vid = f.read().strip().lower()
|
||||||
|
with open(os.path.join(usbdir, "idProduct")) as f:
|
||||||
|
pid = f.read().strip().lower()
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
usbid = "%s:%s" % (vid, pid)
|
||||||
|
return usbid == CANBOOT_ID
|
||||||
|
|
||||||
|
def call_flashcan(device, binfile):
|
||||||
|
try:
|
||||||
|
import serial
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
sys.stderr.write(
|
||||||
|
"Python's pyserial module is required to update. Install\n"
|
||||||
|
"with the following command:\n"
|
||||||
|
" %s -m pip install pyserial\n\n" % (sys.executable,)
|
||||||
|
)
|
||||||
|
sys.exit(-1)
|
||||||
|
args = [sys.executable, "lib/canboot/flash_can.py", "-d",
|
||||||
|
device, "-f", binfile]
|
||||||
|
sys.stderr.write(" ".join(args) + '\n\n')
|
||||||
|
res = subprocess.call(args)
|
||||||
|
if res != 0:
|
||||||
|
sys.stderr.write("Error running flash_can.py\n")
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
def flash_canboot(options, binfile):
|
||||||
|
ttyname, pathname = translate_serial_to_tty(options.device)
|
||||||
|
call_flashcan(pathname, binfile)
|
||||||
|
|
||||||
# Flash via a call to bossac
|
# Flash via a call to bossac
|
||||||
def flash_bossac(device, binfile, extra_flags=[]):
|
def flash_bossac(device, binfile, extra_flags=[]):
|
||||||
ttyname, pathname = translate_serial_to_tty(device)
|
ttyname, pathname = translate_serial_to_tty(device)
|
||||||
|
@ -108,10 +146,14 @@ def flash_dfuutil(device, binfile, extra_flags=[], sudo=True):
|
||||||
if hexfmt_r.match(device.strip()):
|
if hexfmt_r.match(device.strip()):
|
||||||
call_dfuutil(["-d", ","+device.strip()] + extra_flags, binfile, sudo)
|
call_dfuutil(["-d", ","+device.strip()] + extra_flags, binfile, sudo)
|
||||||
return
|
return
|
||||||
|
ttyname, serbypath = translate_serial_to_tty(device)
|
||||||
buspath, devpath = translate_serial_to_usb_path(device)
|
buspath, devpath = translate_serial_to_usb_path(device)
|
||||||
enter_bootloader(device)
|
enter_bootloader(device)
|
||||||
pathname = wait_path(devpath)
|
pathname = wait_path(devpath)
|
||||||
call_dfuutil(["-p", buspath] + extra_flags, binfile, sudo)
|
if detect_canboot(devpath):
|
||||||
|
call_flashcan(serbypath, binfile)
|
||||||
|
else:
|
||||||
|
call_dfuutil(["-p", buspath] + extra_flags, binfile, sudo)
|
||||||
|
|
||||||
def call_hidflash(binfile, sudo):
|
def call_hidflash(binfile, sudo):
|
||||||
args = ["lib/hidflash/hid-flash", binfile]
|
args = ["lib/hidflash/hid-flash", binfile]
|
||||||
|
@ -128,10 +170,40 @@ def flash_hidflash(device, binfile, sudo=True):
|
||||||
if hexfmt_r.match(device.strip()):
|
if hexfmt_r.match(device.strip()):
|
||||||
call_hidflash(binfile, sudo)
|
call_hidflash(binfile, sudo)
|
||||||
return
|
return
|
||||||
|
ttyname, serbypath = translate_serial_to_tty(device)
|
||||||
buspath, devpath = translate_serial_to_usb_path(device)
|
buspath, devpath = translate_serial_to_usb_path(device)
|
||||||
enter_bootloader(device)
|
enter_bootloader(device)
|
||||||
pathname = wait_path(devpath)
|
pathname = wait_path(devpath)
|
||||||
call_hidflash(binfile, sudo)
|
if detect_canboot(devpath):
|
||||||
|
call_flashcan(serbypath, binfile)
|
||||||
|
else:
|
||||||
|
call_hidflash(binfile, sudo)
|
||||||
|
|
||||||
|
# Call Klipper modified "picoboot"
|
||||||
|
def call_picoboot(bus, addr, binfile, sudo):
|
||||||
|
args = ["lib/rp2040_flash/rp2040_flash", binfile]
|
||||||
|
if bus is not None:
|
||||||
|
args.extend([bus, addr])
|
||||||
|
if sudo:
|
||||||
|
args.insert(0, "sudo")
|
||||||
|
sys.stderr.write(" ".join(args) + '\n\n')
|
||||||
|
res = subprocess.call(args)
|
||||||
|
if res != 0:
|
||||||
|
raise error("Error running rp2040_flash")
|
||||||
|
|
||||||
|
# Flash via Klipper modified "picoboot"
|
||||||
|
def flash_picoboot(device, binfile, sudo):
|
||||||
|
buspath, devpath = translate_serial_to_usb_path(device)
|
||||||
|
# We need one level up to get access to busnum/devnum files
|
||||||
|
usbdir = os.path.dirname(devpath)
|
||||||
|
enter_bootloader(device)
|
||||||
|
wait_path(usbdir)
|
||||||
|
with open(usbdir + "/busnum") as f:
|
||||||
|
bus = f.read().strip()
|
||||||
|
with open(usbdir + "/devnum") as f:
|
||||||
|
addr = f.read().strip()
|
||||||
|
call_picoboot(bus, addr, binfile, sudo)
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Device specific helpers
|
# Device specific helpers
|
||||||
|
@ -162,22 +234,6 @@ def flash_atsamd(options, binfile):
|
||||||
options.device, str(e)))
|
options.device, str(e)))
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
# Look for an rp2040 and flash it with rp2040_flash.
|
|
||||||
def rp2040_flash(devpath, binfile, sudo):
|
|
||||||
args = ["lib/rp2040_flash/rp2040_flash", binfile]
|
|
||||||
if len(devpath) > 0:
|
|
||||||
with open(devpath + "/busnum") as f:
|
|
||||||
bus = f.read().strip()
|
|
||||||
with open(devpath + "/devnum") as f:
|
|
||||||
addr = f.read().strip()
|
|
||||||
args += [bus, addr]
|
|
||||||
sys.stderr.write(" ".join(args) + '\n\n')
|
|
||||||
if sudo:
|
|
||||||
args.insert(0, "sudo")
|
|
||||||
res = subprocess.call(args)
|
|
||||||
if res != 0:
|
|
||||||
raise error("Error running rp2040_flash")
|
|
||||||
|
|
||||||
SMOOTHIE_HELP = """
|
SMOOTHIE_HELP = """
|
||||||
Failed to flash to %s: %s
|
Failed to flash to %s: %s
|
||||||
|
|
||||||
|
@ -259,27 +315,22 @@ def flash_stm32f4(options, binfile):
|
||||||
RP2040_HELP = """
|
RP2040_HELP = """
|
||||||
Failed to flash to %s: %s
|
Failed to flash to %s: %s
|
||||||
|
|
||||||
If the device is already in bootloader mode, use 'first' as FLASH_DEVICE.
|
If the device is already in bootloader mode it can be flashed with the
|
||||||
This will use rp2040_flash to flash the first available rp2040.
|
following command:
|
||||||
|
make flash FLASH_DEVICE=2e8a:0003
|
||||||
|
|
||||||
Alternatively, one can flash rp2040 boards like the Pico by manually
|
Alternatively, one can flash rp2040 boards like the Pico by manually
|
||||||
entering bootloader mode(hold bootsel button during powerup), mount the
|
entering bootloader mode(hold bootsel button during powerup), mount the
|
||||||
device as a usb drive, and copy klipper.uf2 to the device.
|
device as a usb drive, and copy klipper.uf2 to the device.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def flash_rp2040(options, binfile):
|
def flash_rp2040(options, binfile):
|
||||||
try:
|
try:
|
||||||
if options.device.lower() == "first":
|
if options.device.lower() == "2e8a:0003":
|
||||||
rp2040_flash("", binfile, options.sudo)
|
call_picoboot(None, None, binfile, options.sudo)
|
||||||
return
|
else:
|
||||||
|
flash_picoboot(options.device, binfile, options.sudo)
|
||||||
buspath, devpath = translate_serial_to_usb_path(options.device)
|
|
||||||
# We need one level up to get access to busnum/devnum files
|
|
||||||
devpath = os.path.dirname(devpath)
|
|
||||||
enter_bootloader(options.device)
|
|
||||||
wait_path(devpath)
|
|
||||||
rp2040_flash(devpath, binfile, options.sudo)
|
|
||||||
|
|
||||||
except error as e:
|
except error as e:
|
||||||
sys.stderr.write(RP2040_HELP % (options.device, str(e)))
|
sys.stderr.write(RP2040_HELP % (options.device, str(e)))
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
@ -288,7 +339,8 @@ MCUTYPES = {
|
||||||
'sam3': flash_atsam3, 'sam4': flash_atsam4, 'samd': flash_atsamd,
|
'sam3': flash_atsam3, 'sam4': flash_atsam4, 'samd': flash_atsamd,
|
||||||
'same70': flash_atsam4, 'lpc176': flash_lpc176x, 'stm32f103': flash_stm32f1,
|
'same70': flash_atsam4, 'lpc176': flash_lpc176x, 'stm32f103': flash_stm32f1,
|
||||||
'stm32f4': flash_stm32f4, 'stm32f042': flash_stm32f4,
|
'stm32f4': flash_stm32f4, 'stm32f042': flash_stm32f4,
|
||||||
'stm32f072': flash_stm32f4, 'rp2040': flash_rp2040
|
'stm32f072': flash_stm32f4, 'stm32g0b1': flash_stm32f4,
|
||||||
|
'stm32h7': flash_stm32f4, 'rp2040': flash_rp2040
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -181,14 +181,48 @@ def plot_system(data):
|
||||||
ax1.grid(True)
|
ax1.grid(True)
|
||||||
return fig
|
return fig
|
||||||
|
|
||||||
def plot_frequency(data, mcu):
|
def plot_mcu_frequencies(data):
|
||||||
all_keys = {}
|
all_keys = {}
|
||||||
for d in data:
|
for d in data:
|
||||||
all_keys.update(d)
|
all_keys.update(d)
|
||||||
one_mcu = mcu is not None
|
|
||||||
graph_keys = { key: ([], []) for key in all_keys
|
graph_keys = { key: ([], []) for key in all_keys
|
||||||
if (key in ("freq", "adj") or (not one_mcu and (
|
if (key in ("freq", "adj")
|
||||||
key.endswith(":freq") or key.endswith(":adj")))) }
|
or (key.endswith(":freq") or key.endswith(":adj"))) }
|
||||||
|
for d in data:
|
||||||
|
st = datetime.datetime.utcfromtimestamp(d['#sampletime'])
|
||||||
|
for key, (times, values) in graph_keys.items():
|
||||||
|
val = d.get(key)
|
||||||
|
if val not in (None, '0', '1'):
|
||||||
|
times.append(st)
|
||||||
|
values.append(float(val))
|
||||||
|
est_mhz = { key: round((sum(values)/len(values)) / 1000000.)
|
||||||
|
for key, (times, values) in graph_keys.items() }
|
||||||
|
|
||||||
|
# Build plot
|
||||||
|
fig, ax1 = matplotlib.pyplot.subplots()
|
||||||
|
ax1.set_title("MCU frequencies")
|
||||||
|
ax1.set_xlabel('Time')
|
||||||
|
ax1.set_ylabel('Microsecond deviation')
|
||||||
|
for key in sorted(graph_keys):
|
||||||
|
times, values = graph_keys[key]
|
||||||
|
mhz = est_mhz[key]
|
||||||
|
label = "%s(%dMhz)" % (key, mhz)
|
||||||
|
hz = mhz * 1000000.
|
||||||
|
ax1.plot_date(times, [(v - hz)/mhz for v in values], '.', label=label)
|
||||||
|
fontP = matplotlib.font_manager.FontProperties()
|
||||||
|
fontP.set_size('x-small')
|
||||||
|
ax1.legend(loc='best', prop=fontP)
|
||||||
|
ax1.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%H:%M'))
|
||||||
|
ax1.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter('%d'))
|
||||||
|
ax1.grid(True)
|
||||||
|
return fig
|
||||||
|
|
||||||
|
def plot_mcu_frequency(data, mcu):
|
||||||
|
all_keys = {}
|
||||||
|
for d in data:
|
||||||
|
all_keys.update(d)
|
||||||
|
graph_keys = { key: ([], []) for key in all_keys
|
||||||
|
if key in ("freq", "adj") }
|
||||||
for d in data:
|
for d in data:
|
||||||
st = datetime.datetime.utcfromtimestamp(d['#sampletime'])
|
st = datetime.datetime.utcfromtimestamp(d['#sampletime'])
|
||||||
for key, (times, values) in graph_keys.items():
|
for key, (times, values) in graph_keys.items():
|
||||||
|
@ -199,10 +233,7 @@ def plot_frequency(data, mcu):
|
||||||
|
|
||||||
# Build plot
|
# Build plot
|
||||||
fig, ax1 = matplotlib.pyplot.subplots()
|
fig, ax1 = matplotlib.pyplot.subplots()
|
||||||
if one_mcu:
|
ax1.set_title("MCU '%s' frequency" % (mcu,))
|
||||||
ax1.set_title("MCU '%s' frequency" % (mcu,))
|
|
||||||
else:
|
|
||||||
ax1.set_title("MCU frequency")
|
|
||||||
ax1.set_xlabel('Time')
|
ax1.set_xlabel('Time')
|
||||||
ax1.set_ylabel('Frequency')
|
ax1.set_ylabel('Frequency')
|
||||||
for key in sorted(graph_keys):
|
for key in sorted(graph_keys):
|
||||||
|
@ -286,7 +317,10 @@ def main():
|
||||||
if options.heater is not None:
|
if options.heater is not None:
|
||||||
fig = plot_temperature(data, options.heater)
|
fig = plot_temperature(data, options.heater)
|
||||||
elif options.frequency:
|
elif options.frequency:
|
||||||
fig = plot_frequency(data, options.mcu)
|
if options.mcu is not None:
|
||||||
|
fig = plot_mcu_frequency(data, options.mcu)
|
||||||
|
else:
|
||||||
|
fig = plot_mcu_frequencies(data)
|
||||||
elif options.system:
|
elif options.system:
|
||||||
fig = plot_system(data)
|
fig = plot_system(data)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -20,7 +20,7 @@ install_packages()
|
||||||
PKGLIST="${PKGLIST} avrdude gcc-avr binutils-avr avr-libc"
|
PKGLIST="${PKGLIST} avrdude gcc-avr binutils-avr avr-libc"
|
||||||
# ARM chip installation and building
|
# ARM chip installation and building
|
||||||
PKGLIST="${PKGLIST} stm32flash libnewlib-arm-none-eabi"
|
PKGLIST="${PKGLIST} stm32flash libnewlib-arm-none-eabi"
|
||||||
PKGLIST="${PKGLIST} gcc-arm-none-eabi binutils-arm-none-eabi libusb-1.0"
|
PKGLIST="${PKGLIST} gcc-arm-none-eabi binutils-arm-none-eabi libusb-1.0 pkg-config"
|
||||||
|
|
||||||
# Update system package info
|
# Update system package info
|
||||||
report_status "Running apt-get update..."
|
report_status "Running apt-get update..."
|
||||||
|
|
|
@ -65,6 +65,16 @@ BOARD_DEFS = {
|
||||||
'mcu': 'stm32h743xx',
|
'mcu': 'stm32h743xx',
|
||||||
'spi_bus': 'spi3a',
|
'spi_bus': 'spi3a',
|
||||||
'cs_pin': 'PA15'
|
'cs_pin': 'PA15'
|
||||||
|
},
|
||||||
|
'monster8': {
|
||||||
|
'mcu': "stm32f407xx",
|
||||||
|
'spi_bus': "spi3a",
|
||||||
|
"cs_pin": "PC9"
|
||||||
|
},
|
||||||
|
'fly-gemini-v2': {
|
||||||
|
'mcu': "stm32f405xx",
|
||||||
|
'spi_bus': "spi1",
|
||||||
|
"cs_pin": "PA4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +109,9 @@ BOARD_ALIASES = {
|
||||||
'mks-robin-e3d': BOARD_DEFS['mks-robin-e3'],
|
'mks-robin-e3d': BOARD_DEFS['mks-robin-e3'],
|
||||||
'fysetc-spider-v1': BOARD_DEFS['fysetc-spider'],
|
'fysetc-spider-v1': BOARD_DEFS['fysetc-spider'],
|
||||||
'fysetc-s6-v1.2': BOARD_DEFS['fysetc-spider'],
|
'fysetc-s6-v1.2': BOARD_DEFS['fysetc-spider'],
|
||||||
'fysetc-s6-v2': BOARD_DEFS['fysetc-spider']
|
'fysetc-s6-v2': BOARD_DEFS['fysetc-spider'],
|
||||||
|
'monster8': BOARD_DEFS['monster8'],
|
||||||
|
'robin_v3': BOARD_DEFS['monster8']
|
||||||
}
|
}
|
||||||
|
|
||||||
def list_boards():
|
def list_boards():
|
||||||
|
|
31
src/Kconfig
31
src/Kconfig
|
@ -42,6 +42,8 @@ source "src/linux/Kconfig"
|
||||||
source "src/simulator/Kconfig"
|
source "src/simulator/Kconfig"
|
||||||
|
|
||||||
# Generic configuration options for serial ports
|
# Generic configuration options for serial ports
|
||||||
|
config SERIAL
|
||||||
|
bool
|
||||||
config SERIAL_BAUD
|
config SERIAL_BAUD
|
||||||
depends on SERIAL
|
depends on SERIAL
|
||||||
int "Baud rate for serial port" if LOW_LEVEL_OPTIONS
|
int "Baud rate for serial port" if LOW_LEVEL_OPTIONS
|
||||||
|
@ -51,28 +53,49 @@ config SERIAL_BAUD
|
||||||
to 250000. Read the FAQ before changing this value.
|
to 250000. Read the FAQ before changing this value.
|
||||||
|
|
||||||
# Generic configuration options for USB
|
# Generic configuration options for USB
|
||||||
|
config USBSERIAL
|
||||||
|
bool
|
||||||
|
config USBCANBUS
|
||||||
|
bool
|
||||||
|
config USB
|
||||||
|
bool
|
||||||
|
default y if USBSERIAL || USBCANBUS
|
||||||
config USB_VENDOR_ID
|
config USB_VENDOR_ID
|
||||||
default 0x1d50
|
default 0x1d50
|
||||||
config USB_DEVICE_ID
|
config USB_DEVICE_ID
|
||||||
default 0x614e
|
default 0x614e
|
||||||
config USB_SERIAL_NUMBER_CHIPID
|
config USB_SERIAL_NUMBER_CHIPID
|
||||||
depends on HAVE_CHIPID
|
depends on USB && HAVE_CHIPID
|
||||||
default y
|
default y
|
||||||
config USB_SERIAL_NUMBER
|
config USB_SERIAL_NUMBER
|
||||||
default "12345"
|
default "12345"
|
||||||
|
|
||||||
menu "USB ids"
|
menu "USB ids"
|
||||||
depends on USBSERIAL && LOW_LEVEL_OPTIONS
|
depends on USB && LOW_LEVEL_OPTIONS
|
||||||
config USB_VENDOR_ID
|
config USB_VENDOR_ID
|
||||||
hex "USB vendor ID"
|
hex "USB vendor ID" if USBSERIAL
|
||||||
config USB_DEVICE_ID
|
config USB_DEVICE_ID
|
||||||
hex "USB device ID"
|
hex "USB device ID" if USBSERIAL
|
||||||
config USB_SERIAL_NUMBER_CHIPID
|
config USB_SERIAL_NUMBER_CHIPID
|
||||||
bool "USB serial number from CHIPID" if HAVE_CHIPID
|
bool "USB serial number from CHIPID" if HAVE_CHIPID
|
||||||
config USB_SERIAL_NUMBER
|
config USB_SERIAL_NUMBER
|
||||||
string "USB serial number" if !USB_SERIAL_NUMBER_CHIPID
|
string "USB serial number" if !USB_SERIAL_NUMBER_CHIPID
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
|
# Generic configuration options for CANbus
|
||||||
|
config CANSERIAL
|
||||||
|
bool
|
||||||
|
config CANBUS
|
||||||
|
bool
|
||||||
|
default y if CANSERIAL || USBCANBUS
|
||||||
|
config CANBUS_FREQUENCY
|
||||||
|
int "CAN bus speed" if LOW_LEVEL_OPTIONS && CANBUS
|
||||||
|
default 500000
|
||||||
|
config CANBUS_FILTER
|
||||||
|
bool
|
||||||
|
default y if CANSERIAL
|
||||||
|
|
||||||
|
# Support setting gpio state at startup
|
||||||
config INITIAL_PINS
|
config INITIAL_PINS
|
||||||
string "GPIO pins to set at micro-controller startup"
|
string "GPIO pins to set at micro-controller startup"
|
||||||
depends on LOW_LEVEL_OPTIONS
|
depends on LOW_LEVEL_OPTIONS
|
||||||
|
|
|
@ -9,5 +9,6 @@ src-$(CONFIG_HAVE_GPIO_I2C) += i2ccmds.c
|
||||||
src-$(CONFIG_HAVE_GPIO_UART) += uartcmds.c
|
src-$(CONFIG_HAVE_GPIO_UART) += uartcmds.c
|
||||||
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += pwmcmds.c
|
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += pwmcmds.c
|
||||||
bb-src-$(CONFIG_HAVE_GPIO_SPI) := spi_software.c sensor_adxl345.c sensor_angle.c
|
bb-src-$(CONFIG_HAVE_GPIO_SPI) := spi_software.c sensor_adxl345.c sensor_angle.c
|
||||||
|
bb-src-$(CONFIG_HAVE_GPIO_I2C) += sensor_mpu9250.c
|
||||||
src-$(CONFIG_HAVE_GPIO_BITBANGING) += $(bb-src-y) lcd_st7920.c lcd_hd44780.c \
|
src-$(CONFIG_HAVE_GPIO_BITBANGING) += $(bb-src-y) lcd_st7920.c lcd_hd44780.c \
|
||||||
buttons.c tmcuart.c neopixel.c pulse_counter.c
|
buttons.c tmcuart.c neopixel.c pulse_counter.c
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue