419 Commits

Author SHA1 Message Date
Kevin O'Connor
ad0e9da00e github: Add a stale ticket tracker for github PRs
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2025-02-02 18:18:36 -05:00
John
0114d72a6c config: Update generic-creality-v4.2.7.cfg (#6790)
Corrected Filament Runout Sensor Pin (as per schematic - see https://github.com/LeeOtts/Ender3v2-Klipper-Configs/blob/main/Creality.4.2.7.-.Schematic.28-5-22-1.pdf )

Signed-off-by: John Minor <theminor@duck.com>
2025-01-24 21:46:08 -05:00
Pedro Lamas
8c1037ef1b screws_tilt_adjust: initialize status result as a dictionary
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
2025-01-24 19:13:36 -05:00
Konstantin Koch
ed796fcfaa ads1x1x: added support for ADC chip (#6584)
Added a temperature sensor configuration for ADS1103, ADS1104, ADS1105, ADS1113, ADS1114 and ADS1115 chips that can be used to add Analog to Digital Conversion capability to machines that don't have that on their own. Like Raspberry Pi's or if more analog input pins are needed than the chip provides like for RP2040. Generally they can be used for any analog input, but the typical use case is for temperature measurement. This code also has been written with temperature measurement in mind and not as a general ADC.

Signed-off-by: Konstantin Koch <korsarnek@gmail.com>
Signed-off-by: Jack Wakefield <jackwakefield@protonmail.com>
2025-01-21 19:10:39 -05:00
Kevin O'Connor
6ab253366c force_move: Use strings for axes to clear in clear_homing_state()
Pass a string such as "xyz" to kin.clear_homing_state().  This makes
the parameter a little less cryptic.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2025-01-21 18:58:23 -05:00
Kevin O'Connor
4aa550837f toolhead: Pass set_position() homing_axes parameter as a string
Use strings such as "xyz" to specify which axes are to be considered
homing during a set_position() call.  This makes the parameter a
little less cryptic.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2025-01-21 18:58:23 -05:00
Kevin O'Connor
c72d73ec45 stepper_enable: Directly call clear_homing_state() on motor off event
Call clear_homing_state() on each motor off event.  This simplifies
the kinematic classes as they no longer need to register and handle
the motor_off event.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2025-01-21 18:58:23 -05:00
Kevin O'Connor
5fe333934d docs: Add a "Professional Services" link to Contacts page
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2025-01-21 18:54:33 -05:00
Kevin O'Connor
9a06d2b7e8 docs: Improve wording of main Klipper page
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2025-01-21 18:54:00 -05:00
Kevin O'Connor
cf3b0475da tmc2240: Allow the slope_control field to be configured via printer.cfg
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2025-01-10 12:29:41 -05:00
Kevin O'Connor
aae29ba48b heaters: Disable heater if it appears main thread has stopped updating
Update the heating code to periodically check that the main thread is
operating properly.  This is a mitigation for some rare cases where
the main thread may lockup while the background heater updating code
continues to run.  The goal is to detect these rare failures and
disable future heater updates.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2025-01-10 12:22:49 -05:00
Kevin O'Connor
485c8f2ef0 lib: Update can2040 to v1.7.0
This provides improved support on rp2350 chips.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2025-01-10 12:20:13 -05:00
Dennis Marttinen
7083879700 force_move: Implement CLEAR for SET_KINEMATIC_POSITION (#6262)
`CLEAR` clears the homing status (resets the axis limits) without turning off
the motors. This is particularly useful when implementing safe Z homing in
`[homing_override]` on printers with multiple independent Z steppers (where
`FORCE_MOVE` can't be used).

Signed-off-by: Dennis Marttinen <twelho@welho.tech>
2025-01-10 10:41:09 -05:00
Kevin O'Connor
9ca71d8608 github: Change to upload-artifact@v4
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2025-01-09 20:43:54 -05:00
Alexander Bazarov
8a3d2afd79 config: Config for Geeetech A-series printers: A10/M/T and A20/M/T (#6767)
Based on few configs found on the discourse forum, facebook groups.
In addition, using official schematics from:
https://www.geeetech.com/download.html
https://github.com/Geeetech3D/Diagram/files/8199212/GT2560V4.1BSCHA20T.pdf

Contains macros for filament mixing based on:
https://klipper.discourse.group/t/mixing-color-support/2246/12
https://klipper.discourse.group/t/mixing-hotend-m163-emulation/11423/2

Signed-off-by: Alexander Bazarov <oss@bazarov.dev>
2025-01-03 09:32:07 -05:00
KrauTech
80d185c94c z_tilt: return done when reties is 0 (#6766)
Signed-off-by: Chris Krause <krautech3d@gmail.com>
2024-12-19 15:24:44 -05:00
Kevin O'Connor
cb13ee76ff docs: Document the QUAD_GANTRY_LEVEL command in G-Codes.md
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-12-19 11:52:31 -05:00
Kevin O'Connor
a2a91654a9 docs: Document Z_TILT_ADJUST RETRIES and RETRY_TOLERNACE options in G-Codes.md
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-12-19 11:49:16 -05:00
Kevin O'Connor
383b83d788 Kconfig: Simplify WANT_XXX definitions
Use WANT_ADXL345 and WANT_MPU9250 instead of WANT_SENSOR_ADXL345 and
WANT_SENSOR_MPU9250.  This makes these definitions similar to the
other accelerometer defintions.

Order menu so accelerometers are close to each other in the menu.

Simplify Makefile as Kconfig already assures a symbol will only be
defined if its dependencies are met.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-12-12 14:46:37 -05:00
Timofey Titovets
2b9e041a86 angle: mt6826s added support
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-12-12 14:28:45 -05:00
Timofey Titovets
90c1b82baa angle: mt6816 added support
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-12-12 14:28:45 -05:00
Timofey Titovets
896343d943 ar100: disable angle sensors code in CI
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-12-12 14:28:45 -05:00
Timofey Titovets
1499bfa489 Kconfig: split sensors
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-12-12 14:28:45 -05:00
BIGTREETECH
b7233d1197 lib: fix rp2040_flash (#6760)
Signed-off-by: Alan.Ma from BigTreeTech <tech@biqu3d.com>
2024-12-06 13:41:22 -05:00
Dmitry Butyugin
16b4b6b302 resonance_tester: Added a new sweeping_vibrations resonance test method (#6723)
This adds a new resonance test method that can help if a user has some mechanical problems with the printer.

Signed-off-by: Dmitry Butyugin <dmbutyugin@google.com>
2024-12-05 21:54:26 -05:00
Liam Powell
7f89668d6c tmc2240: Correct maximum TMC2240 UART address. (#6757)
Signed-off-by: Liam Powell <liam@liampwll.com>
2024-12-02 13:30:57 -05:00
Alexander Bazarov
aecb29d2b0 display: Add support for AIP31068 based displays (#6639)
display: Add support for `AIP31068` based displays
2024-12-02 13:23:46 -05:00
Kevin O'Connor
9ce631e8d1 klippy: Fix missing default parameter of invoke_async_shutdown()
Allow invoke_async_shutdown() to be called with just one parameter.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-12-02 12:51:51 -05:00
Kevin O'Connor
2165c90011 gcode: Improve checksum detection in get_raw_command_parameters()
Only consider a trailing '*' to indicate a checksum if the remainder
of the string is a number.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-12-01 14:16:13 -05:00
Kevin O'Connor
a6df541104 gcode: Some optimizations to get_raw_command_parameters()
Add some minor optimizations to the get_raw_command_parameters() code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-12-01 14:16:13 -05:00
Kevin O'Connor
62325d4a35 gcode: Use the same M117/M118 fixup for M23
The M23 command has similar requirements for extracting the full
parameter string that M117/M118 have.  Use the same code for those
fixups.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-12-01 14:16:13 -05:00
Kevin O'Connor
03068b48fe gcode: Fixup M117/M118 command identification in cmd_default()
Alter gcmd._command in cmd_default if the special M117/M118 handling
is detected.  This avoids having to recheck for this condition in
get_raw_command_parameters().

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-12-01 14:16:13 -05:00
Kevin O'Connor
d45b9c92d8 gcode: Improve handling of extended g-code commands with '*;#' characters
The g-code command parser did not allow three characters to be passed
as parameters to commands (asterisk, semicolon, pound sign).  Rework
the parsing code to better leverage the python shlex package so that
these characters can be supported.

In particular, this should allow better support for printing g-code
files that have unusual characters in the filename.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-12-01 14:16:13 -05:00
Kevin O'Connor
49205f92ff gcode: Don't silently discard characters inside a command name
Don't silently drop leading numbers and unusual characters at the
start of a command - for example, don't interpret '99M88' as 'M88'.

Don't silently drop spaces in a command - for example, don't interpret
"M 101" as the command "M101".  Doing so will cause other parts of the
code (such as get_raw_command_parameters() ) to not work properly.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-12-01 14:16:13 -05:00
Kevin O'Connor
5493c60373 gcode: Validate extended g-code command names
Extended g-code command names may only contain A-Z, 0-9, and
underscore, and the first two characters may not be digits.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-12-01 13:15:08 -05:00
Kevin O'Connor
847331260c toolhead: Remove arbitrary constants controlling junction deviation
When calculating the junction speed between two moves the code checked
for angles greater than 0.999999 or less than -0.999999 to avoid math
issues (sqrt of a negative number and/or divide by zero).  However,
these arbitrary constants could unnecessarily pessimize junction
speeds when angles are close to 180 or 0 degrees.

Change the code to explicitly check for negative numbers during sqrt
and to explicilty check for zero values prior to division.  This
simplifies the code and avoids unnecessarily reducing some junction
speeds.

Signed-off-by: Dmitry Butyugin <dmbutyugin@google.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-29 18:09:29 -05:00
Kevin O'Connor
8291788f40 toolhead: Use delta_v2 when calculating centripetal force
As a minor math optimization, it's possible to calculate:
  .5 * self.move_d * self.accel * tan_theta_d2
using:
  self.delta_v2 * .25 * tan_theta_d2
because self.delta_v2 is "2. * self.move_d * self.accel".

Signed-off-by: Dmitry Butyugin <dmbutyugin@google.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-29 17:41:37 -05:00
Jessica Hunt
a18c74be05 rp2040: Add spi0_gpio4_gpio3_gpio2 bus to support fysetc PITB V2 (#6683)
The Fysetc PITB V2 board uses a spi bus config that is supported by the
RP2040 chip, but not klipper, so this adds the relevant config to the file
to allow you to run the tmc5160's on the board via hardware SPI.  This
resolves the issue of software spi not working on this board, which I
was unable to fully understand.

I have also seen other users encounter similar bus config issues with
the rp2040 setting up things like accelerometers and such with this
pin layout.

As requested, this also uses the new convention for spi bus naming, while
maintaining the old bus names for compatibility.

Signed-off-by: Jessica Hunt <hunt.jessica@proton.me>
2024-11-27 22:32:42 -05:00
Wulfsta
42d8b9b847 docs: Update Measuring Resonances document with LIS2DW/LIS3DH information
Signed-off-by: Luke Vuksta <wulfstawulfsta@gmail.com>
2024-11-27 18:32:09 -05:00
Thijs Triemstra
2cfef4d94d docs: Update config screenshot for rpi235x (#6748)
Signed-off-by: Thijs Triemstra <info@collab.nl>
2024-11-27 18:31:01 -05:00
Kevin O'Connor
f2e69a3703 ci-install: Run 'apt-get update' prior to 'apt-get install'
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-22 14:59:40 -05:00
Emmanuel Ferdman
d6494ffed5 docs: update Manual_Level.md reference
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2024-11-19 17:15:43 -05:00
Kevin O'Connor
9bd0d47576 rp2040: Improve indentation in Kconfig file
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-14 13:01:21 -05:00
Kevin O'Connor
a46dba08e2 docs: Add rp2350 to benchmarks
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-14 11:35:23 -05:00
Kevin O'Connor
f6718291b7 rp2040: Add rp2350 bootrom based chipid and reboot to bootloader code
This adds the bootrom code needed to implement "reboot into
bootloader" and "chipid" capabilities.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-14 11:24:47 -05:00
Kevin O'Connor
8a203cf2cb rp2040: Move chipid reading to bootrom.c
Rewrite chipid.c so that it contains just the USB and canbus id
manipulation code.  Move the low-level chipid reading to bootrom.c.

Also, introduce a new bootrom_reboot_usb_bootloader() function in
bootrom.c so that the main.c code does not need to know the specifics
of rebooting into the bootrom.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-14 11:24:47 -05:00
Kevin O'Connor
58541a799e temperature_mcu: Add support for rp2350 MCUs
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-14 11:17:52 -05:00
Kevin O'Connor
848124ac4d flash_usb: Initial support for flashing rp2350 chips
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-14 11:17:52 -05:00
Kevin O'Connor
3cdb1793d4 lib: Update rp2040_flash code to support rp2350 reboot
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-14 11:17:52 -05:00
Kevin O'Connor
64ba37c02e lib: Update rp2040_flash to upstream picotool.git v2.0.0
This is in preparation for adding rp2350 flash support.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-14 11:17:52 -05:00
Kevin O'Connor
06bb49f135 rp2040: Initial rp2350 support
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-14 11:17:52 -05:00
Kevin O'Connor
61f81bdb26 rp2040: Use a higher USB PLL internal frequency
The rp2350 chip requires a higher internal frequency, so choose a
value that works for both rp2040 and rp2350.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-14 11:17:52 -05:00
Kevin O'Connor
c28ed06e98 rp2040: Avoid using memcpy() on USB dpram
Some versions of the system memcpy() may make unaligned memory
accesses, which can result in a bus fault when accessing the usb dpram
device memory.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-14 11:17:52 -05:00
Kevin O'Connor
405935f918 rp2040: Rename rp2040_link.lds.S to rpxxxx_link.lds.S
This is in preparation for rp2350 support.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-13 14:25:59 -05:00
Kevin O'Connor
906431bb00 rp2040: Rename CONFIG_RP2040_yyy Kconfig symbols to CONFIG_RPXXXX_yyy
Rename the Kconfig symbols.  This is in preparation to adding support
for the rp2350 mcu.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-13 14:25:59 -05:00
Kevin O'Connor
4ef21a1e9b armcm_boot: Support ARM cortex-m33 chips
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-13 14:25:59 -05:00
Kevin O'Connor
c5c79c936f lib: Add cortex-m33 support files to lib/cmsis-core/
This is in preparation for adding support for rp2350 mcus.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-13 14:25:59 -05:00
Kevin O'Connor
f00919070e lib: Add rp2350 files to pico-sdk
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-13 14:25:59 -05:00
Kevin O'Connor
2ad0b1afc2 lib: Update can2040 to support v2.0.0 of pico-sdk
A new version of can2040 is needed due to changes in the 2.0.0 release
of the pico-sdk.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-13 14:25:59 -05:00
Kevin O'Connor
c75eb53c0c lib: Update lib/rp2040 to v2.0.0 SDK release
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-13 14:25:59 -05:00
Kevin O'Connor
9f328cab95 lib: Move lib/rp2040/elf2uf2 to lib/elf2uf2
Recent versions of the rp2040 sdk no longer contain the elf2uf2 tool.
So, move that code to a new dedicated directory.  This is in
preparation for updating the rp2040 sdk version.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-13 14:25:59 -05:00
Kevin O'Connor
c88ee84bed msgproto: Fix return type for create_command()
Return an empty list instead of an emptry string if no command found.
This improves compatibility within console.py on python3.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-13 14:24:03 -05:00
yochiwarez
38bf6f2693 axis_twist_compensation: AXIS_TWIST_COMPENSATION new parameter AUTO for autocalibration
This commit adds automatic calculation support for compensating X and Y axis twist in the axis_twist_compensation module.

Signed-off-by: Jorge Apaza Merma <yochiwarez@gmail.com>
2024-11-12 22:10:04 -05:00
yochiwarez
4f3a7fd227 axis_twist_compensation: Implement Y-axis support
This commit implements support for the Y-axis in the axis_twist_compensation
module. This update enables the module to handle corrections for printers
with a twisted Y rail.

Signed-off-by: Jorge Apaza Merma <yochiwarez@gmail.com>
2024-11-12 22:10:04 -05:00
Kevin O'Connor
f119e96e8f configfile: Fix comments on same line as [include xxx.cfg] directive
Commit 9d4ab862 broke support for '#' style comments on the same line
as [include] config directives.  Fix by adding back in the check for
comments in _parse_config().

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-11-12 21:07:44 -05:00
MRX8024
6d1256ddcc resonance_tester: Fix chips selection, add accel_per_hz selection (#6726)
Corrected issue where accelerometer names were incorrectly prefixed
with "adxl345", preventing the selection of other chip types when running TEST_RESONANCES.

Implemented support for selecting the `accel_per_hz` parameter when running TEST_RESONANCES.

docs: Update TEST_RESONANCES + SHAPER_CALIBRATE with missing parameters and bracket corrections

Signed-off-by: Maksim Bolgov <maksim8024@gmail.com>
2024-11-12 19:55:32 -05:00
Wulfsta
2af8d3f1d0 config: Add lis3dh to Duet3D 1LC sample config
Signed-off-by: Luke Vuksta <wulfstawulfsta@gmail.com>
2024-11-12 19:50:48 -05:00
Wulfsta
6631275ab6 atsamd: allow i2c rate to be 400kHz
Signed-off-by: Luke Vuksta <wulfstawulfsta@gmail.com>
2024-11-12 19:50:48 -05:00
Wulfsta
9d36f31615 docs: Add lis2dw i2c and lis3dh
Signed-off-by: Luke Vuksta <wulfstawulfsta@gmail.com>
2024-11-12 19:50:48 -05:00
Wulfsta
0f7887fffe sensor_lis2dw: add lis3dh sensor and i2c communication
Signed-off-by: Luke Vuksta <wulfstawulfsta@gmail.com>
2024-11-12 19:50:48 -05:00
Lieven Vanhercke
a34034494e config: Added board config for Mellow Fly E3 v2 (#6682)
Signed-off-by: Lieven Vanhercke <lieven.vanh@gmail.com>
2024-11-06 19:58:16 -05:00
Pedro Lamas
eeb2678ec2 fan_generic: fixes missing logging import
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
2024-11-01 11:34:48 -04:00
Kevin O'Connor
a91d8a66f3 configfile: Separate access tracking to new ConfigValidate class
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-30 14:42:53 -04:00
Kevin O'Connor
9d4ab862b9 configfile: Only check for [include file] directives from main printer.cfg
Don't look for includes in autosave data nor from the internal menu,
display, and temperature configs.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-30 14:42:53 -04:00
Kevin O'Connor
85ebafd3f6 configfile: Don't read the autosave data if multiple autosave headers present
Also, verify new autosave looks valid prior to writing it.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-30 14:42:53 -04:00
Kevin O'Connor
9adb313ee8 configfile: Split configfile code into three separate classes
Separate out the low-level parsing code to a new ConfigFileReader()
class.

Separate out the auto-save handling code to a new ConfigAutoSave()
class.

This simplifies the main PrinterConfig() class.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-30 14:42:53 -04:00
Kevin O'Connor
faa89be816 docs: Fix Benchmarks.md git revision references
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-28 15:15:08 -04:00
Kevin O'Connor
89d94dd33b atsamd: Add Kconfig definition for SAME51N19 chip
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-28 15:08:29 -04:00
Kevin O'Connor
a796ca5e72 Kconfig: Remove references to manufacturers in Kconfig
Avoid referring to particular board manufacturers in "make
menuconfig".  This information becomes rapidly outdated and is
sometimes viewed by competing manufacturers as being unfair.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-28 15:08:29 -04:00
JamesH1978
94da4d10d7 docs: Update Measuring_Resonances.md for some NumPY version issues (#6719)
It has been noted over the last six to eight months that some versions of Numpy have issues with the klipper python environment on some machines. This PR introduces a fixed version that is known to work and a small test for people to do to make sure there are no output issues from the get go. These have been pulled from the pinned posts in the discord, from a time when 1.26 was causing issue, and now it seems v2 is also having some issues, hence the change. 

Signed-off-by: James Hartley <james@hartleyns.com>
2024-10-28 15:06:48 -04:00
Kevin O'Connor
31fe50ffa3 homing: Log a warning if probe alters stepper kinematic positions
After a probe attempt the toolhead position needs to be recalculated
to the position that the toolhead ultimately halted at.  Check that
the position setting wouldn't actually change the internal view of the
stepper motor and log a warning if any skew is detected.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-26 22:11:02 -04:00
Kevin O'Connor
b381f509d1 trsync: Don't require callers of trsync_do_trigger() to disable irqs
Disable irqs within trsync_do_trigger().

This fixes a bug in ldc1612 - as that code was calling
trsync_do_trigger() without first disabling irqs.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-26 22:11:02 -04:00
Kevin O'Connor
ea546c789b sched: Improve timer vs task priority check
Rename sched_tasks_busy() to sched_check_set_tasks_busy() and change
it to only return true if tasks are active (running or requested) for
two consecutive calls.  This makes it less likely that timers will
yield to tasks except when tasks really are notably backlogged.

This also makes it less likely that multiple steppers controlling the
same rail will be interrupted by tasks mid-step.  This should slightly
improve the timing, and make it less likely that a halt during
homing/probing will occur with these steppers taking a different
number of total steps.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-26 22:09:14 -04:00
Kevin O'Connor
f0a7797712 mcu: Only warn about mcu clock frequency if drift is more than 1%
This reduces the chance of spurious MCU clock configuration warnings.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-26 22:07:45 -04:00
Timofey Titovets
08102a0bf9 mpu: shutdown on i2c errors
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-10-26 22:06:30 -04:00
Timofey Titovets
1c3b30b815 ldc1612: shutdown on i2c errors
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-10-26 22:06:30 -04:00
Timofey Titovets
1563a68144 i2ccmds: move status checks to function
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-10-26 22:06:30 -04:00
Timofey Titovets
48590a35e4 stm32: forward i2c errors to i2ccmd
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-10-26 22:06:30 -04:00
Timofey Titovets
335a0e20c2 rp2040: forward i2c errors to i2ccmd
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-10-26 22:06:30 -04:00
Timofey Titovets
8a1c3cd668 linux: forward i2c errors to i2ccmd
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-10-26 22:06:30 -04:00
Timofey Titovets
2c246c7d33 i2c_software: forward errors to i2ccmd
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-10-26 22:06:30 -04:00
Timofey Titovets
a4aa2a9002 i2c: handle errors at i2ccmds
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-10-26 22:06:30 -04:00
Timofey Titovets
08a85ba869 i2ccmds: abstract i2c dev from bus implementation
Added wrapper around sw/hw bus API,
pins.py code will ensure that pins will not mix
between HW/SW buses.

Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-10-26 22:06:30 -04:00
Kevin O'Connor
39f08aeda1 docs: Update Sponsors.md
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-26 22:02:20 -04:00
Liam Powell
fe89c19ac0 stm32: Add support for USART3 on PC11/PC10 on STM32G474. (#6704)
Signed-off-by: Liam Powell <liam@liampwll.com>
2024-10-24 11:10:09 -04:00
Gareth Farrington
0c806d84f7 ads1220: Add input_mux and vref options to ADS1220 sensor (#6713)
* fix type comparison bug that stopped the sensor from initializing
* correct mismatch between docs and code for `sample_rate` (fixed to work same as hx71x)
* add input_mux, pga_bypass and vref options
* update configuration reference & fix typo

Signed-off-by: Gareth Farrington <gareth@waves.ky>
2024-10-24 11:07:05 -04:00
Kevin O'Connor
55339998e5 docs: Fix "XH711" typo in Config_Reference.md
Reported by @kabroxiko.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-24 10:05:44 -04:00
JamesH1978
0855994e59 docs: Update OctoPrint.md - wrong serial address (#6716)
This PR corrects a simple mistake where I gave the Unix socket not the serial pts.

Signed-off-by: James Hartley <james@hartleyns.com>
2024-10-22 10:36:13 -04:00
Wulfsta
8e1cdb199a docs: Add step rate benchmark for same70
Signed-off-by: Luke Vuksta <wulfstawulfsta@gmail.com>
2024-10-21 22:27:41 -04:00
Wulfsta
34e9ea55df atsam: Enable TCM and cache for atsame70
Signed-off-by: Luke Vuksta <wulfstawulfsta@gmail.com>
2024-10-21 22:27:41 -04:00
Wulfsta
52af688245 atsam: Add data memory barrier to USB driver
Signed-off-by: Luke Vuksta <wulfstawulfsta@gmail.com>
2024-10-21 22:27:41 -04:00
Kevin O'Connor
8a530cbcce scripts: Update whconsole tool to support python3
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-21 22:16:11 -04:00
Timofey Titovets
b89d552387 stm32: allow 400Khz in stm32f0_i2c.c (#6694)
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-10-09 20:00:38 -04:00
Kevin O'Connor
96cceed23e fan: Fix restart request handling
The change in parameter order introduced in commit f4143af4 failed to
update the call _handle_request_restart() code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-10-01 11:43:27 -04:00
Kevin O'Connor
8f361a15b2 fan_generic: Support setting a TEMPLATE on SET_FAN_SPEED commands
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-30 12:23:24 -04:00
Kevin O'Connor
f4143af4fa fan: Support calling set_speed() without a print_time
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-30 12:23:24 -04:00
Kevin O'Connor
1c0adb9af8 output_pin: Support setting a TEMPLATE on SET_PIN commands
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-30 12:23:24 -04:00
Kevin O'Connor
8a7a39530e output_pin: Move template evaluation code from led.py to output_pin.py
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-30 12:23:24 -04:00
Kevin O'Connor
3358295de8 led: Generalize template evaluation so it is not dependent on LEDs
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-30 12:23:24 -04:00
Kevin O'Connor
ef75346861 heaters: Fix typo - config.config_error() instead config.error()
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-25 23:25:59 -04:00
Kevin O'Connor
064eee6859 stm32: Fix i2c clock speeds for chips with a peripheral clock over 48Mhz
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-22 22:14:47 -04:00
Kevin O'Connor
8b7cc43952 stm32: Reduce peripheral clock speed on stm32g4 chips
A 170mhz (or 150mhz) peripheral clock is too fast for some peripherals.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-22 21:18:34 -04:00
Timofey Titovets
9426485bb6 rp2040: Check for i2c NACK/Start NACK (#6692)
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-09-22 19:28:07 -04:00
Jack Wakefield
b4aca122a1 flash_usb: Wait for busnum file to exist when flashing with picoboot
This solves an issue where the USB directory could exist, but the busnum
file itself may not exist immediately. This was encountered when
flashing a Pico connected to a Raspberry Pi 5.

Signed-off-by: Jack Wakefield <jackwakefield@protonmail.com>
2024-09-22 19:26:18 -04:00
Timofey Titovets
d9236f1c20 STM32: Check for NACK (#6687)
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-09-22 19:24:29 -04:00
Timofey Titovets
8a5801a204 i2c: drop i2c_modify_bits
No longer used and niche

Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-09-22 19:23:14 -04:00
Timofey Titovets
71433b8224 sx1509: drop i2c_modify_bits
According to the datasheet default value is 0000 0000
We do not modify them in other places.

Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-09-22 19:23:14 -04:00
Kevin O'Connor
87ac69363a fan: Wait full kick_start_time even if request is for full speed
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-18 13:59:07 -04:00
Kevin O'Connor
5731d964b6 fan: Use GCodeRequestQueue to queue updates
This is similar to 7940a6a7, but using gcrq.send_async_request() for
requests that could be asynchronous.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-18 13:59:07 -04:00
Kevin O'Connor
f323a4fcc7 output_pin: Add send_async_request() support to GCodeRequestQueue
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-16 13:31:14 -04:00
Kevin O'Connor
69e0d866c0 output_pin: Improve GCodeRequestQueue timing on duplicate requests
If there is a duplicate request it is not necessary to add a 100ms
delay to the next update.  Rework the callback signaling to better
report these duplicate updates.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-16 13:31:14 -04:00
Kevin O'Connor
0532a41c75 led: Fix typo in call to unregister_timer()
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-16 13:29:25 -04:00
Kevin O'Connor
900bf2be55 Revert "fan: Use GCodeRequestQueue to queue updates"
This reverts commit 7940a6a728.

Queing of fan updates via GCodeRequestQueue is only valid if updates
originate from gcode commands.  The heater_fan, controller_fan, and
temperature_fan modules could send updates asynchronously.  Revert the
fan queuing changes until this issue can be resolved.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-13 14:30:56 -04:00
Kevin O'Connor
cc4ad6670f output_pin: Keep flushing GCodeRequestQueue if needed
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-13 00:59:44 -04:00
Kevin O'Connor
28995a8bce servo: Use GCodeRequestQueue to queue updates
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-12 13:25:00 -04:00
Kevin O'Connor
7940a6a728 fan: Use GCodeRequestQueue to queue updates
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-12 13:24:32 -04:00
Kevin O'Connor
6ade82ed7e output_pin: Introduce new helper to facilitate queuing of gcode requests
Add a new GCodeRequestQueue class that can queue and collate g-code
pin requests.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-12 13:24:32 -04:00
Kevin O'Connor
3a57f71f33 output_pin: Remove deprecated maximum_mcu_duration and static_value
Remove support for these two config options that were previously
deprecated.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-12 13:24:29 -04:00
Kevin O'Connor
293858c51f hx71x: Avoid base classes to improve python2 compatibility
Also, add a load_cell regression test case.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-09-12 13:15:01 -04:00
Bevan Weiss
14a83103c3 flashsd: Add support for chitu-v6 (#6671)
Add flashsd configuration for Tronxy x5sa and other printers based on
Chitu v6 board.

These boards should support sdio (this is what the schematic details),
however I couldn't get this working from a quick try.

Signed-off-by: Bevan Weiss <bevan.weiss@gmail.com>
2024-09-05 16:50:32 -04:00
Eric Callahan
08a1c9f127 docs: update temperature_probe documentation
Add documentation for the "max_valid_temp" option.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-09-01 13:37:35 -04:00
Eric Callahan
40d6a06f8f temperature_probe: add max_valid_temp option
Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-09-01 13:37:35 -04:00
Kevin O'Connor
f71d2c7cfc stm32: Fix setting USB clock with USB to CANbus mode on stm32g4/stm32l4
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-08-29 22:31:13 -04:00
Timofey Titovets
81de9a8615 bme680: measure gas VOC once a while
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-08-16 22:21:12 -04:00
Timofey Titovets
f9d7a71195 bme680: select mode once
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-08-16 22:21:12 -04:00
Timofey Titovets
ff3eed2ad8 bme280: use periodic mode for BM[PE]280
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-08-16 22:21:12 -04:00
Timofey Titovets
9e45ec222e bme280.py: drop unused max_sample_time
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-08-16 22:21:12 -04:00
Timofey Titovets
3e55008323 bme280.py: iir_filter mask input value
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-08-16 22:21:12 -04:00
Kevin O'Connor
d81eb557d7 sensor_hx71x: Signal an overflow from the timer handler
Check for overflows in the timer handler instead of checking the
elapsed query time.  This should be a better check as it also accounts
for task delays that occur before the query starts.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-08-14 22:17:10 -04:00
Kevin O'Connor
d5e5a6da2d hx71x: Update api header and docs to correctly note "value" field
Update both hx71x and ads1220 to reflect that there is a third "value"
field in the reported data.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-08-14 22:17:10 -04:00
Bevan Weiss
c0edfbc4ea src: Current code produces warnings for possible value overflows. (#6665)
As the input values are uint8_t types, any shift may result in value loss.
Explicit promotion to the output type (uint32_t) keeps things safe.
Have also changed the int32_t in ads1220_read_adc to uint32_t, type
promotion and bit manipulation are a bit 'weird' on signed integers, so
keep it as an unsigned to align with following function call parameter type.
Have retained the prior explicit sign extension logic however.

Signed-off-by: Bevan Weiss <bevan.weiss@gmail.com>
2024-08-14 22:14:19 -04:00
Timofey Titovets
3f2ef88eb9 gcode_arc: merge coords gen & G1 emit
Chopping lines from arc can take significant time.
Merge cycles to make the event loop progress and optimize performance.

Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-08-12 13:06:28 -04:00
Timofey Titovets
503e7e368b gcode_arc: refactor simplify
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-08-12 13:06:28 -04:00
Nicholas Huskie
ca815f52c8 stm32: Fix getting wrong ADC value on PA0 of STM32G431 (#6660)
* Fix getting wrong ADC value on PA0

* Fix invalid/unused pin being used as adc channel on STM32H7/G431/L4

Signed-off-by: Nicholas Huskie <huskie@idealfuture.org.cn>
2024-08-08 22:45:12 -04:00
JamesH1978
025ae2349d docs: Update Installation.md (#6650)
Added links for Fluidd/Mainsail/Octoprint

Added references to overview.md and mkdocs.yml and spelling errors.

Signed-off-by: James Hartley <james@hartleyns.com>
2024-08-08 22:43:21 -04:00
Dmitry Butyugin
d7d9092a92 servo: Asynchronous adjustments of servo position
This change follows the same approach as implemented for fan control.
The change removes the move queue flushing when changing servo position,
which does not appear to be necessary. This can be beneficial, for
example, for WS7040-based cooling on IDEX setups where the servo can
be used to control the air flow between the toolheads, with this change
eliminating micro-stutters of the toolhead on servo position adjustment.

Signed-off-by: Dmitry Butyugin <dmbutyugin@google.com>
2024-08-03 14:41:30 -04:00
Dmitry Butyugin
ba2a149e9a idex_modes: Improved restoring position in RESTORE_DUAL_CARRIAGE_STATE
Previous implementation could crash the idex carriages into each other.
The new code moves the idex carriages together, eliminating this risk
and decreasing the time needed to restore the carriages positions.

Signed-off-by: Dmitry Butyugin <dmbutyugin@google.com>
2024-08-03 14:40:52 -04:00
Kevin O'Connor
13c75ea876 docs: Fix heading hierarchy for load_cell in Config_Reference.md
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-07-31 21:31:22 -04:00
Kevin O'Connor
cb15d0fec6 load_cell: Don't start sensor on startup
Also, don't report an empty status.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-07-31 21:26:10 -04:00
Gareth Farrington
055f07c638 ads1220: Add ADS1220 bulk sensor to load_cell
Add support for the ADS1220 as an alternative to HX71x that supports SPI and higher sample rates.

Signed-off-by: Gareth Farrington <gareth@waves.ky>
2024-07-31 21:22:33 -04:00
Gareth Farrington
c0095812ff hx71x: Load Cell Skeleton and HX71x bulk ADC
* Create the load_cell host module skeleton to create the sensors and start taking samples.
* Add support for the HX717 and HX711 ADC sensors.

Signed-off-by: Gareth Farrington <gareth@waves.ky>
2024-07-31 21:22:06 -04:00
Miles Pawar
0844388d70 config: Update generic-bigtreetech-skr-mini-e3-v3.0.cfg (#6654)
- Removed stealth from Extruder to stop under extrusion issues
- Changed Bed sensor to correct one for Ender 3

* Update generic-bigtreetech-skr-mini-e3-v3.0.cfg

Signed-off-by: Miles Pawar <slab.paged-0p@icloud.com>
2024-07-26 20:14:22 -04:00
bryan065
12cd1d9e81 spi_flash: Add stm32g0b0xx to board_defs.py (#6646)
Added board definition for stm32g0b0xx variant of the SKR Mini v3.0.

Signed-off-by: Bryan Le <le.bryan.065@gmail.com>
2024-07-18 10:45:51 -04:00
Eric Callahan
6848843224 docs: add initial temperature_probe documentation
Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-07-17 22:25:49 -04:00
Eric Callahan
bd1dbc8af3 probe_eddy_current: support thermal compensation
Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-07-17 22:25:49 -04:00
Eric Callahan
7603953ef7 temperature_probe: probe temperature sensor
Add temperature sensor with thermal drift calibration.
Currently only Eddy Current based probes support
calibration.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-07-17 22:25:49 -04:00
Kevin O'Connor
0087f04cc3 gcode: Minor change to suppress python warning on '\s'
Reported by @matdibu.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-07-11 15:01:32 -04:00
Dmitry Butyugin
c84d78f3f1 extruder: Allow dynamic adjustment of pressure advance (#6635)
Signed-off-by: Dmitry Butyugin <dmbutyugin@google.com>
2024-07-11 14:43:21 -04:00
Timofey Titovets
248d3dbf8b sht3x: use periodic report mode (#6634)
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-07-06 18:28:45 -04:00
Kevin O'Connor
00cb683def serialhdl: Catch IOError in connect_canbus()
It seems the can library on Python2 can sometimes raise an IOError
exception on a failure.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-07-05 20:16:21 -04:00
Kevin O'Connor
34732f857a smart_effector: Define get_position_endstop() wrapper
Reported by @noisyfox.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-07-04 18:36:39 -04:00
Kevin O'Connor
9318901f19 mkdocs: Update id
The Google UA ids are deprecated - update to assigned GA4 id.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-30 15:39:46 -04:00
elmo
4d21ffc1d6 config: Adds support for the Tronxy Crux1 printer (#6627)
Signed-off-by: Louis West <lowest@mailbox.org>
2024-06-27 12:59:48 -04:00
Kevin O'Connor
6d70050261 temperature_mcu: Enhance "ADC out of range" error reports
Try to report which ADC is reporting out of range.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-21 15:32:30 -04:00
Kevin O'Connor
2d73211190 adc_temperature: Enhance "ADC out of range" error reports
Try to report which ADC is reporting out of range.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-21 15:32:30 -04:00
Kevin O'Connor
d89722056b mcu: Rename setup_minmax() to setup_adc_sample()
Rename this method so that it is more distinct from the the common
temperature setup_minmax() method.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-21 15:32:30 -04:00
Kevin O'Connor
9fa0fb1a0e error_mcu: Support mechanism to add per-instance context to a shutdown
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-21 15:32:30 -04:00
Kevin O'Connor
7149bb1b6d error_mcu: Move formatting of mcu connect errors to error_mcu module
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-21 15:32:30 -04:00
Kevin O'Connor
ba529996ea error_mcu: Move mcu protocol error reporting to error_mcu module
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-21 15:32:30 -04:00
Kevin O'Connor
4ac283cc0e error_mcu: Move shutdown error message formatting to new error_mcu.py module
Create a new module to help format verbose mcu error messages.  Move
the shutdown message formatting to this module.  This moves the error
formatting out of the background thread and out of the critical
shutdown code path.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-21 15:32:30 -04:00
Eric Callahan
a19d64febd docs: add rapid probing documentation
Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-06-19 13:47:32 -04:00
Eric Callahan
a19af08894 bed_mesh: add support for MESH_PPS param in BMC
In addition, do not respond with generated points.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-06-19 13:47:32 -04:00
Eric Callahan
2c7e09cfa6 bed_mesh: use generated XY positions in probe_finalize()
The scan modes provide kinematic XYZ coordinates in the
probe results.  These positions may deviate from the requested
positions, which can introduce errors in mesh generation when
transposing the result into the Z matrix.

Rely on the coordinates generated by bed mesh to transpose
the matrix, presuming that points at the same index in the
list match.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-06-19 13:47:32 -04:00
Eric Callahan
fc0f17b920 graph_mesh: script for mesh visualization and analysis
Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-06-19 13:47:32 -04:00
Eric Callahan
c7b7c11cc3 bed_mesh: add dump_mesh webhooks API
Returns current mesh configuration and state.  Includes probed and
mesh matrices, saved profiles, current points, and travel paths.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-06-19 13:47:32 -04:00
Eric Callahan
f2df011c68 bed_mesh: optimize rapid travel paths
This adds supplemental path generation that implements
"overshoot" when a change of direction is performed
during a rapid scan.  This overshoot reduces measurement
error at the extremes of the mesh along the X axis.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-06-19 13:47:32 -04:00
Kevin O'Connor
0a14e33150 probe_eddy_current: Add support for "rapid_scan" mode
Add a scanning mode that does not require pausing the toolhead at each
probe point.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-19 13:47:32 -04:00
Kevin O'Connor
11f04ba1ba configfile: Allow getchoice() to take a list
If a list is passed to getchoice(), seamlessly convert it to a dict.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-17 13:49:05 -04:00
Kevin O'Connor
863a463cb2 rp2040_link: Explicitly set klipper.elf output section flags to avoid warning
Avoid pointless "LOAD segment with RWX permissions" linker warnings
during the rp2040 build.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-17 12:45:07 -04:00
Kevin O'Connor
ae227d485c armcm_link: Fix build on recent arm gcc/newlibc versions
It seems recent arm gcc versions no longer build correctly using the
"--specs=nano.specs --specs=nosys.specs" linker flags.  Replace those
linker flags with "-nostdlib -lgcc -lc_nano".

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-17 12:45:07 -04:00
Kevin O'Connor
433fcb6f24 axis_twist_compensation: Fix missing probe import
Fixes missing import introduced in commit bec47e04.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-15 11:06:56 -04:00
Kevin O'Connor
beba2c2d33 axis_twist_compensation: No need to rename bed_mesh and manual_probe
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-15 11:03:50 -04:00
Kevin O'Connor
fcf064ba68 probe_eddy_current: Add support for probing in "scan" mode
When probing in "scan" mode, the toolhead will pause at each position,
but does not descend.  This can notably reduce the total probing time.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-14 13:38:07 -04:00
Kevin O'Connor
aa0dbf6ee6 probe_eddy_current: Calculate toolhead position along with probed position
Support calculating the low-level kinematic toolhead position while
calculating the probed frequency.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-14 13:38:07 -04:00
Kevin O'Connor
49f511e679 probe_eddy_current: Process samples as they arrive
Convert samples into probe frequencies as the samples arrive.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-14 13:38:07 -04:00
Kevin O'Connor
429aa2b2a6 probe_eddy_current: Generate Z height from average frequency
Calculate the average frequency from a set of samples, and then
calculate the estimated Z height from that frequency.  This may
improve accuracy, as the frequency to Z height is not linear and
averaging after the non-linear transform could bias the results.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-14 13:38:07 -04:00
Kevin O'Connor
bf1bc1ee0f probe_eddy_current: Introduce new EddyGatherSamples helper class
Split the sample gathering code out of EddyEndstopWrapper class and
into a new EddyGatherSamples class.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-14 13:38:07 -04:00
Kevin O'Connor
1591a51f76 probe: Gather multiple results in ProbeSessionHelper
Change run_probe() to gather the results locally, and introduce a new
pull_probed_results() method that returns the previously probed
results.  This is in preparation for future probing code that benefits
from batching probe results.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-14 13:38:07 -04:00
Kevin O'Connor
8de7153952 probe: Rework ProbePointsHelper to store results locally
Store the results of each probe attempt in a local "results" variable
(instead of a class variable) when performing "automatic" probes.
This is in preparation for gathering the results in the probing
implementation.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-14 13:38:07 -04:00
Eric Callahan
0d87bec159 ci-install: update gnu-pru to version 2024.05
Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-06-11 21:36:25 -04:00
Kevin O'Connor
589bd64ce0 command: Support 2-byte message ids
Allow command ids, response ids, and output ids to be either 1 or 2
bytes long.  This increases the total number of message types from 128
to 16384.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:27:12 -04:00
Kevin O'Connor
36b8831c7e sensor_bulk: Change maximum data size from 52 to 51 bytes
Reduce the maximum data size from 52 bytes to 51 bytes.  This will
enable support for 2-byte response ids.

This change would alter the behavior of the ldc1612 sensor support.
Force an ldc1612 command name change so that users are alerted that
they must rebuild the micro-controller code upon update of the host
code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:27:12 -04:00
Kevin O'Connor
17c645f000 msgproto: Support multi-byte command and response ids
Update the msgproto.py code so that it can support message ids that
are larger than a single byte.  (The host C code in
klippy/chelper/msgblock.c already supports multi-byte ids.)

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:27:12 -04:00
Kevin O'Connor
d4bae4dffe probe: Simplify PrinterProbe() now that there are no external callers
Create the mcu_probe interface locally within PrinterProbe().

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
931d1ce8f4 probe_eddy_current: No need to use PrinterProbe() class
Directly register the PrinterEddyProbe() class as the main probe
interface.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
93245b3678 smart_effector: No need to use PrinterProbe() class
Directly register the SmartEffectorProbe() class as the main probe
interface.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
068d2a9f5a bltouch: No need to use PrinterProbe() class
Directly register the BLTouchProbe() class as the main probe
interface.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
f72f94e299 probe: Move add_steppers() logic to HomingViaProbeHelper class
Perform the initial add_steppers() configuration in a single location.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
58753e58a2 probe: Use ppins.setup_pin() helper
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
abfe3675d6 bltouch: Use ppins.setup_pin() helper
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
e780049a74 probe: Use an event for axis twist compensation updates
Instead of directly calling axis_twist_compensation, send an event
that can perform the necessary updates.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
f4adb29999 probe: Ensure all external callers always call end_probe_session()
Rework ProbeSessionHelper's multi_probe_start() and multi_probe_end()
to start_probe_session() and end_probe_session().  Ensure all external
callers always invoke these methods prior to running run_probe().

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
982a50c70a probe: Split z_virtual_endstop handling to new HomingViaProbeHelper class
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
de9798fb5b probe: Move offset handling to new ProbeOffsetsHelper class
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
f9a2920cee probe: Move PROBE_ACCURACY command to ProbeCommandHelper class
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
292512f813 probe: Move PROBE_CALIBRATE to ProbeCommandHelper class
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
6ea5b94d1e probe: Convert probe.get_lift_speed() to probe.get_print_params()
Add a get_print_params() method that can extract all the common
probing parameters.  Replace get_lift_speed() with this more general
function.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
6f6122a576 probe: Move Z_OFFSET_APPLY_PROBE to ProbeCommandHelper class
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
8fc11b4a2e probe: Introduce new ProbeCommandHelper class
Move the PROBE and QUERY_PROBE commands from ProbeSessionHelper class
to new ProbeCommandHelper class.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
bec47e0492 probe: Split out new ProbeSessionHelper() class from PrinterProbe()
Separate out the PrinterProbe() class to make the external probe
interfaces more clear.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
12f92c55f1 probe: Code movement in probe.py
Move code around in probe.py and add some comments.  No code changes.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-10 12:20:21 -04:00
Kevin O'Connor
49c0ad6369 motan: Fix logic error resulting in incorrect stepper phase graphing
The mcu_phase_offset should be added not subtracted.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-06-05 12:53:13 -04:00
Jayofelony
6cd174208b config: Add Artillery Genius Pro config (#6604)
Signed-off-by: Jeroen Oudshoorn <oudshoorn.jeroen@gmail.com>
2024-05-27 20:57:42 -04:00
Elias Bakken
3078912f1d stm32: STM32F031 updates (#6607)
Add support for STM32F031x6 which is the 32 KB version of the STM32F031 MCU.

Add new I2C bus variant.

Signed-off by: Elias Bakken <elias@iagent.no>
2024-05-25 15:47:48 -04:00
Timofey Titovets
b6a0063235 tmc5160: csactual -> cs_actual
Correct the name of "cs_actual" and correct the size on tmc5160.

Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-21 20:31:23 -04:00
Kevin O'Connor
4a92727eab sensor_ldc1612: Halt homing if sensor reports a warning
Explicitly check for sensor warnings during homing and report an error
code back to the host.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-21 20:16:31 -04:00
Kevin O'Connor
37482178b5 mcu: Raise an error on a failed home_wait() call
Raise a printer.command_error exception if a home_wait() call fails.
This makes it easier to support future types of homing errors.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-21 20:16:31 -04:00
Kevin O'Connor
4709f1fad5 sensor_ldc1612: Create new check_home() helper function
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-21 20:16:31 -04:00
Kevin O'Connor
04c562941c sensor_ldc1612: Add support for chips with INTB line routed to mcu
If the INTB line is available it can reduce the MCU load.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-21 20:16:31 -04:00
Kevin O'Connor
cb6cce3934 sensor_ldc1612: Don't require DRDY bit to be set on data read
It is not clear if DRDY is cleared during a STATUS read (which could
occur from command_query_ldc1612_status() ).  So, just check the
"unread conversion" bit when reading data.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-21 20:16:31 -04:00
Kevin O'Connor
3dc7c9ab29 test: Disable ldc1612 on stm32f042 build to reduce size
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-21 20:16:31 -04:00
Kevin O'Connor
6fac654352 probe_eddy_current: Calibrate every 40um instead of 50um
A 40um distance is more likely to be a full step distance on common Z
leadscrews (which often use a rotation distance of 8mm).

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-21 20:16:31 -04:00
Eric Callahan
29bfbd02f9 probe_eddy_current: fix attribute name
Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-05-21 20:16:31 -04:00
Kevin O'Connor
236d780a0a probe_eddy_current: Fix wait for samples in probing_move()
Make sure to wait until all samples are available before performing
analysis on the data.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-21 20:16:31 -04:00
Kevin O'Connor
5d52b32e64 tmc: Remove code that could read microsteps in tmc config sections
Setting of microsteps in the stepper config section has been required
since commit eea0137b.  Remove the no longer needed compatibility
code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-16 12:29:52 -04:00
Kevin O'Connor
2efde0111e tmc: Save and restore thigh during sensorless homing
Make sure thigh is set to zero during sensorless homing, as it would
not make sense for it to be enabled.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-16 12:18:02 -04:00
Kevin O'Connor
faee2c0e52 tmc: Refactor TMCtstepHelper()
Update TMCtstepHelper() to obtain the step_distance, tmc_frequency,
and mres fields directly.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-16 12:18:02 -04:00
Frans-willem Hardijzer
b7f7b8a346 idex_modes: Bugfix for kinematic position calculation.
idex_mode would swap the X and dual-carriage rail in some cases
(homing), but not in others. As such, the position calculation was
correct while homing, but incorrect for the second carriage during
normal moves. This commit fixes homing to work without swapped rails,
removes the swapping of rails while homing, and removes the ability to
swap rails (as it is now no longer used). Fix has been tested in a
Hybrid_CoreXY IDEX printer (Voron Double Dragon). Hybrid_CoreXZ has
identical changes and is similar enough that I am confident it will work
as intended. Changes to cartesion seem simple enough, but would benefit
from someone running a couple of tests.

Signed-off-by: Frans-Willem Hardijzer <fw@hardijzer.nl>
2024-05-16 12:08:13 -04:00
voidtrance
694d38c791 bed_mesh: Fix adaptive probe count on delta printers (#6600)
Round beds require an odd number of probe points in
order to prevent erroneously truncating the mesh.

The adaptive mesh algorithm did not consider that and
as a result, it was possible to generate adaptive
meshes with even number of probe points.

This change fixes this by increasing the probe point
count by 1 in cases where the adaptive probe points
are even.

Signed-off-by: Mitko Haralanov <voidtrance@gmail.com>
2024-05-15 21:38:42 -04:00
Kevin O'Connor
dae8b8cacf docs: Update jinja2 requirement in mkdocs-requirements.txt
Update the jinja2 version to suppress security warnings.  Klipper is
not impacted by the vulnerability, but it is harmless to update the
version.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-15 11:23:51 -04:00
Kevin O'Connor
e0cbd7b5fc docs: Minor wording change to coolstep_threshold in Config_Reference.md
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-05-14 18:22:58 -04:00
Alex Voinea
ed8dca8df0 tmc: Implement high_velocity_threshold for drivers that support it
Signed-off-by: Alex Voinea <voinea.dragos.alexandru@gmail.com>
2024-05-14 18:21:24 -04:00
Alex Voinea
5249d955bb tmc: Implement coolstep_threshold for drivers that support it
Signed-off-by: Alex Voinea <voinea.dragos.alexandru@gmail.com>
2024-05-14 18:21:24 -04:00
Alex Voinea
0f3f29101c tmc: Implement CoolStep fields for all drivers
Signed-off-by: Alex Voinea <voinea.dragos.alexandru@gmail.com>
2024-05-14 13:26:58 -04:00
Alex Voinea
1ca1054957 tmc2130: implement missing HighVelocity fields in the config
Signed-off-by: Alex Voinea <voinea.dragos.alexandru@gmail.com>
2024-05-14 13:26:58 -04:00
Alex Voinea
f01c8853ca tmc: Do not pass the frequency directly to the helpers
Use the new get_tmc_frequency() instead.

Signed-off-by: Alex Voinea <voinea.dragos.alexandru@gmail.com>
2024-05-14 13:26:58 -04:00
Dropeffect GmbH
472d8e5b66 stm32: Add STM32G474 chip to Kconfig
Signed-off-by: Amr Elsayed from Dropeffect GmbH <code@dropeffect.com>
2024-05-14 11:53:38 -04:00
Dropeffect GmbH
80b55d3528 stm32: Add FDCAN2 channel needed for stm32g4 alternate pins
Some of the alternate pins defined are routed to FDCAN2 instead of
FDCAN1, this commit uses the correct IRQ register and peripheral
clock enable bit to enable FDCAN on those pins.

Signed-off-by: Amr Elsayed from Dropeffect GmbH <code@dropeffect.com>
2024-05-14 11:53:38 -04:00
Dropeffect GmbH
8f510da12b stm32g4: Fix ADC3 common interface registers name to ADC345_COMMON
Use ADC345_COMMON instead of ADC3_COMMON for stm32g4 ADC3 channel.

Signed-off-by: Amr Elsayed from Dropeffect GmbH <code@dropeffect.com>
2024-05-14 11:53:38 -04:00
Stéphane Lepin
79930ed99a config: Add safe_z_home section for Creality CR-6 SE
The Creality CR-6 SE has a strain gauge on its hotend used for z-probing and homing. Currently, running G28 to home all axes puts the hotend just outside of the print bed and thus assumes a wrong homing point for the Z axis.

This change aims to address this issue by setting a safe Z-homing point (in the middle of the print bed) into the Creality CR-6 SE 2020 and 2021-revision config files.

Signed-off-by: Stéphane Lepin <stephane.lepin@gmail.com>
2024-05-12 20:08:30 -04:00
Donald A. Cupp Jr
434770eaf9 stm32: Add new spi2 on stm32g0 chips (#6569)
Signed-off-by: Donald A. Cupp Jr <doncuppjr@yahoo.com>
2024-05-03 13:30:45 -04:00
Jelle van der Waa
7e8c7f46a9 klippy: Replace logging.warn usage with logging.warning
logging.warn is an alias to logging.warning since Python 3.3 and will be
removed in Python 3.13.

Signed-off-by: Jelle van der Waa <jelle@vdwaa.nl>
2024-05-01 12:37:10 -04:00
charminULTRA
af149b4781 docs: Update Measuring_Resonances.md (#6509)
Current command, using the *, results in bad chart output when more than one .csv file exists in the tmp folder. This isn't obvious for people who may not know that the * is a wildcard character.

Signed-off-by: Jonathan Williams <jcw122@gmail.com>
2024-04-29 19:48:10 -04:00
Kevin O'Connor
d8d072b375 adxl345: Fix read_axes_map() for non-adxl345 accelerometers
Commit 3f845019 unified the reading of the axes_map configuration
variable, but broke the per-sensor scaling capabilities.  Pass the
scale parameters to read_axes_map() so that it can be implemented
per-sensor.

Reported by @Neko-vecter.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-29 12:07:28 -04:00
Kevin O'Connor
7b490f3ec1 probe: Fix typo in activate/deactive error messages
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-27 11:10:01 -04:00
Oleg Gavavka
b1eec53ff4 pru: BeagleBone Firmware upgrade to Debian 11.7 Bullseye (#6577)
* Porting BeagleBone to Kernel 5.10

* Fixing issue with installation for BeagleBone.

This fix resolve 2 issue:
1. Conflict with AVR packages.
2. "klipper_pru" script is executed before PRU cores are ready

* Adding additional steps to BeagleBone install guide.

* Updating BeagleBone documentation, adding different use cases, adding buses configurations SPI, I2C, CAN, UART

Signed-off-by: Oleg Gavavka <work@datalink.net.ua>
2024-04-27 11:01:57 -04:00
Amken USA
0b329c5d28 rp2040: Add kconfig options for rp2040 uart (#6549)
Modified serial.c and Kconfig to dynamically select all possible UART combinations for RP2040

Signed-off-by: Hriday Keni <info@amken.us>
2024-04-24 22:32:29 -04:00
林玮 (Jade Lin)
c3ec4af6cc bme280: Add BMP388 sensor support to BMxx80 (#6576)
Extends the BMxx80 category with support for the BMP388 sensor,
providing temperature and pressure output similar to the existing BMxx80 class of sensors.

Signed-off-by: 林玮 (Jade Lin) <linw1995@icloud.com>
2024-04-24 21:45:05 -04:00
Alessandro Maggi
2f6e94c94c docs: Fix typo in Bed_Mesh.md (#6572)
Signed-off-by: Maggi Alessandro <maggialessandro360@gmail.com>
2024-04-20 18:57:58 -04:00
Timofey Titovets
713b509698 sht3x: Add sht31 support (#6560)
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-04-20 18:42:31 -04:00
Kevin O'Connor
28faf81414 docs: Update CANBUS_Troubleshooting.md to avoid formatting error
Avoid starting a line with "128." as that confused markdown.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-20 13:23:42 -04:00
Kevin O'Connor
819599362c bulk_sensor: Rename BulkDataQueue methods
Rename pull_samples() to pull_queue() and rename clear_sample() to
clear_queue().  This avoids confusion between the queue of response
messages and the larger list of samples stored within those messages.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-20 12:52:47 -04:00
Kevin O'Connor
abb7910316 bulk_sensor: Rework ChipClockUpdater class into FixedFreqReader
Move the sensor_bulk_data message queuing into the class, and then
rename that class.  This simplifies the users of the code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-20 12:52:47 -04:00
Kevin O'Connor
9ceaae3847 bulk_sensor: Refactor ChipClockUpdater constructor
Build the clock_sync and struct.Struct() in the ChipClockUpdater
constructor.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-20 12:52:47 -04:00
Kevin O'Connor
56829b07d2 ldc1612: Use extract_samples() for sample timestamp calculation
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-20 12:52:47 -04:00
Kevin O'Connor
f73e6dcd12 mpu9250: Use extract_samples() for sample timestamp calculation
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-20 12:52:47 -04:00
Kevin O'Connor
144af05270 lis2dw: Use extract_samples() for sample timestamp calculation
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-20 12:52:47 -04:00
Kevin O'Connor
95fdb68587 adxl345: Move sample timestamp calculation to reusable code
Add a new extract_samples() method to the ChipClockUpdater class that
calculates the sample timestamp for each sample in a list of bulk
sensor reports.

Update the adxl345 code to use that extract_samples() code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-20 12:52:47 -04:00
Kevin O'Connor
c106955850 docs: Add information on txqueuelen to CANBUS_Troubleshooting.md
Provide some background information on the Linux can interface
txqueuelen parameter, errors that it can cause, and considerations
when configuring it.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-20 12:49:13 -04:00
Kevin O'Connor
12e9b633d8 docs: Recommend using "ip" instead of "ifconfig" in CANBUS.md
Some Linux systems do not install ifconfig, while ip should always be
available.  So, update the canbus documentation to recommend that.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-20 12:47:06 -04:00
Kevin O'Connor
2425a74638 virtual_sdcard: Define a default for on_gcode_error
If on_gcode_error is not specified, default to running the
TURN_OFF_HEATERS command.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-15 14:10:36 -04:00
TheFeralEngineer
36f9b26ef9 config: Artillery Sidewinder X3 (#6534)
Signed-off-by: Phil Timpson <theferalengineer@gmail.com>
2024-04-12 12:27:36 -04:00
trofen
75d7c17656 docs: Fix typo in Resonance_Compensation.md
Signed-off-by: Plynskiy Nikita <nikita53ne@yandex.ru>
2024-04-12 12:17:35 -04:00
Pedro Lamas
c37329e9e2 homing_override: Adds rawparams support
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
2024-04-12 12:13:05 -04:00
Kevin O'Connor
4cfa266e00 manual_stepper: Revert "manual_stepper: Add basic status. (#6527)"
This reverts commit b029d04668.

The MCU_Stepper class does not have a is_motor_enabled() method, so
the change above results in an internal exception.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-10 10:40:41 -04:00
Kevin O'Connor
a8b493a1ae motan: Add support for graphing ldc1612 coil frequencies
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-09 16:32:43 -04:00
Kevin O'Connor
b09897245e docs: Add a new Eddy_Probe.md document
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-09 16:32:43 -04:00
Kevin O'Connor
30e0fddbbf docs: Add documentation for probe_eddy_current
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-09 16:32:43 -04:00
Kevin O'Connor
28281c595b probe_eddy_current: Use sensor value at halt position for "trigger" position
Calculate the sensor Z position after the probe halts and return that
as the "probed position".  This sensor position provides a more
accurate measurement.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-09 16:32:43 -04:00
Kevin O'Connor
13b2926e0c probe_eddy_current: Initial support for PROBE command
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-09 16:32:43 -04:00
Kevin O'Connor
b0d90fd013 probe_eddy_current: Support calibrating Z height to sensor frequency
Add a calibration tool that can be used to correlate sensor frequency
to bed Z height.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-09 16:32:43 -04:00
Kevin O'Connor
d84fc431a1 ldc1612: Add LDC_CALIBRATE_DRIVE_CURRENT calibration command
Add a command to calibrate the sensor DRIVE_CURRENT0 register.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-09 16:32:43 -04:00
Kevin O'Connor
da2b258441 ldc1612: Initial host support for reading ldc1612 bulk sensor data
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-09 16:32:43 -04:00
Kevin O'Connor
b8f1df3a96 sensor_ldc1612: Initial support for bulk reading ldc1612 sensor
Signed-off-by: Alan.Ma from BigTreeTech <tech@biqu3d.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-09 16:32:43 -04:00
Kevin O'Connor
acdf8bb108 probe: Add a probing_move() wrapper to low-level mcu_probe class
This allows the low-level probe class more control on the probing
implementation.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-09 16:32:43 -04:00
Kevin O'Connor
fa5fa74761 mcu: Separate trdispatch handling from MCU_endstop class
Create a new TriggerDispatch class to track the low-level handling of
the trdispatch mechanism.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-09 16:32:43 -04:00
Kamil Domański
01c7befacb klippy: remove a few unused variable assignments (#6504)
Signed-off-by: Kamil Domański <kamil@domanski.co>
2024-04-05 17:43:43 -04:00
Viesturs Zariņš
b029d04668 manual_stepper: Add basic status. (#6527)
Adding position and enabled in manual_stepper status. Enabled is already available through stepper_enable object. But this makes it more straightforward to access it.

Signed-off-by: Viesturs Zarins <viesturz@gmail.com>
2024-04-04 16:46:30 -04:00
Michael 'ASAP' Weinrich
6f16e11197 linux: Don't use absolute paths for include
Not all systems (i.e. Nix) repect the standard Linux filesystem hierarchy,
instead relative paths should be used and allowing GCC to rely on it's
builtin search paths.

Signed-off-by: Michael 'ASAP' Weinrich <michael@a5ap.net>
2024-04-03 22:53:09 -04:00
Michael 'ASAP' Weinrich
24c884e9f3 makefile: Replace CFLAGS -I with -iquote
The -iquote tells GCC to only search that path when resolving a quoted
"include" (vs <angle brackets>) which by convention imples a include from
the projects own soruce tree. This prevents a conflict between Klippers
"sched.h" and "gpio.h" and <linux/gpio.h> and glibc <sched.h>.

Signed-off-by: Michael 'ASAP' Weinrich <michael@a5ap.net>
2024-04-03 22:53:09 -04:00
Robert Cambridge
75a40e817d stm32: fix support for USARTs on STM32G0B0
Signed-off-by: Robert Cambridge <robert@cambridge.me>
2024-04-03 12:01:00 -04:00
John Unland
5e280680c5 makefile: fix warning about lto serial compilation (#6543)
Signed-off-by: John Unland <junland.foss@gmail.com>
2024-04-02 22:08:35 -04:00
FOG_Yamato
67c152745e stm32: Add i2c3 bus to STM32H7 (#6541)
Signed-off-by: Balanuta Simion <simion@fogyamato.dev>
2024-04-02 22:02:22 -04:00
Kevin O'Connor
9e1cbdcee3 virtual_sdcard: Fix handling of unicode characters on Python2
Commit 600e89ae fixed unicode handling on Python3, but broke Python2
support.  Use an alternate implementation that should work for both
Python3 and Python2.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-02 21:59:55 -04:00
Kevin O'Connor
0aacbc3973 toolhead: Populate minimum_cruise_ratio to printer.configfile.settings
The default minimum_cruise_ratio setting does not get populated to the
printer.configfile.settings information due to the way the
max_accel_to_decel backwards compatibility support was implemented.
Slightly rework the config reading so that the default for
minimum_cruise_ratio is populated there.

Reported by @ReXT3D.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-02 21:54:43 -04:00
Kevin O'Connor
bedec55154 motion_report: Don't negate step_distance on steppers with inverted dir pin
When querying the stepper motion queue, the resulting "interval",
"count", and "add" are already normalized to the correct direction.
That is, the "count" field will be positive if moving in a positive
axis direction and negative if moving in the reverse direction.  So,
negating the step_distance field just complicates the readers.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-04-02 21:53:13 -04:00
Mathias Pihl
239f8e59e0 scripts: Mark install-ubuntu-22.04 as executable (#6505)
Signed-off-by: Mathias Pihl <mathias@pihlerne.dk>
2024-04-02 20:57:19 -04:00
TheParanoidEngineer
e37b007f67 docs: Update Measuring_Resonances.md (#6515)
Changed "libopenblas-base" to "libopenblas-dev"

Signed-off-by: Philip Weber <philiprweb@gmail.com>
2024-03-25 13:05:22 -04:00
Carl Richard Theodor Schneider
d9043345b6 linux: Allow for more i2c buses
Similar to commit df79893, this allows klipper to use up to
/dev/i2c-14. Similar to before, this limit is arbitrary.

This is required for some other SoCs, which have even
more i2c buses available, e.g. the rk3399:

$ ls -1 /dev/i2c-*
/dev/i2c-0
/dev/i2c-3
/dev/i2c-7

Signed-off-by: Carl Richard Theodor Schneider <dev.github@crtified.me>
2024-03-21 17:10:14 -04:00
Kevin O'Connor
40728e9231 motan: Support recording lis2dw and mpu9250 sensors from data_logger.py
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-03-20 19:40:23 -04:00
Kevin O'Connor
de1cf216ac docs: Sort axis_twist_compensation in G-Codes.md
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-03-20 19:40:23 -04:00
Kamil Domański
e6df93fcf8 tmc2240: add ADC voltage formatters
Signed-off-by: Kamil Domański <kamil@domanski.co>
2024-03-20 11:24:32 -04:00
Mad Beggar
235b75be3c hc32f460: Adding support for 100pin version of H32F460 (#6488)
Signed-off-by: Guillaume Giraudon <ggiraudon@prism19.com>
2024-03-19 16:16:42 -04:00
Markus Küffner
78a15b6d81 scripts: use greenlet version depending on python version
Signed-off-by: Markus Küffner <kueffner.markus@gmail.com>
2024-03-19 10:55:35 -04:00
BIGTREETECH
bfb71bc2dc stm32: Add i2c3_PC0_PC1 for stm32g0 (#6529)
Signed-off-by: Alan.Ma from BigTreeTech <tech@biqu3d.com>
2024-03-15 10:12:05 -04:00
Kevin O'Connor
09a78c31bb buildcommands: Add Klipper app name and license to mcu data dictionary
Add the Klipper name and license to the mcu data dictionary so that it
can be found in the flash.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-03-13 21:52:39 -04:00
Kevin O'Connor
0291a1554c configfile: Add support for reporting runtime_warnings via the API server
Add a new runtime_warning() method that will add a 'runtime_warning'
type message to the printer.configfile.warnings object.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-03-13 21:44:32 -04:00
Kevin O'Connor
d99d1a8463 mcu: Write a warning to the log if an incorrect mcu frequency is detected
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-03-13 21:44:32 -04:00
Kevin O'Connor
bb512ef5d7 heaters: Clarify reported stats after a shutdown
The pid logic can continue after a shutdown, even though the pin
commands sent to the mcu are ignored.  However, this behavior can
result in confusing "stats" messages in the log.  Explicitly disable
updates after a shutdown event so that the log statistics are more
clear.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-03-13 21:42:57 -04:00
Kevin O'Connor
bddefdde36 pid_calibrate: Fix PID_CALIBRATE command when used with heater_generic
Make sure the SAVE_CONFIG command saves the calculated PID parameters
to the correct config name.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-03-13 21:41:04 -04:00
Kevin O'Connor
0105aa330f toolhead: Replace max_accel_to_decel with minimum_cruise_ratio
The user facing max_accel_to_decel setting is complicated and
confusing.  Replace it with a new minimum_cruise_ratio parameter.
Internally this user-facing parameter will calculate the existing
low-level "accel_to_decel" mechanism.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-03-13 21:31:37 -04:00
Attila
18de421c4a stm32: Fix USART3 ALT pinout on STM32G0 (#6523)
Signed-off-by: Attila Rakosi <rattila5@hotmail.com>
2024-03-10 18:48:48 -04:00
Ulf D
71604b712a config: "static_value" in [output_pin enable_pin] is deprecated (#6520)
Signed-off-by: Ulf Dieckmann <1coderookie@quantentunnel.de>
2024-03-08 11:49:29 -05:00
Derek Kaser
31de734d19 config: update Kobra Plus build instructions and fan settings
Signed-off-by: Derek Kaser <derek.kaser@gmail.com>
2024-03-04 17:39:09 -05:00
Jake
b98375b360 avr: enable small code size options for atmega32u4
Signed-off-by: Jake Beju <jake.beju@gmail.com>
2024-03-02 18:47:16 -05:00
Pedro Lamas
a77d07907f docs: updates BED_MESH_CALIBRATE description
Adds the ADAPTIVE and ADAPTIVE_MARGIN parameters to the documentation.

Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
2024-02-20 12:09:57 -05:00
Dmitry Butyugin
28f06a104b shaper_calibrate: Fixed crashes in SHAPER_CALIBRATE and TEST_RESONANCES
Fixed crashes due to wrong parameter passed to the shaper selection function
and when the custom FREQ_END is specified.

Signed-off-by: Dmitry Butyugin <dmbutyugin@google.com>
2024-02-17 18:25:39 -05:00
Dmitry Butyugin
72b301a285 scripts: Added shaper tuning parameters to calibrate_shaper script
The added parameters include square_corner_velocity, shaper frequencies
to optimize, input shapers to test, input shaper damping ratio and
damping ratios to test. All these options can be useful for fine-tuning
the input shapers when the default suggestions generated by the tuning
script are not optimal.

Also the `SHAPER_CALIBRATE` command was modified to pass some of these
parameters to the shaper tuning routine. Specifically, square corner
velocity and the maximum tested frequency are used to adjust shaper
tuning and maximum acceleration recommendations.

Signed-off-by: Dmitry Butyugin <dmbutyugin@google.com>
2024-02-16 19:20:56 -05:00
Kevin O'Connor
4f00f21991 docs: Note removal of deprecated options in Config_Changes.md
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-02-15 12:40:10 -05:00
Kevin O'Connor
c92732e4f1 bed_mesh: Remove deprecated relative_reference_index
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-02-15 12:39:01 -05:00
Kevin O'Connor
2f7b234189 extruder: Remove deprecated commands and config
Remove the deprecated SET_EXTRUDER_STEP_DISTANCE and
SYNC_STEPPER_TO_EXTRUDER commands.  Remove the deprecated
shared_heater config option.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-02-15 12:35:19 -05:00
Kevin O'Connor
b2ac0f1ce3 heaters: Remove deprecated thermistor "NTC 100K beta 3950"
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-02-15 12:35:19 -05:00
Kevin O'Connor
9a940ffccb docs: Fix typo in Skew_Correction.md
Reported by @streetgt.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-02-15 12:29:13 -05:00
Eric Callahan
0aaabf1904 docs: update BED_MESH_OFFSET description
Add the ZFADE parameter to the documentation.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-02-15 12:03:41 -05:00
Eric Callahan
0cd16e956d bed_mesh: add ZFADE parameter to BED_MESH_OFFSET
When a ZFADE value is passed to BED_MESH_OFFSET it is used
to adjust how fade is applied.   This resolves issues with
fade when SET_GCODE_OFFSET is used during a tool change.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-02-15 12:03:41 -05:00
Anders Brujordet
1b24f6a2ad docs: Add required dependency to run numpy with python3 on RPI (#6491)
Signed-off-by: Anders Brujordet <anders@brujordet.no>
2024-02-13 19:18:08 -05:00
Kevin O'Connor
6ce6fbbce0 docs: Fix typo in Probe_Calibrate.md
Reported by @nmattia.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-02-09 19:21:34 -05:00
Eric Callahan
9f41f53c5e bed_mesh: fix profile_name reporting in get_status()
Adaptive meshing avoids saving the mesh after calibration to prevent
users from inadvertently overwriting an existing profile with an
adaptive mesh.  This introduced a change in behavior of how
get_status() reports the profile_name, as it can now be an empty
string when a mesh is active.

This patch assigns adaptive meshes a name with a unique postfix.
In addition, it moves profile name tracking from the profile manager
to the ZMesh class.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2024-01-28 13:43:08 -05:00
Eric Callahan
16a7b50ce9 bed_mesh: fix manual mode point generation
Do not generate points for the zero_reference_position or faulty_regions
when manual probing is requested.

Signed-off-by: Eric Callahan <arksine.code@gmail.com>
2024-01-28 13:43:08 -05:00
Kiswich
600e89ae8c virtual_sdcard: fix virtual SD file position count (#6472)
Signed-off-by: Zhang Qiwei <zxy16305@gmail.com>
2024-01-27 09:23:50 -05:00
voidtrance
5e3daa6f21 bed_mesh: Implement adaptive bed mesh (#6461)
Adaptive bed mesh allows the bed mesh algorithm
to probe only the area of the bed that is being
used by the current print.

It uses [exclude_objects] to get a list of the
printed objects and their area on the bed. It,
then, modifies the bed mesh parameters so only
the area used by the objects is measured.

Adaptive bed mesh works on both cartesian and
delta kinematics printers. On Delta printers,
the algorithm, adjusts the origin point and
radius in order to translate the area of the
bed being probe.

Signed-off-by: Mitko Haralanov <voidtrance@gmail.com>
Signed-off-by: Kyle Hansen <kyleisah@gmail.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-26 17:50:01 -05:00
Kevin O'Connor
5e433fff06 rp2040: Only change SPI settings while peripheral is disabled
Make sure to disable/enable the peripheral to ensure the clock
polarity is properly set prior to a change in CS.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-25 12:06:02 -05:00
Kevin O'Connor
f1982edcd5 rp2040: Load vectortable into ram
Load the interrupt vector table into ram at startup.  This reduces the
chance of a flash cache access causing timing instability.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-25 11:05:11 -05:00
Kevin O'Connor
44e79e0c37 rp2040: Run all code from ram
Place all normal code into ram.  This reduces the chance that rp2040
instruction cache misses could cause subtle timing issues.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-25 11:03:45 -05:00
Kevin O'Connor
23c5b20f5b rp2040: Always link using rp2040_link.lds.S
Use the rp2040 specific linker script even when using a bootloader.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-25 11:03:40 -05:00
Kevin O'Connor
55e46aa625 armcm_boot: Avoid invoking functions in reset_handler_stage_two()
Avoid calling memset() and memcpy() prior to copying the ram and
clearing the bss.  Also, place both ResetHandler() and
reset_handler_stage_two() in an explicit ".text.armcm_boot" linker
section.  These changes make it easier to support targets that want to
run all code in ram.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-25 11:02:49 -05:00
Kevin O'Connor
43a9685c58 mcu: Remove support for set_pwm() cycle_time parameter
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-23 20:04:03 -05:00
Kevin O'Connor
fd2feff67d pwm_cycle_time: New module for output pins with dynamic cycle times
Remove support for changing the cycle time of pwm pins from the
output_pin module.  Use a new pwm_cycle_time module that supports
setting dynamic cycle times.  This simplifies the output_pin code and
low-level pin update code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-23 20:04:03 -05:00
Kevin O'Connor
1baa45913f output_pin: Deprecate the maximum_mcu_duration parameter
Advise users to configure a pwm_tool config section if checking for
maximum mcu duration is required.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-23 20:04:03 -05:00
Kevin O'Connor
7abafb575b mcu: Remove support for "static" pins
Update static_digital_output.py to directly configure static digital
pins.  There are no other users of "static" pins, so remove that
support from mcu.py, replicape.py, and sx1509.py.  This simplifies the
low-level pin handling code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-23 20:04:03 -05:00
Kevin O'Connor
4115ea128a output_pin: Deprecate static_value parameter
Remove support for configuring "static" pins in output_pin module.  A
"static" pin only saves a few bytes of memory in the micro-controller.
The savings does not justify the increased code complexity.

Deprecate the static_value parameter to warn users.  In the interim, a
static_value parameter will set both value and shutdown_value
parameters.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-23 20:04:03 -05:00
Kevin O'Connor
2e8b54ae5f stm32: Remove product names from bootloader choices menu
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-22 18:58:41 -05:00
Jakub
f653db9c88 stm32: Add 36KiB bootloader offset option (#6449)
- This offset is used by Anycubic Kobra 2 Neo bootloader

Signed-off-by: Jakub Przystasz <jakub.przystasz@gmail.com>
2024-01-22 18:55:34 -05:00
BIGTREETECH
daf875e6e4 stm32g0: Disable internal pull-down resistors on UCPDx CCx pins, because klipper never uses UCPD (#6462)
Signed-off-by: Alan.Ma from BigTreeTech <tech@biqu3d.com>
2024-01-21 20:23:12 -05:00
Kevin O'Connor
d785b396a7 sensor_lis2dw: No need to schedule start of bulk reading
It's simpler and faster to enable the lis2dw in the python code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-19 11:55:15 -05:00
Kevin O'Connor
d853c19811 sensor_mpu9250: No need to schedule start of bulk reading
It's simpler and faster to enable the mpu9250 in the python code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-19 11:55:15 -05:00
Kevin O'Connor
6f0e91f69f sensor_adxl345: No need to schedule start of bulk reading
It's simpler and faster to enable the adxl345 in the python code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-19 11:55:15 -05:00
Kevin O'Connor
2dc4cfc5df bulk_sensor: Don't assume chip_clock is zero on start of queries
Send an explicit clock query in ChipClockUpdater to seed the initial
clock.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-19 11:55:15 -05:00
Kevin O'Connor
266e96621c sensor_bulk: New C file with helper code for sending bulk sensor measurements
Refactor the low-level "bulk sensor" management code in the mcu.  This
updates the sensor_adxl345.c, sensor_mpu9250.c, sensor_lis2dw.c, and
sensor_angle.c code to use the same "bulk sensor" messages.  All of
these sensors will now send "sensor_bulk_data" and
"sensor_bulk_status" messages.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-19 11:55:15 -05:00
Kevin O'Connor
dc6182f3b3 sensor_angle: No need to send messages when stopping queries
Simplify the mcu code as any messages are ignored by the host anyway.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-19 11:55:15 -05:00
Kevin O'Connor
95e1a290f1 sensor_lis2dw: No need to send messages when stopping queries
Simplify the mcu code as any messages are ignored by the host anyway.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-19 11:55:15 -05:00
Kevin O'Connor
5ff555a705 sensor_mpu9250: No need to send messages when stopping queries
Simplify the mcu code as any messages are ignored by the host anyway.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-19 11:55:15 -05:00
Kevin O'Connor
3275614b89 sensor_adxl345: No need to send messages when stopping queries
Simplify the mcu code as any messages are ignored by the host anyway.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-19 11:55:15 -05:00
Kevin O'Connor
83d0d2f19b mcu: Add send_wait_ack() support to CommandWrapper
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-19 11:55:15 -05:00
Kevin O'Connor
94719fe327 docs: Update to mkdocs to use latest jinj2 version
There is a jinja2 security advisory on the current Jinja2 version.
Klipper is not impacted by this advisory (as it does not run jinja2 on
any untrusted data), but there is no harm in updating.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-18 13:36:17 -05:00
Kevin O'Connor
1d92be71da toolhead: Rename note_kinematic_activity() to note_mcu_movequeue_activity()
Rename this function to make it more clear why it is called.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-18 12:25:08 -05:00
Kevin O'Connor
6cc409f6fb toolhead: Rename MoveQueue class to LookAheadQueue
Rename this class so that is is not confused with the mcu "move
queue".

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-18 12:16:47 -05:00
Kevin O'Connor
d633ef2cfc force_move: Fix missing call to note_kinematic_activity()
Commit 3d3b87f9 incorrectly removed the call to
note_kinematic_activity().  A call to toolhead.dwell() is not
sufficient to wake up the mcu move queue flushing.  The call to
note_kinematic_activity() is needed for that.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-18 11:24:07 -05:00
grnbrg
43d0dba4b4 config: Add Creality Ender 5 S1. (#6455)
Creality released the Ender 5 S1 model in November of 2022.  It
has enough hardware differences from the previous models that
that the existing Ender 5 configs are not compatible.  This
configuration is based on one provided by Creality that was then
tweaked and modified.  I have been using these values (plus some
additional entries) for about 6 months with no issues.

Signed-off-by: Brian Greenberg <grnbrg@grnbrg.org>
2024-01-18 10:13:54 -05:00
Kevin O'Connor
7a74888b43 toolhead: Extend flushing slightly past required time
There is no harm in enabling flushing for a little longer than
necessary.  In contrast, a slight rounding issue causing a message to
not get flushed properly could result in an error.  So, extend the
flushing time slightly to avoid potential issues.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-17 11:28:42 -05:00
Kevin O'Connor
3d3b87f97f toolhead: Ensure full kin_flush_delay after flush_step_generation()
Commit b7b13588 made it possible that the kinematic code could be
restarted after a flush_step_generation() call without a sufficient
delay.

Rename last_sg_flush_time to min_restart_time and use that to ensure
_calc_print_time() always pauses kin_flush_delay time since the last
flush_step_generation() call.

Also, update force_move to invoke flush_step_generation() after any
movements.  This is needed to ensure there is a sufficient delay
should force_move be called on a stepper motor that is part of the
toolhead kinematics and is using a step generation "scan time".

This fixes possible "internal error in stepcompress" reports when
using FORCE_MOVE.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-16 20:40:55 -05:00
Kevin O'Connor
447a88eb08 docs: Update Multi_MCU_Homing.md to note the importance of low-latency
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-16 14:19:11 -05:00
Kevin O'Connor
1ea9f3aa35 mcu: Increase trsync_state reporting during multi-mcu homing
The current code has the mcu report a trsync_state message every 10ms
and expects a time extension within 25ms.  However, this means that if
a single mcu->host report is lost then 20ms would elapse until the
next report, which would allow for only a 5ms round-trip time before a
timeout error is reported.

Increase the trsync_state timing so that a message is sent every
7.5ms.  This increases the total number of messages per second sent
from mcu to host to 133 (from 100).  With this change, a single lost
message would still allow for up to a 10ms round-trip time.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-16 14:19:11 -05:00
Kevin O'Connor
dab39c02cd mcu: Stagger trsync reporting time during multi-mcu homing
When multiple MCUs are involved in homing, stagger the scheduling of
the trsync_state report messages from each mcu.  Staggering helps
spread the bandwidth, helps reduce locking contention in the host, and
reduces the chance that intermittent latency could result in a
communication timeout.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-16 14:19:11 -05:00
Kevin O'Connor
96ab906946 sensor_mpu9250: Check for overflows on each query_mpu9250_status command
Move overflow detection from mp9250_stop() to
command_query_mpu9250_status().  Currently the host ignores any
contents returned from a stop request, so overflow reporting at that
point has limited utility.

In practice, this change will result in one additional i2c transaction
to the mpu9250 device every 100ms.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-07 11:59:59 -05:00
Kevin O'Connor
84aa3caa45 sensor_mpu9250: Simplify mp9250_query()
The mpu9250 code always reads from the sensor in 48 byte chunks and
always sends an mpu9250_data message immediately after that.  Make
that more clear in the querying code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-07 11:59:59 -05:00
Kevin O'Connor
49315b3cc4 sensor_mpu9250: Fix timing in command_query_mpu9250_status()
Commit 80a7744b optimized the fifo tracking code.  However, it
introduced an error in the time tracking in
command_query_mpu9250_status().  The purpose of that function is to
provide a precise timestamp of the total number of messages produced
at the time of that call.  Thus, the returned fifo value needs to be
the fifo level in the chip at the time of the call (not the value read
during previous checks).

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-07 11:59:59 -05:00
Kevin O'Connor
1a1568c38b mpu9250: Fix incorrect use of time.sleep()
It is not valid to call time.sleep() in the host python code (it could
causes glitches in other processing, and it does not ensure there is a
pause between operations on the mcu).

Use minclock instead of time.sleep() to ensure there is a sufficient
pause during chip startup.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-01-07 11:59:55 -05:00
marbocub
b50d6669a8 docs: Fix typo in Bed_Mesh.md (#6450)
the meaning and the illustration shows 13x9 mesh however the text was 13x8.

Signed-off-by: Mitsunori YOSHIDA <marbocub@gmail.com>
2024-01-06 17:26:48 -05:00
I3DBeeTech
0665dc8976 config: I3DBEEZ9 New board (#6447)
Signed-off-by: Venkata Kamesh <i3dbee@gmail.com>
2024-01-02 11:31:30 -05:00
Kevin O'Connor
92fe8f15b8 buttons: Fix possible ordering issue if a callback blocks
Invoke button callbacks directly from the background thread.  This
ensures that button notifications are delivered and delivered in the
correct order.  Previously, if a callback blocked, it was possible a
new update could start before the previous update was completed, which
could lead to lost events or out of order events.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-30 12:55:59 -05:00
Kevin O'Connor
25bc649cd2 toolhead: Make sure to flush history when in debug output mode
When in debugging "batch mode", use the existing method of keeping the
last 30 seconds of history from the furthest planned move time.  This
avoids keeping all moves in memory during a batch test.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-30 12:35:42 -05:00
Kevin O'Connor
9847b44901 toolhead: Avoid calling reactor.monotonic() on each _advance_flush_time()
Move calculation of clear_history_time to the callers of
_advance_flush_time() as a minor processing optimization.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-30 11:43:32 -05:00
Francois Chagnon
d7f6348ae6 toolhead: Keep stepcompress move history relative to current time (#6439)
Expire history relative to current time rather than last move in history queue

Signed-off-by: Francois Chagnon <fc@francoischagnon.net>
2023-12-30 11:34:21 -05:00
Kevin O'Connor
b502558052 bulk_sensor: Fix missing logging import
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-28 13:13:57 -05:00
Kevin O'Connor
6f686ddee3 bulk_sensor: Add some module level documentation
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
c716edafe2 bulk_sensor: Simplify the registration of internal clients in BatchBulkHelper
Previously, the BatchBulkHelper class was designed primarily to
register webhook clients, and internal clients used a wrapper class
that emulated a webhooks client.

Change BatchBulkHelper to support regular internal callbacks, and
introduce a new BatchWebhooksClient class that can translate these
internal callback to webhooks client messages.

This makes it easier to register internal clients that can process the
bulk messages every batch interval.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
3370134593 bulk_sensor: Rework APIDumpHelper() to BatchBulkHelper()
The APIDumpHelper class is mainly intended to help process messages in
batches.  Rework the class methods to make that more clear.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
95c753292d bulk_sensor: Minor code reorg to _stop() in APIDumpHelper()
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
acde3720a4 bulk_sensor: New add_mux_endpoint() helper function in APIDumpHelper
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
ffd44c0219 bulk_sensor: Move APIDumpHelper() from motion_report.py to bulk_sensor.py
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
f4c8f0bf88 angle: Define BYTES_PER_SAMPLE and SAMPLES_PER_BLOCK
This makes the code a little more readable.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
3f84501955 adxl345: Add a read_axes_map() helper function
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
43ce7c0b9a adxl345: No need to implement is_measuring()
The APIDumpHelper class already ensures that the start/stop callbacks
will only be called when needed.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
d6a4669ce0 bulk_sensor: Add new ChipClockUpdater helper class
All the accelerometers use a standard response for their query_status
messages.  Create a common helper class to process those responses.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
e67cbbe5c1 bulk_sensor: Add new BulkDataQueue class
Move the bulk sample queue collection to a new helper class in
bulk_sensor.py.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
978c294741 bulk_sensor: New file with helper code for reading bulk sensors
Move the ClockSyncRegression class from adxl345.py to a new
bulk_sensors.py file.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:47:21 -05:00
Kevin O'Connor
644f7e0872 toolhead: Simplify _advance_flush_time() sg_flush_time calculation
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:35:27 -05:00
Kevin O'Connor
fe56bf36c9 toolhead: Fix _calc_print_time() after G4 and SET_PRESSURE_ADVANCE
Commit b7b13588 changed the internal flush time tracking, but
introduced the possibility of motion restart occurring too close to
the last motion end in some rare cases.  This could result in
internal stepcompress errors.

Track the last step generation flush time (last_sg_flush_time) and use
when recalculating the next print_time.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-26 11:18:40 -05:00
Alex Maclean
77619e912c stm32: Fix CAN for STM32G4
Signed-off-by: Alex Maclean <monkeh@monkeh.net>
2023-12-21 20:58:57 -05:00
Alex Maclean
147492b253 stm32: Fix ADC for STM32G4
At least STM32G4 requires four ADC clock cycles between hardware
clearing ADCCAL and setting ADEN or the write disappears. Make a
tenacious write attempt.

Signed-off-by: Alex Maclean <monkeh@monkeh.net>
2023-12-21 20:58:57 -05:00
Alex Maclean
71ab6240f2 stm32: Fix STM32G4 USB
STM32G4 USB controller requires 8 or 16-bit access, not 32-bit

Signed-off-by: Alex Maclean <monkeh@monkeh.net>
2023-12-21 20:58:57 -05:00
Kevin O'Connor
547bfbf818 pwm_tool: Fix error reporting
References to pins.error are not valid as the pins module is not
imported.  Reported by @Piezoid.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-19 14:57:39 -05:00
Sami Haahtinen
3417940fd8 hall_filament_width_sensor: max filament diameter
Add support for maximum filament diameter to hall filament width sensor.
If the diameter of the filament diameter is larger than the limit, the
virtual runout sensor will trigger. The default value is set to maximum
flow adjustment threshold to prevent oversized filament from clogging.

Signed-off-by: Sami Haahtinen <ressu@ressukka.net>
2023-12-17 11:13:33 -05:00
Kevin O'Connor
2defd7374a pwm_tool: Add support for maximum_mcu_duration
Implement the maximum_mcu_duration config parameter along with its
associated queue flushing.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-16 15:44:17 -05:00
Kevin O'Connor
1e5f688b53 mcu: Add support for registering callbacks during move queue flush
Support notification callbacks each time the mcu move queue is
flushed.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-16 15:42:39 -05:00
Kevin O'Connor
19862bc3b7 toolhead: Track separate time for flush_step_generation() and need_flush_time
Introduce a new step_gen_time variable for flush_step_generation().
This allows need_flush_time to be set to future times without
interfering with flush_step_generation().

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-16 15:42:39 -05:00
Razor
f0753bd338 docs: add rp2040 to internal temp sensor list (#6426)
Signed-off-by: Levi Szabo <iamrazorshark@gmail.com>
2023-12-13 12:13:39 -05:00
Pedro Lamas
6676c1df86 gcode: expose status with available commands
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
2023-12-11 11:51:51 -05:00
Kevin O'Connor
2c2bb720fa adxl345: Simplify sequence and limit_count upconversion
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-10 14:59:18 -05:00
Kevin O'Connor
1a83845c9f angle: Simplify sequence upconversion
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-10 14:59:18 -05:00
Kevin O'Connor
fe7082e4a8 buttons: Simplify ack upconversion code
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-10 14:59:18 -05:00
Kevin O'Connor
c5bd813d8b clocksync: Simplify 32bit clock upconversion code
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-10 14:59:13 -05:00
Kevin O'Connor
62bf52bfcf serialqueue: Simplify sequence number upconversion
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-07 17:52:06 -05:00
Kevin O'Connor
99d7af87fd pwm_tool: Notify the toolhead that the move_queue needs to be flushed
Call toolhead.note_kinematic_activity() on each pin update to ensure
that those updates will be flushed properly.

This fixes "Timer too close" errors on SET_PIN commands that are
issued when the toolhead is idle.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-07 17:09:26 -05:00
Kevin O'Connor
c491ea669f toolhead: Support flushing even while lookahead queue is idle
Track a "NeedPrime" queue state instead of the "Flushed" state, and
continue running the background flushing timer as long as there may be
data in any of the move queues.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-07 17:09:26 -05:00
Kevin O'Connor
b7b13588c7 toolhead: Rework flushing to be based on mcu flush time
Rename last_kin_move_time to need_flush_time and rename
force_flush_time to last_flush_time to improve variable name clarity.

Move low-level flushing to new _advance_flush_time() so that it is
possible to flush the queues without needing to advance print_time.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-07 17:09:26 -05:00
Kevin O'Connor
9e574c3497 toolhead: Separate out priming flush notification to its own timer
Simplify the code by introducing a separate lookahead priming flush
timer.  After this change, the flush_timer is not active in any of the
special queuing states.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-07 17:09:26 -05:00
Kevin O'Connor
93cd8834f3 toolhead: Clarify internal toolhead "stall" and "pause" naming
Clarify the internal naming to make a more clear distinction between
"stalling" (input not coming fast enough) and "pausing" (the need to
hold up reading of input to avoid buffering too far into the future).

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-07 17:09:26 -05:00
Kevin O'Connor
bafb126abd toolhead: Remove undocumented buffer management config parameters
These internal low-level config parameters were never documented.
Going forward, developers may modify them by altering the internal
settings in toolhead.py.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-07 17:09:26 -05:00
Bassam
5bd32e2984 config: Update printer-sovol-sv06-2022.cfg x_offset (#6417)
Signed-off-by: Bassam Husain <bassamanator.2cj4t@simplelogin.com>
2023-12-06 12:57:10 -05:00
Bassam
d929be487b config: Update printer-sovol-sv06-2022.cfg to stock (#6416)
This printer is advertised as having a `220*220*250mm build volume`.

Signed-off-by: Bassam Husain <bassamanator.2cj4t@simplelogin.com>
2023-12-06 12:56:12 -05:00
Stefan Dej
7ed3c679dc config: Add LDO Leviathan v1.2 generic config
Signed-off-by: Stefan Dej <meteyou@gmail.com>
2023-12-06 12:51:07 -05:00
Kevin O'Connor
0ccf5f8e47 github: Temporarily disable close_reviewer_needed automation
Temporarily disable the closing of PRs marked as "reviewer needed".

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-05 17:57:11 -05:00
Will Puckett
fc102edc24 hard_pwm: Add pin defs for STM32F070 and STM32F072 (#6409)
Define hard_pwm pins for STM32F070 and STM32F072, and update KConfig accordingly. 

Signed-off-by: Will Puckett <willpuckett@gmail.com>
2023-12-05 17:44:51 -05:00
Kevin O'Connor
05d5451347 docs: Improve max_accel documentation in Config_Reference.md
Note that the max_accel parameter is the actual acceleration used in
most movements.

Note that the accel/velocity limits can be changed using the
SET_VELOCITY_LIMIT command.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-12-05 17:42:53 -05:00
Thijs Triemstra
795ce490a0 doc: updates to Installation.md (#6398)
omit rpi device version nrs

rpi2 or newer

Signed-off-by: Thijs Triemstra <info@collab.nl>
2023-12-03 21:10:59 -05:00
CODeRUS
38221df83a avr: enable small code size options for 328 and 328p (#6411)
Signed-off-by: Andrei Kozhevnikov <coderusinbox@gmail.com>
2023-11-29 11:31:49 -05:00
Kevin O'Connor
03f69cd81a tmc: Query latest value during _init_registers()
The set_register() code may block, and it therefore may be possible
that the loop in _init_registers() could occur in parallel with other
updates.  That could result in a "OrderedDict mutated during
iteration" error.

Avoid the error by querying the latest value during each iteration of
the loop.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-11-28 21:24:41 -05:00
Andrei Ignat
ea2f6bc0f5 exclude_object: Don't use gcmd.respond_error() (#6407)
gcmd.respond_error() has been deprecated: 61524542d2

Signed-off-by: Andrei Ignat <andrei@ignat.se>
2023-11-22 11:53:38 -05:00
Kevin O'Connor
bb4711c5d3 tmc5160: Increase maximum current error check
It's possible to build and configure tmc5160 drivers with external
mosfets that support more than 3 amps.  The actual maximum for tmc5160
drivers is dependent on how the board is wired and the mosfets used.
Increase the error check to 10 amps.  This error checking is primarily
intended to catch "obvious misconfigurations" (eg, specifying
milli-amps instead of amps), and the new value of 10 amps should
suffice for this task.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-11-20 18:15:46 -05:00
Kevin O'Connor
187cc2f1b8 configfile: Improve support for python3.12
It seems python3.12 has removed support for readfp() - use read_file()
instead.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-11-16 22:28:40 -05:00
vaxxi
3c8f202dfa bmp280: Add support for BMP180 sensor (#6370)
Extends the BMxx80 category with support for the older BMP180 sensor, providing temperature and humidity output.

Signed-off-by: VAXXi Popescu <github@vaxxi.net>
2023-11-16 22:27:44 -05:00
docgalaxyblock
83df4a8627 stm32: enable 64KiB bootloader offset for all F4
Signed-off-by: Joshua Schlicker <potter-91@web.de>
2023-11-16 22:09:59 -05:00
Kevin O'Connor
29b7550ce5 pwm_tool: Add support for high-speed PWM pin updates
The output_pin module is only capable of updating an output pin at
most once every 100ms.  Add a new pwm_tool module that is capable of
queuing updates in the micro-controller and thus allowing for much
higher update rates.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-11-16 22:07:15 -05:00
Kevin O'Connor
48a05eaa54 stepcompress: Add support for queuing messages that consume move queue space
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2023-11-16 22:07:15 -05:00
Thijs Triemstra
4688c21c54 klippy: Replace deprecated logger.warn with logger.warning (#6385)
Replace deprecated logger.warn with logger.warning

logger.warn will be removed in Python 3.13

Signed-off-by: Thijs Triemstra <info@collab.nl>
2023-11-16 22:06:13 -05:00
Herb McNew
3f8f30d612 config: Update printer-sovol-sv06-plus-2023.cfg (#6401)
In testing with a user on Discord we discovered the sensorless homing thresholds were out of line with what Sovol ships with Marlin. This aligns with their settings.

Signed-off-by: Herb McNew <herb.mcnew@gmail.com>
2023-11-16 21:57:45 -05:00
Herb McNew
74473322e5 config: Add Sovol SV06 Plus (#6397)
New configuration for the Sovol SV06 Plus
Initial pass at adding a printer configuration for the Sovol SV06
Plus based on the existing Sovol SV06 config. Updated for the larger
build volume, added filament runout sensor, and removed any references
to the LCD screen since the stock screen doesn't work with Klipper.

Signed-off-by: Herb McNew <herb.mcnew@gmail.com>
2023-11-14 13:15:46 -05:00
563 changed files with 161994 additions and 15254 deletions

View File

@@ -21,7 +21,7 @@ jobs:
run: ./scripts/ci-build.sh 2>&1
- name: Upload micro-controller data dictionaries
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: data-dict
path: ci_build/dict

View File

@@ -11,16 +11,14 @@ jobs:
steps:
- uses: actions/stale@v8
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: |
stale-pr-message: |
Hello,
It looks like there hasn't been any recent updates on this
Klipper github issue. If you created this issue and no
longer consider it open, then please login to github and
close the issue. Otherwise, if there is no further activity
on this thread then it will be automatically closed in a few
days.
github ticket. We prefer to only list tickets as "open" if
they are actively being worked on. Feel free to provide an
update on this ticket. Otherwise the ticket will be
automatically closed in a few days.
Best regards,
@@ -29,10 +27,10 @@ jobs:
PS: I'm just an automated script, not a human being.
exempt-issue-labels: 'enhancement,bug'
days-before-stale: 35
days-before-stale: 60
days-before-close: 7
days-before-pr-stale: -1
days-before-pr-close: -1
days-before-issue-stale: -1
days-before-issue-close: -1
# Close tickets marked with "not on github" label
close_not_on_github:
if: github.repository == 'Klipper3d/klipper'
@@ -62,54 +60,54 @@ jobs:
state: 'closed'
});
}
# Close tickets marked with "reviewer needed" label for 2+ weeks
close_reviewer_needed:
if: github.repository == 'Klipper3d/klipper'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
script: |
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'reviewer needed',
assignee: 'none',
per_page: 100,
page: 1
});
msg = "Unfortunately a reviewer has not assigned themselves to"
+ " this GitHub Pull Request and it is therefore being"
+ " closed. It is a good idea to move"
+ " further discussion to the [Klipper Discourse]"
+ "(https://www.klipper3d.org/Contact.html#discourse-forum)"
+ " server. Reviewers can reach out on that forum to let you"
+ " know if they are interested and when they are available."
+ "\n\n"
+ "Best regards,\n"
+ "~ Your friendly GitIssueBot"
+ "\n\n"
+ "PS: I'm just an automated script, not a human being.";
const expireMillis = 1000 * 60 * 60 * 24 * 14;
const curtime = new Date().getTime();
for (const issue of issues.data.values()) {
const updatetime = new Date(issue.updated_at).getTime();
if (curtime < updatetime + expireMillis)
continue;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: msg
});
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed'
});
}
# # Close tickets marked with "reviewer needed" label for 2+ weeks
# close_reviewer_needed:
# if: github.repository == 'Klipper3d/klipper'
# runs-on: ubuntu-latest
# steps:
# - uses: actions/github-script@v6
# with:
# script: |
# const issues = await github.rest.issues.listForRepo({
# owner: context.repo.owner,
# repo: context.repo.repo,
# state: 'open',
# labels: 'reviewer needed',
# assignee: 'none',
# per_page: 100,
# page: 1
# });
# msg = "Unfortunately a reviewer has not assigned themselves to"
# + " this GitHub Pull Request and it is therefore being"
# + " closed. It is a good idea to move"
# + " further discussion to the [Klipper Discourse]"
# + "(https://www.klipper3d.org/Contact.html#discourse-forum)"
# + " server. Reviewers can reach out on that forum to let you"
# + " know if they are interested and when they are available."
# + "\n\n"
# + "Best regards,\n"
# + "~ Your friendly GitIssueBot"
# + "\n\n"
# + "PS: I'm just an automated script, not a human being.";
# const expireMillis = 1000 * 60 * 60 * 24 * 14;
# const curtime = new Date().getTime();
# for (const issue of issues.data.values()) {
# const updatetime = new Date(issue.updated_at).getTime();
# if (curtime < updatetime + expireMillis)
# continue;
# await github.rest.issues.createComment({
# owner: context.repo.owner,
# repo: context.repo.repo,
# issue_number: issue.number,
# body: msg
# });
# await github.rest.issues.update({
# owner: context.repo.owner,
# repo: context.repo.repo,
# issue_number: issue.number,
# state: 'closed'
# });
# }
# Mark unassigned PRs that are idle for 2 weeks
mark_reviewer_needed:
if: github.repository == 'Klipper3d/klipper'
@@ -330,7 +328,7 @@ jobs:
}
# Lock closed issues after 6 months of inactivity and PRs after 1 year.
lock:
name: Lock Closed Issues
name: Lock Closed Tickets
if: github.repository == 'Klipper3d/klipper'
runs-on: ubuntu-latest
steps:

View File

@@ -29,10 +29,11 @@ dirs-y = src
cc-option=$(shell if test -z "`$(1) $(2) -S -o /dev/null -xc /dev/null 2>&1`" \
; then echo "$(2)"; else echo "$(3)"; fi ;)
CFLAGS := -I$(OUT) -Isrc -I$(OUT)board-generic/ -std=gnu11 -O2 -MD \
-Wall -Wold-style-definition $(call cc-option,$(CC),-Wtype-limits,) \
CFLAGS := -iquote $(OUT) -iquote src -iquote $(OUT)board-generic/ \
-std=gnu11 -O2 -MD -Wall \
-Wold-style-definition $(call cc-option,$(CC),-Wtype-limits,) \
-ffunction-sections -fdata-sections -fno-delete-null-pointer-checks
CFLAGS += -flto -fwhole-program -fno-use-linker-plugin -ggdb3
CFLAGS += -flto=auto -fwhole-program -fno-use-linker-plugin -ggdb3
OBJS_klipper.elf = $(patsubst %.c, $(OUT)src/%.o,$(src-y))
OBJS_klipper.elf += $(OUT)compile_time_request.o

View File

@@ -4,15 +4,14 @@ Welcome to the Klipper project!
https://www.klipper3d.org/
Klipper is a 3d-Printer firmware. It combines the power of a general
purpose computer with one or more micro-controllers. See the
The Klipper firmware controls 3d-Printers. It combines the power of a
general purpose computer with one or more micro-controllers. See the
[features document](https://www.klipper3d.org/Features.html) for more
information on why you should use Klipper.
information on why you should use the Klipper software.
To begin using Klipper start by
[installing](https://www.klipper3d.org/Installation.html) it.
Start by [installing Klipper software](https://www.klipper3d.org/Installation.html).
Klipper is Free Software. See the [license](COPYING) or read the
[documentation](https://www.klipper3d.org/Overview.html). We depend on
the generous support from our
Klipper software is Free Software. See the [license](COPYING) or read
the [documentation](https://www.klipper3d.org/Overview.html). We
depend on the generous support from our
[sponsors](https://www.klipper3d.org/Sponsors.html).

223
config/generic-I3DBEEZ9.cfg Normal file
View File

@@ -0,0 +1,223 @@
# This file contains common pin mappings for the I3DBEEZ9 V1.0.
# To use this config, the firmware should be compiled for the
# STM32F407 with a "32KiB bootloader".
# The "make flash" command does not work on the I3DBEEZ9. 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 I3DBEEZ9
# with that SD card.
# See docs/Config_Reference.md for a description of parameters.
[stepper_x]
step_pin: PE9
dir_pin: PF1
enable_pin: !PF2
microsteps: 16
rotation_distance: 40
endstop_pin: PB10
position_endstop: 0
position_max: 200
homing_speed: 50
[stepper_y]
step_pin: PE11
dir_pin: PE1
enable_pin: !PD7
microsteps: 16
rotation_distance: 40
endstop_pin: PE12
position_endstop: 0
position_max: 200
homing_speed: 50
[stepper_z]
step_pin: PE13
dir_pin: PC2
enable_pin: !PC0
microsteps: 16
rotation_distance: 8
endstop_pin: PG8
position_endstop: 0
position_max: 200
[extruder]
step_pin: PE14
dir_pin: PA0
enable_pin: !PC3
microsteps: 16
rotation_distance: 33.500
nozzle_diameter: 0.400
filament_diameter: 1.750
heater_pin: PB1 # Heat0
sensor_pin: PF4 # T1 Header
sensor_type: EPCOS 100K B57560G104F
control: pid
pid_Kp: 22.2
pid_Ki: 1.08
pid_Kd: 114
min_temp: 0
max_temp: 250
#[extruder1]
#step_pin: PD15
#dir_pin: PE7
#enable_pin: !PA3
#heater_pin: PD14 # Heat1
#sensor_pin: PF5 # T2
#...
#[extruder2]
#step_pin: PD13
#dir_pin: PG9
#enable_pin: !PF0
#heater_pin: PB0 # Heat2
#sensor_pin: PF6 # T3
#...
#[stepper_z1]
#step_pin: PE4
#dir_pin: PE3
#enable_pin: !PC13
#microsteps: 16
#rotation_distance: 8
#endstop_pin: PD0
#position_endstop: 0.5
#position_max: 200
[heater_bed]
heater_pin: PD12
sensor_pin: PF3 # T0
sensor_type: ATC Semitec 104GT-2
control: watermark
min_temp: 0
max_temp: 130
[fan]
pin: PC8
[heater_fan fan1]
pin: PE5
#[heater_fan fan2]
#pin: PE6
[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
########################################
# TMC2208 configuration
########################################
#[tmc2208 stepper_x]
#uart_pin: PA15
#run_current: 0.800
#stealthchop_threshold: 999999
#[tmc2208 stepper_y]
#uart_pin: PB8
#run_current: 0.800
#stealthchop_threshold: 999999
#[tmc2208 stepper_z]
#uart_pin: PB9
#run_current: 0.650
#stealthchop_threshold: 999999
#[tmc2208 extruder]
#uart_pin: PB3
#run_current: 0.800
#stealthchop_threshold: 999999
#[tmc2208 extruder1]
#uart_pin: PG15
#run_current: 0.800
#stealthchop_threshold: 999999
#[tmc2208 extruder2]
#uart_pin: PG12
#run_current: 0.800
#stealthchop_threshold: 999999
#[tmc2208 stepper_z1]
#uart_pin: PE2
#run_current: 0.650
#stealthchop_threshold: 999999
########################################
# TMC2130 configuration
########################################
#[tmc2130 stepper_x]
#cs_pin: PA15
#spi_bus: spi3a
##diag1_pin: PB10
#run_current: 0.800
#stealthchop_threshold: 999999
#[tmc2130 stepper_y]
#cs_pin: PB8
#spi_bus: spi3a
##diag1_pin: PE12
#run_current: 0.800
#stealthchop_threshold: 999999
#[tmc2130 stepper_z]
#cs_pin: PB9
#spi_bus: spi3a
##diag1_pin: PG8
#run_current: 0.650
#stealthchop_threshold: 999999
#[tmc2130 extruder]
#cs_pin: PB3
#spi_bus: spi3a
##diag1_pin: PE15
#run_current: 0.800
#stealthchop_threshold: 999999
#[tmc2130 extruder1]
#cs_pin: PG15
#spi_bus: spi3a
##diag1_pin: PE10
#run_current: 0.800
#stealthchop_threshold: 999999
#[tmc2130 extruder2]
#cs_pin: PG12
#spi_bus: spi3a
##diag1_pin: PG5
#run_current: 0.800
#stealthchop_threshold: 999999
#[tmc2130 stepper_z1]
#cs_pin: PE2
#spi_bus: spi3a
##diag1_pin: PD0
#run_current: 0.650
#stealthchop_threshold: 999999
########################################
# EXP1 / EXP2 (display) pins
########################################
[board_pins]
aliases:
# EXP1 header
EXP1_1=PG4, EXP1_3=PD11, EXP1_5=PG2, EXP1_7=PG6, EXP1_9=<GND>,
EXP1_2=PA8, EXP1_4=PD10, EXP1_6=PG3, EXP1_8=PG7, EXP1_10=<5V>,
# EXP2 header
EXP2_1=PB14, EXP2_3=PG10, EXP2_5=PF11, EXP2_7=PF12, EXP2_9=<GND>,
EXP2_2=PB13, EXP2_4=PB12, EXP2_6=PB15, EXP2_8=<RST>, EXP2_10=PF13
# Pins EXP2_1, EXP2_6, EXP2_2 are also MISO, MOSI, SCK of bus "spi2"
# See the sample-lcd.cfg file for definitions of common LCD displays.

View File

@@ -85,11 +85,10 @@ uart_pin: PC11
tx_pin: PC10
uart_address: 3
run_current: 0.650
stealthchop_threshold: 999999
[heater_bed]
heater_pin: PC9
sensor_type: ATC Semitec 104GT-2
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC4
control: pid
pid_Kp: 54.027

View File

@@ -95,4 +95,4 @@ max_z_accel: 100
aliases:
EXP1_1=PC6,EXP1_3=PB10,EXP1_5=PB14,EXP1_7=PB12,EXP1_9=<GND>,
EXP1_2=PB2,EXP1_4=PB11,EXP1_6=PB13,EXP1_8=PB15,EXP1_10=<5V>,
PROBE_IN=PB0,PROBE_OUT=PB1,FIL_RUNOUT=PC6
PROBE_IN=PB0,PROBE_OUT=PB1,FIL_RUNOUT=PA4

View File

@@ -0,0 +1,241 @@
# This file contains common pin mappings for the LDO Leviathan v1.2.
# To use this config, during "make menuconfig", select "Enable
# low-level configuration options", select the STM32F446 micro-controller,
# select a "32KiB bootloader", and select a "12Mhz crystal".
# See docs/Config_Reference.md for a description of parameters.
# HV-STEPPER-0
[stepper_x]
step_pin: PB10
dir_pin: PB11
enable_pin: !PG0
microsteps: 32
rotation_distance: 40
endstop_pin: PC1 # X-ENDSTOP
position_endstop: 0
position_max: 200
homing_speed: 50
[tmc5160 stepper_x]
spi_bus: spi4
cs_pin: PE15
#diag0_pin: PG1
interpolate: False
sense_resistor: 0.075
run_current: 0.8
stealthchop_threshold: 0
# HV-STEPPER-1
[stepper_y]
step_pin: PF15
dir_pin: PF14
enable_pin: !PE9
microsteps: 32
rotation_distance: 40
endstop_pin: PC2 # Y-ENDSTOP
position_endstop: 0
position_max: 200
homing_speed: 50
[tmc5160 stepper_y]
spi_bus: spi4
cs_pin: PE11
#diag0_pin: PE10
interpolate: False
sense_resistor: 0.075
run_current: 0.8
stealthchop_threshold: 0
# STEPPER-0
[stepper_z]
step_pin: PD4
dir_pin: PD3
enable_pin: !PD7
microsteps: 32
rotation_distance: 8
endstop_pin: PC3 # Z-ENDSTOP
position_endstop: 0
position_max: 200
[tmc2209 stepper_z]
uart_pin: PD5
#diag_pin: PD6
interpolate: False
run_current: 0.6
stealthchop_threshold: 999999
# The Leviathan was developed for Voron printers. It therefore has several
# steppers for the z-axes, but only one heater for one extruder.
# STEPPER-1
#[stepper_z1]
#step_pin: PC12
#dir_pin: PC11
#enable_pin: !PD2
#microsteps: 32
#rotation_distance: 8
#
#[tmc2209 stepper_z1]
#uart_pin: PD5
##diag_pin: PD6
#interpolate: False
#run_current: 0.6
#stealthchop_threshold: 999999
# STEPPER-2
#[stepper_z2]
#step_pin: PC9
#dir_pin: PC8
#enable_pin: !PC10
#microsteps: 32
#rotation_distance: 8
#
#[tmc2209 stepper_z2]
#uart_pin: PA8
##diag_pin: PA15
#interpolate: False
#run_current: 0.6
#stealthchop_threshold: 999999
# STEPPER-3
#[stepper_z3]
#step_pin: PG7
#dir_pin: PG6
#enable_pin: !PC7
#microsteps: 32
#rotation_distance: 8
#
#[tmc2209 stepper_z2]
#uart_pin: PG8
##diag_pin: PC6
#interpolate: False
#run_current: 0.6
#stealthchop_threshold: 999999
# STEPPER-4
[extruder]
step_pin: PD10
dir_pin: PD9
enable_pin: !PD13
microsteps: 32
rotation_distance: 22.67
nozzle_diameter: 0.400
filament_diameter: 1.750
heater_pin: PG10 # HEATER
sensor_pin: PA2 # TH1
sensor_type: ATC Semitec 104NT-4-R025H42G
pullup_resistor: 2200
control: pid
pid_Kp: 36.787
pid_Ki: 4.716
pid_Kd: 71.735
min_temp: 0
max_temp: 250
[tmc2209 stepper_z]
uart_pin: PD11
#diag_pin: PD12
interpolate: False
run_current: 0.5
stealthchop_threshold: 0
#[filament_switch_sensor material_0]
#switch_pin: PC0 # FILAMENT-SENSOR
[heater_bed]
heater_pin: PG11 # HEATBED
sensor_pin: PA1 # TH0
sensor_type: ATC Semitec 104GT-2
pullup_resistor: 2200
control: pid
pid_kp: 56.723
pid_ki: 5.561
pid_kd: 144.642
min_temp: 0
max_temp: 130
[fan]
pin: PB7 # FAN0
#tachometer_pin: PB0
#[heater_fan fan1]
#pin: PB3
#tachometer_pin: PB4
#[heater_fan fan2]
#pin: PF7
#tachometer_pin: PF6
#[controller_fan fan3]
#pin: PF9
#tachometer_pin: PF8
[mcu]
serial: /dev/serial/by-id/usb-Klipper_Klipper_firmware_12345-if00
# CAN bus is also available on this board
[printer]
kinematics: cartesian
max_velocity: 300
max_accel: 3000
max_z_velocity: 5
max_z_accel: 100
[board_pins]
aliases:
# EXP1 header
EXP1_1=PG9, EXP1_2=PG12,
EXP1_3=PG13, EXP1_4=PG14,
EXP1_5=PC13, EXP1_6=PC14,
EXP1_7=PC15, EXP1_8=PF0,
EXP1_9=<GND>, EXP1_10=<5V>,
# EXP2 header
EXP2_1=PA6, EXP2_2=PA5,
EXP2_3=PE2, EXP2_4=PE4,
EXP2_5=PE3, EXP2_6=PA7,
EXP2_7=PE5, EXP2_8=<RST>,
EXP2_9=<GND>, EXP2_10=PE4,
# See the sample-lcd.cfg file for definitions of common LCD displays.
# EXTENSION PORT
EXP3_1=<5V>, EXP3_2=<5V>, # max. 0.5A
EXP3_3=<GND>, EXP3_4=<GND>,
EXP3_5=<3.3V>, EXP3_6=<3.3V>, # max. 0.5A
EXP3_7=PF5, EXP3_8=PF4,
EXP3_9=PF3, EXP3_10=PF2,
EXP3_11=PC4, EXP3_12=PC5, # EXP3_11 and EXP3_12 are ADC inputs
EXP3_13=PB0, EXP3_14=PB1, # EXP3_13 and EXP3_14 are ADC inputs
EXP3_15=PE8, EXP3_16=PE7, # EXP3_15 is UART5_TX, EXP3_16 is UART5_RX
EXP3_17=PG5, EXP3_18=PG4,
EXP3_19=PG3, EXP3_20=PG2,
EXP3_21=PD15, EXP3_22=PD14,
EXP3_23=PB15, EXP3_24=PB14, # EXP3_23 is SPI2_MOSI
# EXP3_24 is SPI2_MISO
EXP3_25=PB13, EXP3_26=PB12, # EXP3_25 is SPI2_SCK + CAN2_TX
# EXP3_26 is SPI2_CS + CAN2_RX
EXP3_27=<GND>, EXP3_28=<GND>,
EXP3_29=<24V>, EXP3_30=<24V>, # max. 0.5A
#[probe]
#sensor_pin: PF1 # Z-PROBE
#z_offset: 0
#[led my_led]
#white_pin: PE6 # LED-Strip
#[neopixel my_neopixel]
#pin: PF10 # NEOPIXEL
#[temperature_sensor TH2]
#sensor_type: ATC Semitec 104GT-2
#sensor_pin: PA0 # TH2
#pullup_resistor: 2200
#[temperature_sensor TH3]
#sensor_type: ATC Semitec 104GT-2
#sensor_pin: PA3 # TH3
#pullup_resistor: 2200

View File

@@ -0,0 +1,232 @@
# This file contains common pin mappings for the Mellow Fly-E3-v2.
# To use this config, the firmware should be compiled for the
# STM32F407 with a "32KiB bootloader".
# The "make flash" command does not work on the Fly-E3-v2. Instead,
# after running "make", copy the generated "out/klipper.bin" file to a
# file named "firmware.bin" or "klipper.bin" on an SD card and then restart the Fly-E3-v2
# with that SD card.
# See docs/Config_Reference.md for a description of parameters.
[mcu]
serial: /dev/serial/by-id/usb-Klipper_stm32f407xx_27004A001851323333353137-if00
[stepper_x]
step_pin: PE5
dir_pin: PC0
enable_pin: !PC1
microsteps: 16
rotation_distance: 30
full_steps_per_rotation: 200
endstop_pin: PE7 #X-STOP
position_endstop: 0
position_max: 200
homing_speed: 50
second_homing_speed: 10
homing_retract_dist: 5.0
homing_positive_dir: false
step_pulse_duration: 0.000004
[stepper_y]
step_pin: PE4
dir_pin: !PC13
enable_pin: !PC14
microsteps: 16
rotation_distance: 30
full_steps_per_rotation: 200
endstop_pin: PE8 #Y-STOP
position_endstop: 0
position_max: 200
homing_speed: 50
second_homing_speed: 10
homing_retract_dist: 5.0
homing_positive_dir: false
step_pulse_duration: 0.000004
[stepper_z]
step_pin: PE1
dir_pin: !PB7
enable_pin: !PE3
microsteps: 16
rotation_distance: 30
full_steps_per_rotation: 200
endstop_pin: PE9 #Z-STOP
position_min: 0
position_endstop: 0
position_max: 200
homing_speed: 5
second_homing_speed: 3
homing_retract_dist: 5.0
homing_positive_dir: false
step_pulse_duration: 0.000004
[extruder]
step_pin: PE2
dir_pin: PD5
enable_pin: !PD6
microsteps: 16
rotation_distance: 33.500
nozzle_diameter: 0.400
filament_diameter: 1.750
heater_pin: PC6 #E0
########################################
# Extruder 100K thermistor configuration
########################################
sensor_type: ATC Semitec 104GT-2
sensor_pin: PC4 #T0 TEMP
control: pid
pid_Kp: 22.2
pid_Ki: 1.08
pid_Kd: 114
min_temp: 0
max_temp: 275
########################################
# Extruder MAX31865 PT100 2 wire config
########################################
# sensor_type: MAX31865
# sensor_pin: PD15 #PT-100
# spi_speed: 4000000
# spi_software_sclk_pin: PD12
# spi_software_mosi_pin: PD11
# spi_software_miso_pin: PD13
# rtd_nominal_r: 100
# rtd_reference_r: 430
# rtd_num_of_wires: 2
# rtd_use_50Hz_filter: True
min_temp: 0
max_temp: 300
#[extruder1]
#step_pin: PE0
#dir_pin: PD1
#enable_pin: !PD3
#microsteps: 16
#heater_pin: PC7 #E1
#sensor_pin: PC5 #T1 TEMP
########################################
# TMC2209 configuration
########################################
[tmc2209 stepper_x]
uart_pin: PC15
interpolate: False
run_current: 0.3
sense_resistor: 0.110
stealthchop_threshold: 999999
[tmc2209 stepper_y]
uart_pin: PB6
interpolate: False
run_current: 0.3
sense_resistor: 0.110
stealthchop_threshold: 999999
[tmc2209 stepper_z]
uart_pin: PD7
interpolate: False
run_current: 0.4
sense_resistor: 0.110
stealthchop_threshold: 999999
[tmc2209 extruder]
uart_pin: PD4
interpolate: False
run_current: 0.27
sense_resistor: 0.075
stealthchop_threshold: 999999
#[tmc2209 extruder1]
#uart_pin: PD0
#interpolate: False
#run_current: 0.27
#sense_resistor: 0.075
#stealthchop_threshold: 999999
#######################################
# Heated Bed
#######################################
[heater_bed]
heater_pin: PB0 #BED
sensor_type: Generic 3950
sensor_pin: PB1 #B-TEMP
max_power: 1.0
min_temp: 0
max_temp: 120
control: pid
pid_kp: 58.437
pid_ki: 2.347
pid_kd: 363.769
#######################################
# LIGHTING
#######################################
#[led Toolhead]
#white_pin: PA2 #FAN2
#cycle_time: 0.010
#initial_white: 0
#######################################
# COOLING
#######################################
[heater_fan hotend_fan]
pin: PA1 #FAN1
max_power: 1.0
kick_start_time: 0.5
heater: extruder
heater_temp: 50
fan_speed: 1.0
[controller_fan controller_fan]
pin: PA0 #FAN0
max_power: 1.0
kick_start_time: 0.5
heater: extruder
stepper: stepper_x, stepper_y, stepper_z
fan_speed: 1.0
idle_timeout: 60
[fan]
pin: PA3 #FAN3
max_power: 1.0
off_below: 0.2
[temperature_sensor Mellow_Fly_E3_V2]
sensor_type: temperature_mcu
min_temp: 5
max_temp: 80
[printer]
kinematics: cartesian
max_velocity: 300
max_accel: 3000
max_z_velocity: 50
max_z_accel: 100
########################################
# EXP1 / EXP2 (display) pins
########################################
[board_pins]
aliases:
EXP1_1=PD10, EXP1_3=PA8, EXP1_5=PE15, EXP1_7=PA14, EXP1_9=<GND>,
EXP1_2=PA9, EXP1_4=PA10, EXP1_6=PE14, EXP1_8=PA13, EXP1_10=<5V>,
# EXP2 header
EXP2_1=PA6, EXP2_3=PB11, EXP2_5=PB10, EXP2_7=PE13, 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.
#######################################
# BL-Touch
#######################################
#[bltouch]
#sensor_pin: PC2
#control_pin: PE6
#z_offset: 0

View File

@@ -84,7 +84,7 @@ pwm: True
scale: 2.0
cycle_time: .000030
hardware_pwm: True
static_value: 1.3
value: 1.3
[output_pin stepper_z_current]
pin: PL4
@@ -92,7 +92,7 @@ pwm: True
scale: 2.0
cycle_time: .000030
hardware_pwm: True
static_value: 1.3
value: 1.3
[output_pin stepper_e_current]
pin: PL5
@@ -100,7 +100,7 @@ pwm: True
scale: 2.0
cycle_time: .000030
hardware_pwm: True
static_value: 1.25
value: 1.25
[static_digital_output stepper_config]
pins:

View File

@@ -97,7 +97,7 @@ max_z_accel: 30
[output_pin case_light]
pin: PH5
static_value: 1.0
value: 1.0
# Motor current settings.
[output_pin stepper_xy_current]
@@ -107,7 +107,7 @@ scale: 2.000
# Max power setting.
cycle_time: .000030
hardware_pwm: True
static_value: 1.200
value: 1.200
# Power adjustment setting.
[output_pin stepper_z_current]
@@ -116,7 +116,7 @@ pwm: True
scale: 2.000
cycle_time: .000030
hardware_pwm: True
static_value: 1.200
value: 1.200
[output_pin stepper_e_current]
pin: PL3
@@ -124,4 +124,4 @@ pwm: True
scale: 2.000
cycle_time: .000030
hardware_pwm: True
static_value: 1.250
value: 1.250

View File

@@ -89,7 +89,7 @@ pwm: True
scale: 2.0
cycle_time: .000030
hardware_pwm: True
static_value: 1.3
value: 1.3
[output_pin stepper_z_current]
pin: PL4
@@ -97,7 +97,7 @@ pwm: True
scale: 2.0
cycle_time: .000030
hardware_pwm: True
static_value: 1.3
value: 1.3
[output_pin stepper_e_current]
pin: PL3
@@ -105,7 +105,7 @@ pwm: True
scale: 2.0
cycle_time: .000030
hardware_pwm: True
static_value: 1.25
value: 1.25
[display]
lcd_type: st7920

View File

@@ -126,7 +126,6 @@ restart_method: arduino
kinematics: cartesian
max_velocity: 150
max_accel: 3000
max_accel_to_decel: 1500
max_z_velocity: 7
max_z_accel: 50
square_corner_velocity: 5

View File

@@ -118,7 +118,7 @@ cycle_time: 0.00005 #20kHz
[output_pin enable_pin]
pin: PB6
static_value: 1
value: 1
#This pin enables the bed, hotend, extruder fan, part fan.
[mcu]

View File

@@ -14,6 +14,7 @@
# To build the firmware, use the following configuration:
# - Micro-controller: Huada Semiconductor HC32F460
# - Communication interface: Serial (PA3 & PA2) - Anycube
# - Clock Speed: 200 MHz
#
# Installation:
# 1. Rename the klipper bin to `firmware.bin` and copy it to an SD Card.
@@ -144,10 +145,9 @@ max_temp: 120
pause_on_runout: True
switch_pin: !PC13
[heater_fan controller_fan]
[controller_fan controller_fan]
pin: PA14
heater: heater_bed
heater_temp: 45.0
[heater_fan hotend_fan]
pin: PA13

View File

@@ -0,0 +1,126 @@
# This file contains pin mappings for the Artillery Genius Pro (2022)
# with a Artillery_Ruby-v1.2 board. To use this config, during "make menuconfig"
# select the STM32F401 with "No bootloader" and USB (on PA11/PA12)
# communication.
# To flash this firmware, set the physical bridge between +3.3V and Boot0 PIN
# on Artillery_Ruby mainboard. Then run the command:
# make flash FLASH_DEVICE=/dev/serial/by-id/usb-Klipper_stm32f401xc_*-if00
# See docs/Config_Reference.md for a description of parameters.
[extruder]
max_extrude_only_distance: 700.0
step_pin: PA7
dir_pin: PA6
enable_pin: !PC4
microsteps: 16
rotation_distance: 7.1910
nozzle_diameter: 0.400
filament_diameter: 1.750
heater_pin: PC9
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC0
min_temp: 0
max_temp: 250
control: pid
pid_Kp: 23.223
pid_Ki: 1.518
pid_Kd: 88.826
[stepper_x]
step_pin: !PB14
dir_pin: PB13
enable_pin: !PB15
microsteps: 16
rotation_distance: 40
endstop_pin: !PA2
position_endstop: 0
position_max: 220
homing_speed: 60
[stepper_y]
step_pin: PB10
dir_pin: PB2
enable_pin: !PB12
microsteps: 16
rotation_distance: 40
endstop_pin: !PA1
position_endstop: 0
position_max: 220
homing_speed: 60
[stepper_z]
step_pin: PB0
dir_pin: !PC5
enable_pin: !PB1
microsteps: 16
rotation_distance: 8
endstop_pin: probe:z_virtual_endstop
position_max: 250
position_min: -5
[heater_bed]
heater_pin: PA8
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC1
min_temp: 0
max_temp: 130
control: pid
pid_Kp: 23.223
pid_Ki: 1.518
pid_Kd: 88.826
[bed_screws]
screw1: 38,45
screw2: 180,45
screw3: 180,180
screw4: 38,180
[fan]
pin: PC8
off_below: 0.1
[heater_fan hotend_fan]
pin: PC7
heater: extruder
heater_temp: 50.0
[controller_fan stepper_fan]
pin: PC6
idle_timeout: 300
[mcu]
serial: /dev/serial/by-id/usb-Klipper_stm32f401xc_
[printer]
kinematics: cartesian
max_velocity: 500
max_accel: 4000
max_z_velocity: 50
square_corner_velocity: 5.0
max_z_accel: 100
[bltouch]
sensor_pin: PC2
control_pin: PC3
x_offset:27.25
y_offset:-12.8
z_offset: 0.25
speed:10
samples:1
samples_result:average
[bed_mesh]
speed: 800
mesh_min: 30, 20
mesh_max: 210, 200
probe_count: 5,5
algorithm: bicubic
move_check_distance: 3.0
[safe_z_home]
home_xy_position: 110,110
speed: 100
z_hop: 10
z_hop_speed: 5

View File

@@ -0,0 +1,188 @@
# For the Artillery Sidewinder X3 Pro/Plus that came factory installed with V1.29 firmware, follow these steps.
# - Compile with the processor model STM32F401.
# - Select the 48KiB bootloader,
# - Select USB PA11/PA12 for USB communication interface.
# - Select USART2 PA3/PA2 for UART communication via the Wi-Fi Tx/Rx pins
# To set 48KiB bootloader, you need to make a change to make menuconfig Kconfig file
# Here is a link to a how-to video: https://youtu.be/dpc76zN7Dh0
# Rename klipper.bin to yuntu.bin
# Copy the file out/yuntu.bin to an SD card and then restart the printer with that SD card
#
# For models that did not come with V1.29 installed
# - Compile with the processor model STM32F401.
# - Select the NO BOOTLOADER
# - Select USB PA11/PA12 for USB communication interface.
# - Select USART2 PA3/PA2 for UART communication via the Wi-Fi Tx/Rx pins
# - quit, save, make
# - Connect your printer to a computer running Pronterface, Octoprint, Repetier, BedLeveler5000 (anything with Console capability)
# - Power on the machine and send M997 through console into Marlin, this will put the board into "DFU" mode
# - DO NOT TURN OFF THE PRINTER
# - Connect your Linux/Klipper device to the USB port
# - Run lsusb and verify that the STM32 DFU device is visible (Bus 001 Device 006: ID 0483:df11 STMicroelectronics STM Device in DFU Mode)
# - Run sudo make flash 0483:df11
# - Run lsusb again and there should be two devices:
# Bus 001 Device 007: ID 1d50:614e OpenMoko, Inc. stm32f401xc
# Bus 001 Device 003: ID 0cf3:e010 Qualcomm Atheros Communications stm32f401xc
# See docs/Config_Reference.md for a description of parameters.
[mcu]
serial: /dev/ttyACM0
restart_method: command
[printer]
kinematics: cartesian
max_velocity: 300
max_accel: 3000
max_z_velocity: 15
max_z_accel: 100
square_corner_velocity: 5
[led LED_Light]
white_pin: PC2
initial_white: 1.0
[neopixel hotend_neopixel]
pin: PD2
color_order: GRB
initial_RED: 1.0
initial_GREEN: 1.0
initial_BLUE: 1.0
[stepper_x]
step_pin: PA8
dir_pin: PC9
enable_pin: !PA15
microsteps: 16
rotation_distance: 40
endstop_pin: !PB9
position_min: 0
position_endstop: 0
position_max: 315
homing_speed: 50
[stepper_y]
step_pin: PC7
dir_pin: !PC6
enable_pin: !PC8
microsteps: 16
rotation_distance: 40
endstop_pin: !PB8
position_endstop: 0
position_max: 315
homing_speed: 50
[stepper_z]
step_pin: PB10
dir_pin: !PA4
enable_pin: !PC4
rotation_distance: 8
microsteps: 16
position_min: -1
position_max: 400
endstop_pin: probe:z_virtual_endstop # Use Z- as endstop
#homing_speed: 10.0
[extruder]
max_extrude_only_distance: 100.0
step_pin: PC11
dir_pin: !PC10
enable_pin: !PC12
microsteps: 64
nozzle_diameter: 0.400
filament_diameter: 1.750
heater_pin: PA6
sensor_type: EPCOS 100K B57560G104F #Generic 3950
sensor_pin: PC5
min_extrude_temp: 170
min_temp: 0
max_temp: 300
# Calibrate E-Steps https://www.klipper3d.org/Rotation_Distance.html#calibrating-rotation_distance-on-extruders
rotation_distance: 17.75
# Calibrate PID: https://www.klipper3d.org/Config_checks.html#calibrate-pid-settings
# - Example: PID_CALIBRATE HEATER=extruder TARGET=200
control: pid
pid_kp: 30.356
pid_ki: 1.857
pid_kd: 124.081
# Calibrate PA: https://www.klipper3d.org/Pressure_Advance.html
[heater_bed]
heater_pin: PA7
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC0
max_temp: 100
min_temp: 0
# Calibrate PID: https://www.klipper3d.org/Config_checks.html#calibrate-pid-settings
# - Example: PID_CALIBRATE HEATER=heater_bed TARGET=60
control: pid
pid_kp: 64.230
pid_ki: 0.723
pid_kd: 1425.905
[heater_fan hotend_fan]
pin: PB1
heater: extruder
heater_temp: 50.0
[fan]
pin: PB0
[temperature_fan Artillery_MCU]
sensor_type: temperature_mcu
pin: PA5
max_temp: 60.0
target_temp: 40.0
min_temp: 0
shutdown_speed: 0.0
kick_start_time: 0.5
off_below: 0.19
max_speed: 1.0
min_speed: 0.0
control: watermark
[filament_switch_sensor filament_sensor]
pause_on_runout: true
switch_pin: PC1
[probe]
pin: PC14
x_offset:45.2
y_offset:11.6
speed:5
lift_speed:15
z_offset: 2.350
[safe_z_home]
home_xy_position: 110, 145 # X, Y coordinate (e.g. 100, 100) where the Z homing should be
speed: 300.0
z_hop: 10
z_hop_speed: 15.0
[bed_mesh]
speed: 300
horizontal_move_z: 6
mesh_min: 46,15
mesh_max: 300,300
probe_count: 10, 10
fade_start: 1.0
fade_end: 0.0
algorithm: bicubic
[screws_tilt_adjust]
screw1: 120, 153
screw1_name: center reference
screw2: 7, 45
screw2_name: front left
screw3: 210, 45
screw3_name: front right
screw4: 227, 145
screw4_name: right center
screw5: 210, 245
screw5_name: rear right
screw6: 7, 245
screw6_name: rear left
screw7: 7, 145
screw7_name: left center
horizontal_move_z: 8
speed: 300
screw_thread: CW-M4

View File

@@ -98,7 +98,6 @@ max_temp: 100
[output_pin led]
pin: PC14
static_value: 0
# Neopixel LED support
# [neopixel led_neopixel]

View File

@@ -98,6 +98,10 @@ z_offset: 0.0
speed: 2.0
samples: 5
[safe_z_home]
home_xy_position: 117, 117
z_hop: 10
[filament_switch_sensor filament_sensor]
pause_on_runout: true
switch_pin: ^!PA7

View File

@@ -98,6 +98,10 @@ z_offset: 0.0
speed: 2.0
samples: 5
[safe_z_home]
home_xy_position: 117, 117
z_hop: 10
[filament_switch_sensor filament_sensor]
pause_on_runout: true
switch_pin: ^!PA7

View File

@@ -0,0 +1,170 @@
# Creality Ender 5 S1 (HW version: CR4NS200141C13)
#
# printer_size: 220x220x280
# To use this config, during "make menuconfig" select the STM32F401
# with a "64KiB bootloader" and serial (on USART1 PA10/PA9)
# communication.
#
# Flash this firmware by creating a directory named "STM32F4_UPDATE"
# on an SD card, copying the "out/klipper.bin" to it and then turn
# on the printer with the card inserted. The firmware filename must
# end in ".bin" and must not match the last filename that was flashed.
#
# See docs/Config_Reference.md for a description of parameters.
[stepper_x]
step_pin: PC2
dir_pin: !PB9
enable_pin: !PC3
rotation_distance: 40
microsteps: 16
endstop_pin: !PA5
position_endstop: 220
position_max: 222
homing_speed: 80
[stepper_y]
step_pin: PB8
dir_pin: !PB7
enable_pin: !PC3
rotation_distance: 40
microsteps: 16
endstop_pin: !PA6
position_endstop: 220
position_max: 220
homing_speed: 80
[stepper_z]
step_pin: PB6
dir_pin: PB5
enable_pin: !PC3
rotation_distance: 8
microsteps: 16
endstop_pin: probe:z_virtual_endstop
position_max: 280
homing_speed: 20
second_homing_speed: 1
homing_retract_dist: 2.0
[extruder]
step_pin: PB4
dir_pin: PB3
enable_pin: !PC3
rotation_distance: 7.5
microsteps: 16
nozzle_diameter: 0.400
filament_diameter: 1.750
heater_pin: PA1
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC5
control: pid # tuned for stock hardware with 210 degree Celsius target
pid_kp: 20.749
pid_ki: 1.064
pid_kd: 101.153
min_temp: 0
max_temp: 305
[heater_bed]
heater_pin: PA7
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC4
control: pid # tuned for stock hardware with 60 degree Celsius target
pid_kp: 66.566
pid_ki: 0.958
pid_kd: 1155.761
min_temp: 0
max_temp: 110
# Part cooling fan
[fan]
pin: PA0
kick_start_time: 0.5
# Hotend fan
# set fan runnig when extruder temperature is over 60
[heater_fan heatbreak_fan]
pin: PC0
heater:extruder
heater_temp: 60
fan_speed: 0.8
[filament_switch_sensor filament_sensor]
pause_on_runout: true
switch_pin: ^!PC15
# Stock CR Touch bed sensor
[bltouch]
sensor_pin: ^PC14
control_pin: PC13
x_offset: -13
y_offset: 27
z_offset: 2.0
speed: 10
stow_on_each_sample: true # Occasional bed crashes when false
samples: 4
sample_retract_dist: 2
samples_result: average
probe_with_touch_mode: true
[bed_mesh]
speed: 150
mesh_min: 3,28 # need to handle head distance with bl_touch
mesh_max: 205,218
mesh_pps: 3
probe_count: 4,4
fade_start: 1
fade_end: 10
fade_target: 0
[mcu]
serial: /dev/serial/by-id/usb-1a86_USB_Serial-if00-port0
restart_method: command
[safe_z_home]
home_xy_position: 123,83
speed: 200
z_hop: 10
z_hop_speed: 10
# Many Ender 5 S1 printers appear to suffer from a slight twist
# in the X axis. This twist can be measured, and compensated for
# using the AXIS_TWIST_COMPENSATION_CALIBRATE G-Code command. See
# https://www.klipper3d.org/Axis_Twist_Compensation.html for more
# information. This section provides the setup for this optional
# calibration step.
[axis_twist_compensation]
calibrate_start_x: 3
calibrate_end_x: 207
calibrate_y: 110
# Probe locations for assisted bed screw adjustment.
[screws_tilt_adjust]
screw1: 38,6
screw1_name: Front Left Screw
screw2: 215,6
screw2_name: Front Right Screw
screw3: 215,175
screw3_name: Rear Right Screw
screw4: 38,175
screw4_name: Rear Left Screw
horizontal_move_z: 5
speed: 100
screw_thread: CW-M4
[bed_screws]
screw1: 25,25
screw1_name: Front Left Screw
screw2: 195,25
screw2_name: Front Right Screw
screw3: 195,195
screw3_name: Rear Right Screw
screw4: 25,195
screw4_name: Rear Left Screw
[printer]
kinematics: cartesian
max_velocity: 300
max_accel: 5000
max_z_velocity: 5
max_z_accel: 100
square_corner_velocity: 5.0

View File

@@ -71,25 +71,21 @@ pid_Kp: 39
pid_Ki: 2
pid_Kd: 210
[extruder1]
[extruder_stepper e1]
extruder:
step_pin: PA0
dir_pin: !PB6
enable_pin: !PA1
microsteps: 16
rotation_distance: 32
nozzle_diameter: 0.4
filament_diameter: 1.75
shared_heater: extruder
[extruder2]
[extruder_stepper e2]
extruder:
step_pin: PB2
dir_pin: !PB11
enable_pin: !PC4
microsteps: 16
rotation_distance: 32
nozzle_diameter: 0.4
filament_diameter: 1.75
shared_heater: extruder
[heater_bed]
heater_pin: PB1

View File

@@ -0,0 +1,256 @@
# This file contains common pin mappings for the Geeetech GT2560 v4.0 and v4.1b
# boards. These boards use a firmware compiled for the AVR atmega2560.
# For default Geeetech A10/A20 (1 extruder),
# A10M/A20M (mixing 2 in 1 out),
# A10T/A20T (mixing 3 in 1 out) printers
# Installation: https://www.klipper3d.org/Installation.html
# Always read for first start: https://www.klipper3d.org/Config_checks.html
[mcu]
# Might need to be changed: https://www.klipper3d.org/Installation.html
serial: /dev/serial/by-id/usb-1a86_USB_Serial-if00-port0
[printer]
kinematics: cartesian
max_velocity: 200
max_accel: 1500
max_z_velocity: 20
max_z_accel: 500
# # uncomment for BLTouch/3DTouch
# [bltouch]
# sensor_pin: PC7 # there is an external pull up so no need in ^
# control_pin: PB5
# speed: 3.0
# samples: 2
# x_offset: -42.0
# y_offset: -1.0
# z_offset: 1.0 # during calibration this line is commented out and new record added at the end of file
[safe_z_home]
home_xy_position: 100, 100 # Change coordinates to the center of your print bed
speed: 50
z_hop: 10 # Move up 10mm
z_hop_speed: 5
[stepper_x]
enable_pin: !PC2
dir_pin: !PG2
step_pin: PC0
microsteps: 16
rotation_distance: 40
endstop_pin: !PA2 # there are external pull ups
position_endstop: 0
position_max: 220 # for A10/M/T / change to 250 for A20/M/T
homing_speed: 40
[stepper_y]
enable_pin: !PA7
dir_pin: !PC4
step_pin: PC6
microsteps: 16
rotation_distance: 40
endstop_pin: !PA6 # there are external pull ups
position_endstop: 0
position_max: 220 # for A10/M/T / change to 250 for A20/M/T
homing_speed: 40
[stepper_z]
enable_pin: !PA5
dir_pin: PA1
step_pin: PA3
microsteps: 16
rotation_distance: 8
#endstop_pin: probe:z_virtual_endstop # uncomment for BLTouch/3DTouch
endstop_pin: !PC7 # comment for BLTouch/3DTouch
position_endstop: 0 # comment for BLTouch/3DTouch
position_max: 230 # for A10/M/T / change to 250 for A20/M/T
position_min: -5
homing_speed: 20
[extruder]
enable_pin: !PB6
dir_pin: PL5
step_pin: PL3
microsteps: 16
rotation_distance: 8 # Needs to be optimized: https://www.klipper3d.org/Rotation_Distance.html#calibrating-rotation_distance-on-extruders
nozzle_diameter: 0.4
filament_diameter: 1.750
heater_pin: PB4
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PK3
min_temp: 0
max_temp: 250
max_extrude_only_distance: 200.0
# Parameters for stock hotend on A10M
# Please recalibrate according to https://www.klipper3d.org/Config_checks.html#calibrate-pid-settings
control: pid
pid_kp: 54.722
pid_ki: 4.800
pid_kd: 155.958
[extruder_stepper extruder_1]
extruder:
enable_pin: !PL1
dir_pin: PL2
step_pin: PL0
microsteps: 16
rotation_distance: 8 # Needs to be optimized: https://www.klipper3d.org/Rotation_Distance.html#calibrating-rotation_distance-on-extruders
[extruder_stepper extruder_2]
extruder:
enable_pin: !PG0
dir_pin: PL4
step_pin: PL6
microsteps: 16
rotation_distance: 8 # Needs to be optimized: https://www.klipper3d.org/Rotation_Distance.html#calibrating-rotation_distance-on-extruders
[heater_bed]
heater_pin: PG5
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PK2
min_temp: 0
max_temp: 120
# Parameters for `SuperPlate` on A10M
# Please recalibrate according to https://www.klipper3d.org/Config_checks.html#calibrate-pid-settings
control: pid
pid_kp: 70.936
pid_ki: 1.785
pid_kd: 704.924
[fan]
pin: PH6
cycle_time: 0.150
kick_start_time: 0.300
# # for GT2560V4.0 with 20pin flat cable toward the display
# [display]
# lcd_type: hd44780
# hd44780_protocol_init: True
# rs_pin: PD1
# e_pin: PH0
# d4_pin: PH1
# d5_pin: PD0
# d6_pin: PE3
# d7_pin: PC1
# encoder_pins: ^PG1, ^PL7
# click_pin: ^!PD2
# for GT2560V4.1B with 12pin flat cable toward the display YHCB2004-06 ver3.0
# the aip31068_spi driver was added to Klipper on 2024-12-02, commit aecb29d2
[display]
lcd_type: aip31068_spi
latch_pin: PE3
spi_software_sclk_pin: PD0
spi_software_mosi_pin: PC1
spi_software_miso_pin: PH7 # any unused pin
encoder_pins: ^PH0, ^PH1
click_pin: ^!PD2
[filament_switch_sensor sensor_e0]
switch_pin: !PK4
[filament_switch_sensor sensor_e1]
switch_pin: !PK5
[filament_switch_sensor sensor_e2]
# switch_pin: !PE2 # for GT2560V4.0
switch_pin: !PF0 # for GT2560V4.1B
# to enable M118 echo command
[respond]
# Specific macros for mixing colors.
# Add in slicer new filament color and in filament start G-Code add desired mixing factor:
# M163 S0 P50 ; set extruder 0 to 50%
# M163 S1 P40 ; set extruder 1 to 40%
# M163 S2 P10 ; set extruder 2 to 10%
# M164 ; commit the mix factors
[gcode_macro M163]
description: M163 [P<factor>] [S<index>] Set a single mix factor (in proportion to the sum total of all mix factors). The mix must be committed to a virtual tool by M164 before it takes effect.
gcode:
{% if 'P' in params %}
{% set s = params.S|default(0)| int %}
{% if s == 0 %}
SET_GCODE_VARIABLE MACRO=M164 VARIABLE=e0_parts VALUE={params.P|default(0)|float}
M118 Set Mixing factor for extruder 0 to {params.P|default(0)|float}
{% elif s == 1 %}
SET_GCODE_VARIABLE MACRO=M164 VARIABLE=e1_parts VALUE={params.P|default(0)|float}
M118 Set Mixing factor for extruder 1 to {params.P|default(0)|float}
{% elif s == 2 %}
SET_GCODE_VARIABLE MACRO=M164 VARIABLE=e2_parts VALUE={params.P|default(0)|float}
M118 Set Mixing factor for extruder 2 to {params.P|default(0)|float}
{% endif %}
{% else %}
M118 No Mixing factor set, missing value for P
{% endif %}
M118 {e0_parts} {e1_parts} {e2_parts}
[gcode_macro M164]
description: Applies the set mixing factors to the extruders
# default values:
variable_e0_parts : 100
variable_e1_parts : 0
variable_e2_parts : 0
gcode:
# normalize the parts to sum of 1
{% set e0 = e0_parts / (e0_parts + e1_parts + e2_parts) | float %}
{% set e1 = e1_parts / (e0_parts + e1_parts + e2_parts) | float %}
{% set e2 = e2_parts / (e0_parts + e1_parts + e2_parts) | float %}
M118 scaled rot-dist_e0 { printer.configfile.settings.extruder.rotation_distance / (e0 + 0.000001) | float }
M118 scaled rot-dist_e1 { printer.configfile.settings['extruder_stepper extruder_1'].rotation_distance / (e1 + 0.000001) | float }
M118 scaled rot-dist_e2 { printer.configfile.settings['extruder_stepper extruder_2'].rotation_distance / (e2 + 0.000001) |float }
# activate stepper percentages
SYNC_EXTRUDER_MOTION EXTRUDER=extruder MOTION_QUEUE=extruder
SYNC_EXTRUDER_MOTION EXTRUDER=extruder_1 MOTION_QUEUE=extruder
SYNC_EXTRUDER_MOTION EXTRUDER=extruder_2 MOTION_QUEUE=extruder
SET_EXTRUDER_ROTATION_DISTANCE EXTRUDER=extruder DISTANCE={ printer.configfile.settings.extruder.rotation_distance / (e0+0.000001)|float }
SET_EXTRUDER_ROTATION_DISTANCE EXTRUDER=extruder_1 DISTANCE={ printer.configfile.settings['extruder_stepper extruder_1'].rotation_distance / (e1+0.000001)|float }
SET_EXTRUDER_ROTATION_DISTANCE EXTRUDER=extruder_2 DISTANCE={ printer.configfile.settings['extruder_stepper extruder_2'].rotation_distance / (e2+0.000001)|float }
M118 Mixing factors {e0} {e1} {e2} are activated
# In PrusaSlicer:
# - you can add as many extruders as mixing ratios you want
# - in Printer Settings -> Custom G-code -> Tool change G-code add:
# TOOL_CHANGE EXTRUDER={next_extruder}
# - in this config file add:
# [gcode_macro TOOL_CHANGE]
# description: Tool change macro with mix ratio setup for 11 extruders
# variable_extruder: 0
# gcode:
# {% set extruder = params.EXTRUDER|default(0)| int %}
# {% if extruder == 0 %}
# M163 S0 P100
# M163 S1 P0
# M163 S2 P0
# M164
# M118 Switching to Extruder 0
# {% elif extruder == 1 %}
# M163 S0 P90
# M163 S1 P10
# M163 S2P0
# M164
# M118 Switching to Extruder 1
# {% elif extruder == 2 %}
# # and so on ...
# {% else %}
# M118 Unknown extruder number: {extruder}
# {% endif %}
# In OrcaSlicer:
# you can add as many filaments as mixing ratios you want
# in Material settings -> Advanced -> Filament start G-code add desired mixing ratio:
# ; filament start gcode
# M163 S0 P100 ; set extruder 0
# M163 S1 P0 ; set extruder 1
# M163 S2 P0 ; set extruder 2
# M164 ; commit the mix factors
# For gradient over Z axis:
# In `Printer -> Custom G-code -> After layer change G-code` add:
# M163 S0 P{ layer_num * 100 / total_layer_count } ; Gradient 0-100
# M163 S1 P{(total_layer_count-layer_num) * 100 / total_layer_count} ; Gradient 100-0
# M164 ; commit the mix factors

View File

@@ -125,7 +125,7 @@ pwm: True
scale: 2.0
cycle_time: .000030
hardware_pwm: True
static_value: 1.300
value: 1.300
[output_pin stepper_z_current]
pin: PL4
@@ -133,7 +133,7 @@ pwm: True
scale: 2.0
cycle_time: .000030
hardware_pwm: True
static_value: 1.630
value: 1.630
[output_pin stepper_e_current]
pin: PL5
@@ -141,7 +141,7 @@ pwm: True
scale: 2.0
cycle_time: .000030
hardware_pwm: True
static_value: 1.250
value: 1.250
[static_digital_output stepper_config]
# Microstepping pins

View File

@@ -199,7 +199,6 @@ algorithm: bicubic
bicubic_tension: 0.15
fade_start: 0.5
fade_end: 2.5
relative_reference_index: 60
[bed_screws]
screw1: 0,0

View File

@@ -19,7 +19,7 @@ restart_method: command
kinematics: cartesian
max_velocity: 300
max_accel: 1000
max_accel_to_decel: 1000
minimum_cruise_ratio: 0.0
max_z_velocity: 5
max_z_accel: 100

View File

@@ -28,7 +28,7 @@ microsteps: 16
rotation_distance: 40
endstop_pin: tmc2209_stepper_x:virtual_endstop
position_endstop: 0
position_max: 225
position_max: 220
homing_speed: 40
homing_retract_dist: 0
@@ -50,7 +50,7 @@ microsteps: 16
rotation_distance: 40
endstop_pin: tmc2209_stepper_y:virtual_endstop
position_endstop: 0
position_max: 225
position_max: 220
homing_speed: 40
homing_retract_dist: 0
@@ -72,7 +72,7 @@ microsteps: 16
rotation_distance: 4
endstop_pin: probe:z_virtual_endstop
position_min: -4
position_max: 261
position_max: 250
homing_speed: 4
[tmc2209 stepper_z]
@@ -127,7 +127,7 @@ pin: PA0
[probe]
pin: PB1
x_offset: 28
x_offset: 27
y_offset: -20
z_offset: 0
samples: 2

View File

@@ -0,0 +1,147 @@
# This file contains pin mappings for the stock Sovol SV06 Plus
# To use this config, during "make menuconfig" select the
# STM32F103 with a "28KiB bootloader" and serial (on USART1 PA10/PA9) communication.
# Also, since it is using the GD32F103, please select Disable SWD at startup
#
# Flash this firmware by copying "out/klipper.bin" to a SD card and
# turning on the printer with the card inserted. The firmware
# filename must end in ".bin" and must not match the last filename
# that was flashed.
#
# Note: The stock LCD display does not currently work with Klipper
#
# See docs/Config_Reference.md for a description of parameters.
[mcu]
serial: /dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0
restart_method: command
[printer]
kinematics: cartesian
max_velocity: 500
max_accel: 2000
max_z_velocity: 10
max_z_accel: 100
[stepper_x]
step_pin: PC2
dir_pin: !PB9
enable_pin: !PC3
microsteps: 16
rotation_distance: 40
endstop_pin: tmc2209_stepper_x:virtual_endstop
position_endstop: 0
position_max: 305
homing_speed: 40
homing_retract_dist: 0
[tmc2209 stepper_x]
uart_pin: PC1
run_current: 0.860
sense_resistor: 0.150
uart_address: 3
driver_SGTHRS: 86
diag_pin: PA5
[stepper_y]
step_pin: PB8
dir_pin: PB7
enable_pin: !PC3
microsteps: 16
rotation_distance: 40
endstop_pin: tmc2209_stepper_y:virtual_endstop
position_endstop: 0
position_max: 305
homing_speed: 40
homing_retract_dist: 0
[tmc2209 stepper_y]
uart_pin: PC0
run_current: 0.900
sense_resistor: 0.150
uart_address: 3
driver_SGTHRS: 110
diag_pin: PA6
[stepper_z]
step_pin: PB6
dir_pin: !PB5
enable_pin: !PC3
microsteps: 16
rotation_distance: 4
endstop_pin: probe:z_virtual_endstop
position_min: -4
position_max: 350
homing_speed: 4
[tmc2209 stepper_z]
uart_pin: PA15
run_current: 1.000
interpolate: False
sense_resistor: 0.150
uart_address: 3
diag_pin: PA7
[extruder]
max_extrude_only_distance: 100.0
step_pin: PB4
dir_pin: !PB3
enable_pin: !PC3
microsteps: 16
rotation_distance: 4.56
nozzle_diameter: 0.400
filament_diameter: 1.750
heater_pin: PA1
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC5
control: pid
pid_kd: 41.96
pid_kp: 15.66
pid_ki: 1.49
min_temp: 0
max_temp: 300
[tmc2209 extruder]
uart_pin: PC14
run_current: 0.550
stealthchop_threshold: 0
interpolate: False
sense_resistor: 0.150
uart_address: 3
[heater_bed]
heater_pin: PA2
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC4
control: pid
pid_kp: 186.38
pid_ki: 36.12
pid_kd: 637.30
min_temp: 0
max_temp: 130
[fan]
pin: PA0
[probe]
pin: PB1
x_offset: 28
y_offset: -20
z_offset: 0
[safe_z_home]
home_xy_position: 123,170
z_hop: 10
z_hop_speed: 5
[bed_mesh]
speed: 120
mesh_min: 28, 20
mesh_max: 270, 270
probe_count: 5
algorithm: bicubic
fade_end: 10
fade_target: 0
[filament_switch_sensor filament_runout_sensor]
switch_pin: PA4
pause_on_runout: True

View File

@@ -0,0 +1,138 @@
# Klipper configuration for the TronXY Crux1 printer
# CXY-V10.1-220921 mainboard, GD32F4XX or STM32F446 MCU
#
# =======================
# BUILD AND FLASH OPTIONS
# =======================
#
# MCU-architecture: STMicroelectronics
# Processor model: STM32F446
# Bootloader offset: 64KiB
# Comms interface: Serial on USART1 PA10/PA9
#
# Build the firmware with these options
# Rename the resulting klipper.bin into fmw_tronxy.bin
# Put the file into a directory called "update" on a FAT32 formatted SD card.
# Turn off the printer, plug in the SD card and turn the printer back on
# Flashing will start automatically and progress will be indicated on the LCD
# Once the flashing is completed the display will get stuck on the white Tronxy logo bootscreen
# The LCD display will NOT work anymore after flashing Klipper onto this printer
[mcu]
serial: /dev/serial/by-id/usb-1a86_USB_Serial-if00-port0
restart_method: command
[printer]
kinematics: cartesian
max_velocity: 250
max_accel: 1500
square_corner_velocity: 5
max_z_velocity: 15
max_z_accel: 100
[controller_fan drivers_fan]
pin: PD7
[pwm_cycle_time BEEPER_pin]
pin: PA8
value: 0
shutdown_value: 0
cycle_time: 0.001
[safe_z_home]
home_xy_position: 0, 0
speed: 100
z_hop: 10
z_hop_speed: 5
[stepper_x]
step_pin: PE5
dir_pin: PF1
enable_pin: !PF0
microsteps: 16
rotation_distance: 20
endstop_pin: ^!PC15
position_endstop: -1
position_min: -1
position_max: 180
homing_speed: 100
homing_retract_dist: 10
second_homing_speed: 25
[stepper_y]
step_pin: PF9
dir_pin: !PF3
enable_pin: !PF5
microsteps: 16
rotation_distance: 20
endstop_pin: ^!PC14
position_endstop: -3
position_min: -3
position_max: 180
homing_retract_dist: 10
homing_speed: 100
second_homing_speed: 25
[stepper_z]
step_pin: PA6
dir_pin: !PF15
enable_pin: !PA5
microsteps: 16
rotation_distance: 4
endstop_pin: ^!PC13
position_endstop: 0
position_max: 180
position_min: 0
[extruder]
step_pin: PB1
dir_pin: PF13
enable_pin: !PF14
microsteps: 16
rotation_distance: 16.75
nozzle_diameter: 0.400
filament_diameter: 1.750
heater_pin: PG7
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC3
control: pid
pid_kp: 22.2
pid_ki: 1.08
pid_kd: 114.00
min_temp: 0
max_temp: 250
min_extrude_temp: 170
max_extrude_only_distance: 450
[heater_fan hotend_fan]
heater: extruder
heater_temp: 50.0
pin: PG9
[fan]
pin: PG0
[filament_switch_sensor filament_sensor]
pause_on_runout: True
switch_pin: ^!PE6
[heater_bed]
heater_pin: PE2
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC2
min_temp: 0
max_temp: 130
control: pid
pid_kp: 10.00
pid_ki: 0.023
pid_kd: 305.4
[bed_screws]
screw1: 17.5, 11
screw1_name: front_left
screw2: 162.5, 11
screw2_name: front_right
screw3: 162.5, 162.5
screw3_name: back_right
screw4: 17.5, 162.5
screw4_name: back_left

View File

@@ -86,7 +86,7 @@ pwm: True
scale: 2.782
cycle_time: .000030
hardware_pwm: True
static_value: 1.2
value: 1.2
[output_pin stepper_z_current]
pin: PL4
@@ -94,7 +94,7 @@ pwm: True
scale: 2.782
cycle_time: .000030
hardware_pwm: True
static_value: 1.2
value: 1.2
[output_pin stepper_e_current]
pin: PL3
@@ -102,7 +102,7 @@ pwm: True
scale: 2.782
cycle_time: .000030
hardware_pwm: True
static_value: 1.0
value: 1.0
[display]
lcd_type: ssd1306

View File

@@ -77,5 +77,14 @@ heater_temp: 50.0
pin: toolboard:PA9
z_offset: 20
[samd_sercom sercom_i2c]
sercom: sercom1
tx_pin: toolboard:PA16
clk_pin: toolboard:PA17
[lis3dh]
i2c_mcu: toolboard
i2c_bus: sercom1
[mcu toolboard]
canbus_uuid: 4b194673554e

View File

@@ -61,12 +61,10 @@ gcode:
# P is the tone duration, S the tone frequency.
# The frequency won't be pitch perfect.
[output_pin BEEPER_pin]
[pwm_cycle_time BEEPER_pin]
pin: ar37
# Beeper pin. This parameter must be provided.
# ar37 is the default RAMPS/MKS pin.
pwm: True
# A piezo beeper needs a PWM signal, a DC buzzer doesn't.
value: 0
# Silent at power on, set to 1 if active low.
shutdown_value: 0

View File

@@ -2,9 +2,8 @@
# such as a laser or spindle.
# See docs/Using_PWM_Tools.md for a more detailed description.
[output_pin TOOL]
[pwm_tool TOOL]
pin: !ar9 # use your fan's pin number
pwm: True
hardware_pwm: True
cycle_time: 0.001
shutdown_value: 0
@@ -36,9 +35,9 @@ gcode:
[menu __main __control __toolonoff]
type: input
enable: {'output_pin TOOL' in printer}
enable: {'pwm_tool TOOL' in printer}
name: Fan: {'ON ' if menu.input else 'OFF'}
input: {printer['output_pin TOOL'].value}
input: {printer['pwm_tool TOOL'].value}
input_min: 0
input_max: 1
input_step: 1
@@ -47,9 +46,9 @@ gcode:
[menu __main __control __toolspeed]
type: input
enable: {'output_pin TOOL' in printer}
enable: {'pwm_tool TOOL' in printer}
name: Tool speed: {'%3d' % (menu.input*100)}%
input: {printer['output_pin TOOL'].value}
input: {printer['pwm_tool TOOL'].value}
input_min: 0
input_max: 1
input_step: 0.01

View File

@@ -364,6 +364,38 @@ and might later produce asynchronous messages such as:
The "header" field in the initial query response is used to describe
the fields found in later "data" responses.
### hx71x/dump_hx71x
This endpoint is used to subscribe to raw HX711 and HX717 ADC data.
Obtaining these low-level ADC updates may be useful for diagnostic
and debugging purposes. Using this endpoint may increase Klipper's
system load.
A request may look like:
`{"id": 123, "method":"hx71x/dump_hx71x",
"params": {"sensor": "load_cell", "response_template": {}}}`
and might return:
`{"id": 123,"result":{"header":["time","counts","value"]}}`
and might later produce asynchronous messages such as:
`{"params":{"data":[[3292.432935, 562534, 0.067059278],
[3292.4394937, 5625322, 0.670590639]]}}`
### ads1220/dump_ads1220
This endpoint is used to subscribe to raw ADS1220 ADC data.
Obtaining these low-level ADC updates may be useful for diagnostic
and debugging purposes. Using this endpoint may increase Klipper's
system load.
A request may look like:
`{"id": 123, "method":"ads1220/dump_ads1220",
"params": {"sensor": "load_cell", "response_template": {}}}`
and might return:
`{"id": 123,"result":{"header":["time","counts","value"]}}`
and might later produce asynchronous messages such as:
`{"params":{"data":[[3292.432935, 562534, 0.067059278],
[3292.4394937, 5625322, 0.670590639]]}}`
### pause_resume/cancel
This endpoint is similar to running the "PRINT_CANCEL" G-Code command.
@@ -401,3 +433,130 @@ might return:
As with the "gcode/script" endpoint, this endpoint only completes
after any pending G-Code commands complete.
### bed_mesh/dump_mesh
Dumps the configuration and state for the current mesh and all
saved profiles.
For example:
`{"id": 123, "method": "bed_mesh/dump_mesh"}`
might return:
```
{
"current_mesh": {
"name": "eddy-scan-test",
"probed_matrix": [...],
"mesh_matrix": [...],
"mesh_params": {
"x_count": 9,
"y_count": 9,
"mesh_x_pps": 2,
"mesh_y_pps": 2,
"algo": "bicubic",
"tension": 0.5,
"min_x": 20,
"max_x": 330,
"min_y": 30,
"max_y": 320
}
},
"profiles": {
"default": {
"points": [...],
"mesh_params": {
"min_x": 20,
"max_x": 330,
"min_y": 30,
"max_y": 320,
"x_count": 9,
"y_count": 9,
"mesh_x_pps": 2,
"mesh_y_pps": 2,
"algo": "bicubic",
"tension": 0.5
}
},
"eddy-scan-test": {
"points": [...],
"mesh_params": {
"x_count": 9,
"y_count": 9,
"mesh_x_pps": 2,
"mesh_y_pps": 2,
"algo": "bicubic",
"tension": 0.5,
"min_x": 20,
"max_x": 330,
"min_y": 30,
"max_y": 320
}
},
"eddy-rapid-test": {
"points": [...],
"mesh_params": {
"x_count": 9,
"y_count": 9,
"mesh_x_pps": 2,
"mesh_y_pps": 2,
"algo": "bicubic",
"tension": 0.5,
"min_x": 20,
"max_x": 330,
"min_y": 30,
"max_y": 320
}
}
},
"calibration": {
"points": [...],
"config": {
"x_count": 9,
"y_count": 9,
"mesh_x_pps": 2,
"mesh_y_pps": 2,
"algo": "bicubic",
"tension": 0.5,
"mesh_min": [
20,
30
],
"mesh_max": [
330,
320
],
"origin": null,
"radius": null
},
"probe_path": [...],
"rapid_path": [...]
},
"probe_offsets": [
0,
25,
0.5
],
"axis_minimum": [
0,
0,
-5,
0
],
"axis_maximum": [
351,
358,
330,
0
]
}
```
The `dump_mesh` endpoint takes one optional parameter, `mesh_args`.
This parameter must be an object, where the keys and values are
parameters available to [BED_MESH_CALIBRATE](#bed_mesh_calibrate).
This will update the mesh configuration and probe points using the
supplied parameters prior to returning the result. It is recommended
to omit mesh parameters unless it is desired to visualize the probe points
and/or travel path before performing `BED_MESH_CALIBRATE`.

View File

@@ -24,19 +24,51 @@ try to probe the bed without attaching the probe if you use it.
> **Tip:** Make sure the [probe X and Y offsets](Config_Reference.md#probe) are
> correctly set as they greatly influence calibration.
1. After setting up the [axis_twist_compensation] module,
perform `AXIS_TWIST_COMPENSATION_CALIBRATE`
* The calibration wizard will prompt you to measure the probe Z offset at a few
points along the bed
* The calibration defaults to 3 points but you can use the option
`SAMPLE_COUNT=` to use a different number.
2. [Adjust your Z offset](Probe_Calibrate.md#calibrating-probe-z-offset)
3. Perform automatic/probe-based bed tramming operations, such as
[Screws Tilt Adjust](G-Codes.md#screws_tilt_adjust),
[Z Tilt Adjust](G-Codes.md#z_tilt_adjust) etc
4. Home all axis, then perform a [Bed Mesh](Bed_Mesh.md) if required
5. Perform a test print, followed by any
[fine-tuning](Axis_Twist_Compensation.md#fine-tuning) as desired
### Basic Usage: X-Axis Calibration
1. After setting up the ```[axis_twist_compensation]``` module, run:
```
AXIS_TWIST_COMPENSATION_CALIBRATE
```
This command will calibrate the X-axis by default.
- The calibration wizard will prompt you to measure the probe Z offset at
several points along the bed.
- By default, the calibration uses 3 points, but you can specify a different
number with the option:
``
SAMPLE_COUNT=<value>
``
2. **Adjust Your Z Offset:**
After completing the calibration, be sure to [adjust your Z offset]
(Probe_Calibrate.md#calibrating-probe-z-offset).
3. **Perform Bed Leveling Operations:**
Use probe-based operations as needed, such as:
- [Screws Tilt Adjust](G-Codes.md#screws_tilt_adjust)
- [Z Tilt Adjust](G-Codes.md#z_tilt_adjust)
4. **Finalize the Setup:**
- Home all axes, and perform a [Bed Mesh](Bed_Mesh.md) if necessary.
- Run a test print, followed by any
[fine-tuning](Axis_Twist_Compensation.md#fine-tuning)
if needed.
### For Y-Axis Calibration
The calibration process for the Y-axis is similar to the X-axis. To calibrate
the Y-axis, use:
```
AXIS_TWIST_COMPENSATION_CALIBRATE AXIS=Y
```
This will guide you through the same measuring process as for the X-axis.
### Automatic Calibration for Both Axes
To perform automatic calibration for both the X and Y axes without manual
intervention, use:
```
AXIS_TWIST_COMPENSATION_CALIBRATE AUTO=True
```
In this mode, the calibration process will run for both axes automatically.
> **Tip:** Bed temperature and nozzle temperature and size do not seem to have
> an influence to the calibration process.

View File

@@ -6,23 +6,64 @@ PRU.
## Building an OS image
Start by installing the
[Debian 9.9 2019-08-03 4GB SD IoT](https://beagleboard.org/latest-images)
[Debian 11.7 2023-09-02 4GB microSD IoT](https://beagleboard.org/latest-images)
image. One may run the image from either a micro-SD card or from
builtin eMMC. If using the eMMC, install it to eMMC now by following
the instructions from the above link.
Then ssh into the Beaglebone machine (`ssh debian@beaglebone` --
password is `temppwd`) and install Klipper by running the following
password is `temppwd`).
Before start installing Klipper you need to free-up additional space.
there are 3 options to do that:
1. remove some BeagleBone "Demo" resources
2. if you did boot from SD-Card, and it's bigger than 4Gb - you can expand
current filesystem to take whole card space
3. do option #1 and #2 together.
To remove some BeagleBone "Demo" resources execute these commands
```
sudo apt remove bb-node-red-installer
sudo apt remove bb-code-server
```
To expand filesystem to full size of your SD-Card execute this command, reboot is not required.
```
sudo growpart /dev/mmcblk0 1
sudo resize2fs /dev/mmcblk0p1
```
Install Klipper by running the following
commands:
```
git clone https://github.com/Klipper3d/klipper
git clone https://github.com/Klipper3d/klipper.git
./klipper/scripts/install-beaglebone.sh
```
## Install Octoprint
After installing Klipper you need to decide what kind of deployment do you need,
but take a note that BeagleBone is 3.3v based hardware and in most cases you can't
directly connect pins to 5v or 12v based hardware without conversion boards.
One may then install Octoprint:
As Klipper have multimodule architecture on BeagleBone you can achieve many different use cases,
but general ones are following:
Use case 1: Use BeagleBone only as a host system to run Klipper and additional software
like OctoPrint/Fluidd + Moonraker/... and this configuration will be driving
external micro-controllers via serial/usb/canbus connections.
Use case 2: Use BeagleBone with extension board (cape) like CRAMPS board.
in this configuration BeagleBone will host Klipper + additional software, and
it will drive extension board with BeagleBone PRU cores (2 additional cores 200Mh, 32Bit).
Use case 3: It's same as "Use case 1" but additionally you want to drive
BeagleBone GPIOs with high speed by utilizing PRU cores to offload main CPU.
## Installing Octoprint
One may then install Octoprint or fully skip this section if desired other software:
```
git clone https://github.com/foosel/OctoPrint.git
cd OctoPrint/
@@ -51,25 +92,89 @@ Then start the Octoprint service:
```
sudo systemctl start octoprint
```
Make sure the OctoPrint web server is accessible - it should be at:
Wait 1-2 minutes and make sure the OctoPrint web server is accessible - it should be at:
[http://beaglebone:5000/](http://beaglebone:5000/)
## Building the micro-controller code
To compile the Klipper micro-controller code, start by configuring it
for the "Beaglebone PRU":
## Building the BeagleBone PRU micro-controller code (PRU firmware)
This section is required for "Use case 2" and "Use case 3" mentioned above,
you should skip it for "Use case 1".
Check that required devices are present
```
sudo beagle-version
```
You should check that output contains successful "remoteproc" drivers loading and presence of PRU cores,
in Kernel 5.10 they should be "remoteproc1" and "remoteproc2" (4a334000.pru, 4a338000.pru)
Also check that many GPIOs are loaded they will look like "Allocated GPIO id=0 name='P8_03'"
Usually everything is fine and no hardware configuration is required.
If something is missing - try to play with "uboot overlays" options or with cape-overlays
Just for reference some output of working BeagleBone Black configuration with CRAMPS board:
```
model:[TI_AM335x_BeagleBone_Black]
UBOOT: Booted Device-Tree:[am335x-boneblack-uboot-univ.dts]
UBOOT: Loaded Overlay:[BB-ADC-00A0.bb.org-overlays]
UBOOT: Loaded Overlay:[BB-BONE-eMMC1-01-00A0.bb.org-overlays]
kernel:[5.10.168-ti-r71]
/boot/uEnv.txt Settings:
uboot_overlay_options:[enable_uboot_overlays=1]
uboot_overlay_options:[disable_uboot_overlay_video=0]
uboot_overlay_options:[disable_uboot_overlay_audio=1]
uboot_overlay_options:[disable_uboot_overlay_wireless=1]
uboot_overlay_options:[enable_uboot_cape_universal=1]
pkg:[bb-cape-overlays]:[4.14.20210821.0-0~bullseye+20210821]
pkg:[bb-customizations]:[1.20230720.1-0~bullseye+20230720]
pkg:[bb-usb-gadgets]:[1.20230414.0-0~bullseye+20230414]
pkg:[bb-wl18xx-firmware]:[1.20230414.0-0~bullseye+20230414]
.............
.............
```
To compile the Klipper micro-controller code, start by configuring it for the "Beaglebone PRU",
for "BeagleBone Black" additionally disable options "Support GPIO Bit-banging devices" and disable "Support LCD devices"
inside the "Optional features" because they will not fit in 8Kb PRU firmware memory,
then exit and save config:
```
cd ~/klipper/
make menuconfig
```
To build and install the new micro-controller code, run:
To build and install the new PRU micro-controller code, run:
```
sudo service klipper stop
make flash
sudo service klipper start
```
After previous commands was executed your PRU firmware should be ready and started
to check if everything was fine you can execute following command
```
dmesg
```
and compare last messages with sample one which indicate that everything started properly:
```
[ 71.105499] remoteproc remoteproc1: 4a334000.pru is available
[ 71.157155] remoteproc remoteproc2: 4a338000.pru is available
[ 73.256287] remoteproc remoteproc1: powering up 4a334000.pru
[ 73.279246] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 97112
[ 73.285807] remoteproc1#vdev0buffer: registered virtio0 (type 7)
[ 73.285836] remoteproc remoteproc1: remote processor 4a334000.pru is now up
[ 73.286322] remoteproc remoteproc2: powering up 4a338000.pru
[ 73.313717] remoteproc remoteproc2: Booting fw image am335x-pru1-fw, size 188560
[ 73.313753] remoteproc remoteproc2: header-less resource table
[ 73.329964] remoteproc remoteproc2: header-less resource table
[ 73.348321] remoteproc remoteproc2: remote processor 4a338000.pru is now up
[ 73.443355] virtio_rpmsg_bus virtio0: creating channel rpmsg-pru addr 0x1e
[ 73.443727] virtio_rpmsg_bus virtio0: msg received with no recipient
[ 73.444352] virtio_rpmsg_bus virtio0: rpmsg host is online
[ 73.540993] rpmsg_pru virtio0.rpmsg-pru.-1.30: new rpmsg_pru device: /dev/rpmsg_pru30
```
take a note about "/dev/rpmsg_pru30" - it's your future serial device for main mcu configuration
this device is required to be present, if it's absent - your PRU cores did not start properly.
## Building and installing Linux host micro-controller code
This section is required for "Use case 2" and optional for "Use case 3" mentioned above
It is also necessary to compile and install the micro-controller code
for a Linux host process. Configure it a second time for a "Linux process":
@@ -83,12 +188,24 @@ sudo service klipper stop
make flash
sudo service klipper start
```
take a note about "/tmp/klipper_host_mcu" - it will be your future serial device for "mcu host"
if that file don't exist - refer to "scripts/klipper-mcu.service" file, it was installed by
previous commands, and it's responsible for it.
Take a note for "Use case 2" about following: when you will define printer configuration you should always
use temperature sensors from "mcu host" because ADCs not present in default "mcu" (PRU cores).
Sample configuration of "sensor_pin" for extruder and heated bed are available in "generic-cramps.cfg"
You can use any other GPIO directly from "mcu host" by referencing them this way "host:gpiochip1/gpio17"
but that should be avoided because it will be creating additional load on main CPU and most probably
you can't use them for stepper control.
## Remaining configuration
Complete the installation by configuring Klipper and Octoprint
Complete the installation by configuring Klipper
following the instructions in
the main [Installation](Installation.md#configuring-klipper) document.
the main [Installation](Installation.md#configuring-octoprint-to-use-klipper) document.
## Printing on the Beaglebone
@@ -97,4 +214,111 @@ OctoPrint well. Print stalls have been known to occur on complex
prints (the printer may move faster than OctoPrint can send movement
commands). If this occurs, consider using the "virtual_sdcard" feature
(see [Config Reference](Config_Reference.md#virtual_sdcard) for
details) to print directly from Klipper.
details) to print directly from Klipper
and disable any DEBUG or VERBOSE logging options if you did enable them.
## AVR micro-controller code build
This environment have everything to build necessary micro-controller code except AVR,
AVR packages was removed because of conflict with PRU packages.
if you still want to build AVR micro-controller code in this environment you need to remove
PRU packages and install AVR packages by executing following commands
```
sudo apt-get remove gcc-pru
sudo apt-get install avrdude gcc-avr binutils-avr avr-libc
```
if you need to restore PRU packages - then remove ARV packages before that
```
sudo apt-get remove avrdude gcc-avr binutils-avr avr-libc
sudo apt-get install gcc-pru
```
## Hardware Pin designation
BeagleBone is very flexible in terms of pin designation, same pin can be configured for different function
but always single function for single pin, same function can be present on different pins.
So you can't have multiple functions on single pin or have same function on multiple pins.
Example:
P9_20 - i2c2_sda/can0_tx/spi1_cs0/gpio0_12/uart1_ctsn
P9_19 - i2c2_scl/can0_rx/spi1_cs1/gpio0_13/uart1_rtsn
P9_24 - i2c1_scl/can1_rx/gpio0_15/uart1_tx
P9_26 - i2c1_sda/can1_tx/gpio0_14/uart1_rx
Pin designation is defined by using special "overlays" which will be loaded during linux boot
they are configured by editing file /boot/uEnv.txt with elevated permissions
```
sudo editor /boot/uEnv.txt
```
and defining which functionality to load, for example to enable CAN1 you need to define overlay for it
```
uboot_overlay_addr4=/lib/firmware/BB-CAN1-00A0.dtbo
```
This overlay BB-CAN1-00A0.dtbo will reconfigure all required pins for CAN1 and create CAN device in Linux.
Any change in overlays will require system reboot to be applied.
If you need to understand which pins are involved in some overlay - you can analyze source files in
this location: /opt/sources/bb.org-overlays/src/arm/
or search info in BeagleBone forums.
## Enabling hardware SPI
BeagleBone usually have multiple hardware SPI buses, for example BeagleBone Black can have 2 of them,
they can work up to 48Mhz, but usually they are limited to 16Mhz by Kernel Device-tree.
By default, in BeagleBone Black some of SPI1 pins are configured for HDMI-Audio output,
to fully enable 4-wire SPI1 you need to disable HDMI Audio and enable SPI1
To do that edit file /boot/uEnv.txt with elevated permissions
```
sudo editor /boot/uEnv.txt
```
uncomment variable
```
disable_uboot_overlay_audio=1
```
next uncomment variable and define it this way
```
uboot_overlay_addr4=/lib/firmware/BB-SPIDEV1-00A0.dtbo
```
Save changes in /boot/uEnv.txt and reboot the board.
Now you have SPI1 Enabled, to verify its presence execute command
```
ls /dev/spidev1.*
```
Take a note that BeagleBone usually is 3.3v based hardware and to use 5V SPI devices
you need to add Level-Shifting chip, for example SN74CBTD3861, SN74LVC1G34 or similar.
If you are using CRAMPS board - it already contains Level-Shifting chip and SPI1 pins
will become available on P503 port, and they can accept 5v hardware,
check CRAMPS board Schematics for pin references.
## Enabling hardware I2C
BeagleBone usually have multiple hardware I2C buses, for example BeagleBone Black can have 3 of them,
they support speed up-to 400Kbit Fast mode.
By default, in BeagleBone Black there are two of them (i2c-1 and i2c-2) usually both are already configured and
present on P9, third ic2-0 usually reserved for internal use.
If you are using CRAMPS board then i2c-2 is present on P303 port with 3.3v level,
If you want to obtain I2c-1 in CRAMPS board - you can get them on Extruder1.Step, Extruder1.Dir pins,
they also are 3.3v based, check CRAMPS board Schematics for pin references.
Related overlays, for [Hardware Pin designation](#hardware-pin-designation):
I2C1(100Kbit): BB-I2C1-00A0.dtbo
I2C1(400Kbit): BB-I2C1-FAST-00A0.dtbo
I2C2(100Kbit): BB-I2C2-00A0.dtbo
I2C2(400Kbit): BB-I2C2-FAST-00A0.dtbo
## Enabling hardware UART(Serial)/CAN
BeagleBone have up to 6 hardware UART(Serial) buses (up to 3Mbit)
and up to 2 hardware CAN(1Mbit) buses.
UART1(RX,TX) and CAN1(TX,RX) and I2C2(SDA,SCL) are using same pins - so you need to chose what to use
UART1(CTSN,RTSN) and CAN0(TX,RX) and I2C1(SDA,SCL) are using same pins - so you need to chose what to use
All UART/CAN related pins are 3.3v based, so you will need to use Transceiver chips/boards like SN74LVC2G241DCUR (for UART),
SN65HVD230 (for CAN), TTL-RS485 (for RS-485) or something similar which can convert 3.3v signals to appropriate levels.
Related overlays, for [Hardware Pin designation](#hardware-pin-designation)
CAN0: BB-CAN0-00A0.dtbo
CAN1: BB-CAN1-00A0.dtbo
UART0: - used for Console
UART1(RX,TX): BB-UART1-00A0.dtbo
UART1(RTS,CTS): BB-UART1-RTSCTS-00A0.dtbo
UART2(RX,TX): BB-UART2-00A0.dtbo
UART3(RX,TX): BB-UART3-00A0.dtbo
UART4(RS-485): BB-UART4-RS485-00A0.dtbo
UART5(RX,TX): BB-UART5-00A0.dtbo

View File

@@ -44,10 +44,9 @@ probe_count: 5, 3
- `mesh_max: 240, 198`\
_Required_\
The probed coordinate farthest farthest from the origin. This is not
necessarily the last point probed, as the probing process occurs in a
zig-zag fashion. As with `mesh_min`, this coordinate is relative to
the probe's location.
The probed coordinate farthest from the origin. This is not necessarily
the last point probed, as the probing process occurs in a zig-zag fashion.
As with `mesh_min`, this coordinate is relative to the probe's location.
- `probe_count: 5, 3`\
_Default Value: 3, 3_\
@@ -142,7 +141,7 @@ bicubic_tension: 0.2
integer pair, and also may be specified a single integer that is applied
to both axes. In this example there are 4 segments along the X axis
and 2 segments along the Y axis. This evaluates to 8 interpolated
points along X, 6 interpolated points along Y, which results in a 13x8
points along X, 6 interpolated points along Y, which results in a 13x9
mesh. Note that if mesh_pps is set to 0 then mesh interpolation is
disabled and the probed matrix will be sampled directly.
@@ -270,7 +269,7 @@ printers use an endstop for homing the Z axis and a probe for calibrating the
mesh. In this configuration it is possible offset the mesh so that the (X, Y)
`reference position` applies zero adjustment. The `reference postion` should
be the location on the bed where a
[Z_ENDSTOP_CALIBRATE](./Manual_Level#calibrating-a-z-endstop)
[Z_ENDSTOP_CALIBRATE](./Manual_Level.md#calibrating-a-z-endstop)
paper test is performed. The bed_mesh module provides the
`zero_reference_position` option for specifying this coordinate:
@@ -370,21 +369,146 @@ are identified in green.
![bedmesh_interpolated](img/bedmesh_faulty_regions.svg)
### Adaptive Meshes
Adaptive bed meshing is a way to speed up the bed mesh generation by only probing
the area of the bed used by the objects being printed. When used, the method will
automatically adjust the mesh parameters based on the area occupied by the defined
print objects.
The adapted mesh area will be computed from the area defined by the boundaries of all
the defined print objects so it covers every object, including any margins defined in
the configuration. After the area is computed, the number of probe points will be
scaled down based on the ratio of the default mesh area and the adapted mesh area. To
illustrate this consider the following example:
For a 150mmx150mm bed with `mesh_min` set to `25,25` and `mesh_max` set to `125,125`,
the default mesh area is a 100mmx100mm square. An adapted mesh area of `50,50`
means a ratio of `0.5x0.5` between the adapted area and default mesh area.
If the `bed_mesh` configuration specified `probe_count` as `7x7`, the adapted bed
mesh will use 4x4 probe points (7 * 0.5 rounded up).
![adaptive_bedmesh](img/adaptive_bed_mesh.svg)
```
[bed_mesh]
speed: 120
horizontal_move_z: 5
mesh_min: 35, 6
mesh_max: 240, 198
probe_count: 5, 3
adaptive_margin: 5
```
- `adaptive_margin` \
_Default Value: 0_ \
Margin (in mm) to add around the area of the bed used by the defined objects. The diagram
below shows the adapted bed mesh area with an `adaptive_margin` of 5mm. The adapted mesh
area (area in green) is computed as the used bed area (area in blue) plus the defined margin.
![adaptive_bedmesh_margin](img/adaptive_bed_mesh_margin.svg)
By nature, adaptive bed meshes use the objects defined by the Gcode file being printed.
Therefore, it is expected that each Gcode file will generate a mesh that probes a different
area of the print bed. Therefore, adapted bed meshes should not be re-used. The expectation
is that a new mesh will be generated for each print if adaptive meshing is used.
It is also important to consider that adaptive bed meshing is best used on machines that can
normally probe the entire bed and achieve a maximum variance less than or equal to 1 layer
height. Machines with mechanical issues that a full bed mesh normally compensates for may
have undesirable results when attempting print moves **outside** of the probed area. If a
full bed mesh has a variance greater than 1 layer height, caution must be taken when using
adaptive bed meshes and attempting print moves outside of the meshed area.
## Surface Scans
Some probes, such as the [Eddy Current Probe](./Eddy_Probe.md), are capable of
"scanning" the surface of the bed. That is, these probes can sample a mesh
without lifting the tool between samples. To activate scanning mode, the
`METHOD=scan` or `METHOD=rapid_scan` probe parameter should be passed in the
`BED_MESH_CALIBRATE` gcode command.
### Scan Height
The scan height is set by the `horizontal_move_z` option in `[bed_mesh]`. In
addition it can be supplied with the `BED_MESH_CALIBRATE` gcode command via the
`HORIZONTAL_MOVE_Z` parameter.
The scan height must be sufficiently low to avoid scanning errors. Typically
a height of 2mm (ie: `HORIZONTAL_MOVE_Z=2`) should work well, presuming that the
probe is mounted correctly.
It should be noted that if the probe is more than 4mm above the surface then the
results will be invalid. Thus, scanning is not possible on beds with severe
surface deviation or beds with extreme tilt that hasn't been corrected.
### Rapid (Continuous) Scanning
When performing a `rapid_scan` one should keep in mind that the results will
have some amount of error. This error should be low enough to be useful on
large print areas with reasonably thick layer heights. Some probes may be
more prone to error than others.
It is not recommended that rapid mode be used to scan a "dense" mesh. Some of
the error introduced during a rapid scan may be gaussian noise from the sensor,
and a dense mesh will reflect this noise (ie: there will be peaks and valleys).
Bed Mesh will attempt to optimize the travel path to provide the best possible
result based on the configuration. This includes avoiding faulty regions
when collecting samples and "overshooting" the mesh when changing direction.
This overshoot improves sampling at the edges of a mesh, however it requires
that the mesh be configured in a way that allows the tool to travel outside
of the mesh.
```
[bed_mesh]
speed: 120
horizontal_move_z: 5
mesh_min: 35, 6
mesh_max: 240, 198
probe_count: 5
scan_overshoot: 8
```
- `scan_overshoot`
_Default Value: 0 (disabled)_\
The maximum amount of travel (in mm) available outside of the mesh.
For rectangular beds this applies to travel on the X axis, and for round beds
it applies to the entire radius. The tool must be able to travel the amount
specified outside of the mesh. This value is used to optimize the travel
path when performing a "rapid scan". The minimum value that may be specified
is 1. The default is no overshoot.
If no scan overshoot is configured then travel path optimization will not
be applied to changes in direction.
## Bed Mesh Gcodes
### Calibration
`BED_MESH_CALIBRATE PROFILE=<name> METHOD=[manual | automatic] [<probe_parameter>=<value>]
[<mesh_parameter>=<value>]`\
`BED_MESH_CALIBRATE PROFILE=<name> METHOD=[manual | automatic | scan | rapid_scan] \
[<probe_parameter>=<value>] [<mesh_parameter>=<value>] [ADAPTIVE=[0|1] \
[ADAPTIVE_MARGIN=<value>]`\
_Default Profile: default_\
_Default Method: automatic if a probe is detected, otherwise manual_
_Default Method: automatic if a probe is detected, otherwise manual_ \
_Default Adaptive: 0_ \
_Default Adaptive Margin: 0_
Initiates the probing procedure for Bed Mesh Calibration.
The mesh will be saved into a profile specified by the `PROFILE` parameter,
or `default` if unspecified. If `METHOD=manual` is selected then manual probing
will occur. When switching between automatic and manual probing the generated
mesh points will automatically be adjusted.
or `default` if unspecified. The `METHOD` parameter takes one of the following
values:
- `METHOD=manual`: enables manual probing using the nozzle and the paper test
- `METHOD=automatic`: Automatic (standard) probing. This is the default.
- `METHOD=scan`: Enables surface scanning. The tool will pause over each position
to collect a sample.
- `METHOD=rapid_scan`: Enables continuous surface scanning.
XY positions are automatically adjusted to include the X and/or Y offsets
when a probing method other than `manual` is selected.
It is possible to specify mesh parameters to modify the probed area. The
following parameters are available:
@@ -398,7 +522,10 @@ following parameters are available:
- `MESH_ORIGIN`
- `ROUND_PROBE_COUNT`
- All beds:
- `MESH_PPS`
- `ALGORITHM`
- `ADAPTIVE`
- `ADAPTIVE_MARGIN`
See the configuration documentation above for details on how each parameter
applies to the mesh.
@@ -486,11 +613,207 @@ This gcode may be used to clear the internal mesh state.
### Apply X/Y offsets
`BED_MESH_OFFSET [X=<value>] [Y=<value>]`
`BED_MESH_OFFSET [X=<value>] [Y=<value>] [ZFADE=<value>]`
This is useful for printers with multiple independent extruders, as an offset
is necessary to produce correct Z adjustment after a tool change. Offsets
should be specified relative to the primary extruder. That is, a positive
X offset should be specified if the secondary extruder is mounted to the
right of the primary extruder, and a positive Y offset should be specified
if the secondary extruder is mounted "behind" the primary extruder.
right of the primary extruder, a positive Y offset should be specified
if the secondary extruder is mounted "behind" the primary extruder, and
a positive ZFADE offset should be specified if the secondary extruder's
nozzle is above the primary extruder's.
Note that a ZFADE offset does *NOT* directly apply additional adjustment. It
is intended to compensate for a `gcode offset` when [mesh fade](#mesh-fade)
is enabled. For example, if a secondary extruder is higher than the primary
and needs a negative gcode offset, ie: `SET_GCODE_OFFSET Z=-.2`, it can be
accounted for in `bed_mesh` with `BED_MESH_OFFSET ZFADE=.2`.
## Bed Mesh Webhooks APIs
### Dumping mesh data
`{"id": 123, "method": "bed_mesh/dump_mesh"}`
Dumps the configuration and state for the current mesh and all
saved profiles.
The `dump_mesh` endpoint takes one optional parameter, `mesh_args`.
This parameter must be an object, where the keys and values are
parameters available to [BED_MESH_CALIBRATE](#bed_mesh_calibrate).
This will update the mesh configuration and probe points using the
supplied parameters prior to returning the result. It is recommended
to omit mesh parameters unless it is desired to visualize the probe points
and/or travel path before performing `BED_MESH_CALIBRATE`.
## Visualization and analysis
Most users will likely find that the visualizers included with
applications such as Mainsail, Fluidd, and Octoprint are sufficient
for basic analysis. However, Klipper's `scripts` folder contains the
`graph_mesh.py` script that may be used to perform additional
visualizations and more detailed analysis, particularly useful
for debugging hardware or the results produced by `bed_mesh`:
```
usage: graph_mesh.py [-h] {list,plot,analyze,dump} ...
Graph Bed Mesh Data
positional arguments:
{list,plot,analyze,dump}
list List available plot types
plot Plot a specified type
analyze Perform analysis on mesh data
dump Dump API response to json file
options:
-h, --help show this help message and exit
```
### Pre-requisites
Like most graphing tools provided by Klipper, `graph_mesh.py` requires
the `matplotlib` and `numpy` python dependencies. In addition, connecting
to Klipper via Moonraker's websocket requires the `websockets` python
dependency. While all visualizations can be output to an `svg` file, most of
the visualizations offered by `graph_mesh.py` are better viewed in live
preview mode on a desktop class PC. For example, the 3D visualizations may be
rotated and zoomed in preview mode, and the path visualizations can optionally
be animated in preview mode.
### Plotting Mesh data
The `graph_mesh.py` tool can plot several types of visualizations.
Available types can be shown by running `graph_mesh.py list`:
```
graph_mesh.py list
points Plot original generated points
path Plot probe travel path
rapid Plot rapid scan travel path
probedz Plot probed Z values
meshz Plot mesh Z values
overlay Plots the current probed mesh overlaid with a profile
delta Plots the delta between current probed mesh and a profile
```
Several options are available when plotting visualizations:
```
usage: graph_mesh.py plot [-h] [-a] [-s] [-p PROFILE_NAME] [-o OUTPUT] <plot type> <input>
positional arguments:
<plot type> Type of data to graph
<input> Path/url to Klipper Socket or path to json file
options:
-h, --help show this help message and exit
-a, --animate Animate paths in live preview
-s, --scale-plot Use axis limits reported by Klipper to scale plot X/Y
-p PROFILE_NAME, --profile-name PROFILE_NAME
Optional name of a profile to plot for 'probedz'
-o OUTPUT, --output OUTPUT
Output file path
```
Below is a description of each argument:
- `plot type`: A required positional argument designating the type of
visualization to generate. Must be one of the types output by the
`graph_mesh.py list` command.
- `input`: A required positional argument containing a path or url
to the input source. This must be one of the following:
- A path to Klipper's Unix Domain Socket
- A url to an instance of Moonraker
- A path to a json file produced by `graph_mesh.py dump <input>`
- `-a`: Optional animation for the `path` and `rapid` visualization types.
Animations only apply to a live preview.
- `-s`: Optionally scales a plot using the `axis_minimum` and `axis_maximum`
values reported by Klipper's `toolhead` object when the dump file was
generated.
- `-p`: A profile name that may be specified when generating the
`probedz` 3D mesh visualization. When generating an `overlay` or
`delta` visualization this argument must be provided.
- `-o`: An optional file path indicating that the script should save the
visualization to this location rather than run in preview mode. Images
are saved in `svg` format.
For example, to plot an animated rapid path, connecting via Klipper's unix
socket:
```
graph_mesh.py plot -a rapid ~/printer_data/comms/klippy.sock
```
Or to plot a 3d visualization of the mesh, connecting via Moonraker:
```
graph_mesh.py plot meshz http://my-printer.local
```
### Bed Mesh Analysis
The `graph_mesh.py` tool may also be used to perform an analysis on the
data provided by the [bed_mesh/dump_mesh](#dumping-mesh-data) API:
```
graph_mesh.py analyze <input>
```
As with the `plot` command, the `<input>` must be a path to Klipper's
unix socket, a URL to an instance of Moonraker, or a path to a json file
generated by the dump command.
To begin, the analysis will perform various checks on the points and
probe paths generated by `bed_mesh` at the time of the dump. This
includes the following:
- The number of probe points generated, without any additions
- The number of probe points generated including any points generated
as the result faulty regions and/or a configured zero reference position.
- The number of probe points generated when performing a rapid scan.
- The total number of moves generated for a rapid scan.
- A validation that the probe points generated for a rapid scan are
identical to the probe points generated for a standard probing procedure.
- A "backtracking" check for both the standard probe path and a rapid scan
path. Backtracking can be defined as moving to the same position more than
once during the probing procedure. Backtracking should never occur during a
standard probe. Faulty regions *can* result in backtracking during a rapid
scan in an attempt to avoid entering a faulty region when approaching or
leaving a probe location, however should never occur otherwise.
Next each probed mesh present in the dump will by analyzed, beginning with
the mesh loaded at the time of the dump (if present) and followed by any
saved profiles. The following data is extracted:
- Mesh shape (Min X,Y, Max X,Y Probe Count)
- Mesh Z range, (Minimum Z, Maximum Z)
- Mean Z value in the mesh
- Standard Deviation of the Z values in the Mesh
In addition to the above, a delta analysis is performed between meshes
with the same shape, reporting the following:
- The range of the delta between to meshes (Minimum and Maximum)
- The mean delta
- Standard Deviation of the delta
- The absolute maximum difference
- The absolute mean
### Save mesh data to a file
The `dump` command may be used to save the response to a file which
can be shared for analysis when troubleshooting:
```
graph_mesh.py dump -o <output file name> <input>
```
The `<input>` should be a path to Klipper's unix socket or
a URL to an instance of Moonraker. The `-o` option may be used to
specify the path to the output file. If omitted, the file will be
saved in the working directory, with a file name in the following
format:
`klipper-bedmesh-{year}{month}{day}{hour}{minute}{second}.json`

View File

@@ -354,6 +354,26 @@ micro-controller.
| 1 stepper (200Mhz) | 39 |
| 3 stepper (200Mhz) | 181 |
### SAME70 step rate benchmark
The following configuration sequence is used on the SAME70:
```
allocate_oids count=3
config_stepper oid=0 step_pin=PC18 dir_pin=PB5 invert_step=-1 step_pulse_ticks=0
config_stepper oid=1 step_pin=PC16 dir_pin=PD10 invert_step=-1 step_pulse_ticks=0
config_stepper oid=2 step_pin=PC28 dir_pin=PA4 invert_step=-1 step_pulse_ticks=0
finalize_config crc=0
```
The test was last run on commit `34e9ea55` with gcc version
`arm-none-eabi-gcc (NixOS 10.3-2021.10) 10.3.1` on a SAME70Q20B
micro-controller.
| same70 | ticks |
| -------------------- | ----- |
| 1 stepper | 45 |
| 3 stepper | 190 |
### AR100 step rate benchmark ###
The following configuration sequence is used on AR100 CPU (Allwinner A64):
@@ -366,7 +386,7 @@ finalize_config crc=0
```
The test was last run on commit `08d037c6` with gcc version
The test was last run on commit `b7978d37` with gcc version
`or1k-linux-musl-gcc (GCC) 9.2.0` on an Allwinner A64-H
micro-controller.
@@ -375,9 +395,9 @@ micro-controller.
| 1 stepper | 85 |
| 3 stepper | 359 |
### RP2040 step rate benchmark
### RPxxxx step rate benchmark
The following configuration sequence is used on the RP2040:
The following configuration sequence is used on the RP2040 and RP2350:
```
allocate_oids count=3
@@ -387,15 +407,26 @@ config_stepper oid=2 step_pin=gpio27 dir_pin=gpio5 invert_step=-1 step_pulse_tic
finalize_config crc=0
```
The test was last run on commit `59314d99` with gcc version
`arm-none-eabi-gcc (Fedora 10.2.0-4.fc34) 10.2.0` on a Raspberry Pi
Pico board.
The test was last run on commit `f6718291` with gcc version
`arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0` on Raspberry Pi
Pico and Pico 2 boards.
| rp2040 | ticks |
| rp2040 (*) | ticks |
| -------------------- | ----- |
| 1 stepper | 5 |
| 3 stepper | 22 |
| rp2350 | ticks |
| -------------------- | ----- |
| 1 stepper | 36 |
| 3 stepper | 169 |
(*) Note that the reported rp2040 ticks are relative to a 12Mhz
scheduling timer and do not correspond to its 125Mhz internal ARM
processing rate. It is expected that 5 scheduling ticks corresponds to
~47 ARM core cycles and 22 scheduling ticks corresponds to ~224 ARM
core cycles.
### Linux MCU step rate benchmark
The following configuration sequence is used on a Raspberry Pi:
@@ -456,7 +487,8 @@ hub.
| sam4s8c (USB) | 650K | 8d4a5c16 | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 |
| samd51 (USB) | 864K | 01d2183f | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 |
| stm32f446 (USB) | 870K | 01d2183f | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 |
| rp2040 (USB) | 873K | c5667193 | arm-none-eabi-gcc (Fedora 10.2.0-4.fc34) 10.2.0 |
| rp2040 (USB) | 885K | f6718291 | arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0 |
| rp2350 (USB) | 885K | f6718291 | arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0 |
## Host Benchmarks

View File

@@ -31,7 +31,7 @@ adapter. This is typically done by creating a new file named
allow-hotplug can0
iface can0 can static
bitrate 1000000
up ifconfig $IFACE txqueuelen 128
up ip link set $IFACE txqueuelen 128
```
## Terminating Resistors
@@ -113,7 +113,7 @@ Some important notes when using this mode:
allow-hotplug can0
iface can0 can static
bitrate 1000000
up ifconfig $IFACE txqueuelen 128
up ip link set $IFACE txqueuelen 128
```
* The "bridge mcu" is not actually on the CAN bus. Messages to and

View File

@@ -52,6 +52,56 @@ Reordered messages is a severe problem that must be fixed. It will
result in unstable behavior and can lead to confusing errors at any
part of a print.
## Use an appropriate txqueuelen setting
The Klipper code uses the Linux kernel to manage CAN bus traffic. By
default, the kernel will only queue 10 CAN transmit packets. It is
recommended to [configure the can0 device](CANBUS.md#host-hardware)
with a `txqueuelen 128` to increase that size.
If Klipper transmits a packet and Linux has filled all of its transmit
queue space then Linux will drop that packet and messages like the
following will appear in the Klipper log:
```
Got error -1 in can write: (105)No buffer space available
```
Klipper will automatically retransmit the lost messages as part of its
normal application level message retransmit system. Thus, this log
message is a warning and it does not indicate an unrecoverable error.
If a complete CAN bus failure occurs (such as a CAN wire break) then
Linux will not be able to transmit any messages on the CAN bus and it
is common to find the above message in the Klipper log. In this case,
the log message is a symptom of a larger problem (the inability to
transmit any messages) and is not directly related to Linux
`txqueuelen`.
One may check the current queue size by running the Linux command `ip
link show can0`. It should report a bunch of text including the
snippet `qlen 128`. If one sees something like `qlen 10` then it
indicates the CAN device has not been properly configured.
It is not recommended to use a `txqueuelen` significantly larger
than 128. A CAN bus running at a frequency of 1000000 will typically
take around 120us to transmit a CAN packet. Thus a queue of 128
packets is likely to take around 15-20ms to drain. A substantially
larger queue could cause excessive spikes in message round-trip-time
which could lead to unrecoverable errors. Said another way, Klipper's
application retransmit system is more robust if it does not have to
wait for Linux to drain an excessively large queue of possibly stale
data. This is analogous to the problem of
[bufferbloat](https://en.wikipedia.org/wiki/Bufferbloat) on internet
routers.
Under normal circumstances Klipper may utilize ~25 queue slots per
MCU - typically only utilizing more slots during retransmits.
(Specifically, the Klipper host may transmit up to 192 bytes to each
Klipper MCU before receiving an acknowledgment from that MCU.) If a
single CAN bus has 5 or more Klipper MCUs on it, then it might be
necessary to increase the `txqueuelen` above the recommended value
of 128. However, as above, care should be taken when selecting a new
value to avoid excessive round-trip-time latency.
## Obtaining candump logs
The CAN bus messages sent to and from the micro-controller are handled

View File

@@ -136,8 +136,9 @@ provides further information on the mechanics of moves.
* The ToolHead class (in toolhead.py) handles "look-ahead" and tracks
the timing of printing actions. The main codepath for a move is:
`ToolHead.move() -> MoveQueue.add_move() -> MoveQueue.flush() ->
Move.set_junction() -> ToolHead._process_moves()`.
`ToolHead.move() -> LookAheadQueue.add_move() ->
LookAheadQueue.flush() -> Move.set_junction() ->
ToolHead._process_moves()`.
* ToolHead.move() creates a Move() object with the parameters of the
move (in cartesian space and in units of seconds and millimeters).
* The kinematics class is given the opportunity to audit each move
@@ -146,10 +147,10 @@ provides further information on the mechanics of moves.
may raise an error if the move is not valid. If check_move()
completes successfully then the underlying kinematics must be able
to handle the move.
* MoveQueue.add_move() places the move object on the "look-ahead"
queue.
* MoveQueue.flush() determines the start and end velocities of each
move.
* LookAheadQueue.add_move() places the move object on the
"look-ahead" queue.
* LookAheadQueue.flush() determines the start and end velocities of
each move.
* Move.set_junction() implements the "trapezoid generator" on a
move. The "trapezoid generator" breaks every move into three parts:
a constant acceleration phase, followed by a constant velocity
@@ -170,17 +171,18 @@ provides further information on the mechanics of moves.
placed on a "trapezoid motion queue": `ToolHead._process_moves() ->
trapq_append()` (in klippy/chelper/trapq.c). The step times are then
generated: `ToolHead._process_moves() ->
ToolHead._update_move_time() -> MCU_Stepper.generate_steps() ->
itersolve_generate_steps() -> itersolve_gen_steps_range()` (in
klippy/chelper/itersolve.c). The goal of the iterative solver is to
find step times given a function that calculates a stepper position
from a time. This is done by repeatedly "guessing" various times
until the stepper position formula returns the desired position of
the next step on the stepper. The feedback produced from each guess
is used to improve future guesses so that the process rapidly
converges to the desired time. The kinematic stepper position
formulas are located in the klippy/chelper/ directory (eg,
kin_cart.c, kin_corexy.c, kin_delta.c, kin_extruder.c).
ToolHead._advance_move_time() -> ToolHead._advance_flush_time() ->
MCU_Stepper.generate_steps() -> itersolve_generate_steps() ->
itersolve_gen_steps_range()` (in klippy/chelper/itersolve.c). The
goal of the iterative solver is to find step times given a function
that calculates a stepper position from a time. This is done by
repeatedly "guessing" various times until the stepper position
formula returns the desired position of the next step on the
stepper. The feedback produced from each guess is used to improve
future guesses so that the process rapidly converges to the desired
time. The kinematic stepper position formulas are located in the
klippy/chelper/ directory (eg, kin_cart.c, kin_corexy.c,
kin_delta.c, kin_extruder.c).
* Note that the extruder is handled in its own kinematic class:
`ToolHead._process_moves() -> PrinterExtruder.move()`. Since
@@ -357,10 +359,10 @@ Useful steps:
be efficient as it is typically only called during homing and
probing operations.
5. Other methods. Implement the `check_move()`, `get_status()`,
`get_steppers()`, `home()`, and `set_position()` methods. These
functions are typically used to provide kinematic specific checks.
However, at the start of development one can use boiler-plate code
here.
`get_steppers()`, `home()`, `clear_homing_state()`, and `set_position()`
methods. These functions are typically used to provide kinematic
specific checks. However, at the start of development one can use
boiler-plate code here.
6. Implement test cases. Create a g-code file with a series of moves
that can test important cases for the given kinematics. Follow the
[debugging documentation](Debugging.md) to convert this g-code file

View File

@@ -8,6 +8,84 @@ All dates in this document are approximate.
## Changes
20241203: The resonance test has been changed to include slow sweeping
moves. This change requires that testing point(s) have some clearance
in X/Y plane (+/- 30 mm from the test point should suffice when using
the default settings). The new test should generally produce more
accurate and reliable test results. However, if required, the previous
test behavior can be restored by adding options `sweeping_period: 0` and
`accel_per_hz: 75` to the `[resonance_tester]` config section.
20241201: In some cases Klipper may have ignored leading characters or
spaces in a traditional G-Code command. For example, "99M123" may have
been interpreted as "M123" and "M 321" may have been interpreted as
"M321". Klipper will now report these cases with an "Unknown command"
warning.
20241112: Option `CHIPS=<chip_name>` in `TEST_RESONANCES` and
`SHAPER_CALIBRATE` requires specifying the full name(s) of the accel
chip(s). For example, `adxl345 rpi` instead of short name - `rpi`.
20240912: `SET_PIN`, `SET_SERVO`, `SET_FAN_SPEED`, `M106`, and `M107`
commands are now collated. Previously, if many updates to the same
object were issued faster than the minimum scheduling time (typically
100ms) then actual updates could be queued far into the future. Now if
many updates are issued in rapid succession then it is possible that
only the latest request will be applied. If the previous behavior is
requried then consider adding explicit `G4` delay commands between
updates.
20240912: Support for `maximum_mcu_duration` and `static_value`
parameters in `[output_pin]` config sections have been removed. These
options have been deprecated since 20240123.
20240415: The `on_error_gcode` parameter in the `[virtual_sdcard]`
config section now has a default. If this parameter is not specified
it now defaults to `TURN_OFF_HEATERS`. If the previous behavior is
desired (take no default action on an error during a virtual_sdcard
print) then define `on_error_gcode` with an empty value.
20240313: The `max_accel_to_decel` parameter in the `[printer]` config
section has been deprecated. The `ACCEL_TO_DECEL` parameter of the
`SET_VELOCITY_LIMIT` command has been deprecated. The
`printer.toolhead.max_accel_to_decel` status has been removed. Use the
[minimum_cruise_ratio parameter](./Config_Reference.md#printer)
instead. The deprecated features will be removed in the near future,
and using them in the interim may result in subtly different behavior.
20240215: Several deprecated features have been removed. Using "NTC
100K beta 3950" as a thermistor name has been removed (deprecated on
20211110). The `SYNC_STEPPER_TO_EXTRUDER` and
`SET_EXTRUDER_STEP_DISTANCE` commands have been removed, and the
extruder `shared_heater` config option has been removed (deprecated on
20220210). The bed_mesh `relative_reference_index` option has been
removed (deprecated on 20230619).
20240123: The output_pin SET_PIN CYCLE_TIME parameter has been
removed. Use the new
[pwm_cycle_time](Config_Reference.md#pwm_cycle_time) module if it is
necessary to dynamically change a pwm pin's cycle time.
20240123: The output_pin `maximum_mcu_duration` parameter is
deprecated. Use a [pwm_tool config section](Config_Reference.md#pwm_tool)
instead. The option will be removed in the near future.
20240123: The output_pin `static_value` parameter is deprecated.
Replace with `value` and `shutdown_value` parameters. The option will
be removed in the near future.
20231216: The `[hall_filament_width_sensor]` is changed to trigger filament runout
when the thickness of the filament exceeds `max_diameter`. The maximum diameter
defaults to `default_nominal_filament_diameter + max_difference`. See
[[hall_filament_width_sensor] configuration
reference](./Config_Reference.md#hall_filament_width_sensor) for more details.
20231207: Several undocumented config parameters in the `[printer]`
config section have been removed (the buffer_time_low,
buffer_time_high, buffer_time_start, and move_flush_time parameters).
20231110: Klipper v0.12.0 released.
20230826: If `safe_distance` is set or calculated to be 0 in `[dual_carriage]`,
the carriages proximity checks will be disabled as per documentation. A user
may wish to configure `safe_distance` explicitly to prevent accidental crashes

View File

@@ -88,16 +88,31 @@ kinematics:
# deltesian, polar, winch, or none. This parameter must be specified.
max_velocity:
# Maximum velocity (in mm/s) of the toolhead (relative to the
# print). This parameter must be specified.
# print). This value may be changed at runtime using the
# SET_VELOCITY_LIMIT command. This parameter must be specified.
max_accel:
# Maximum acceleration (in mm/s^2) of the toolhead (relative to the
# print). This parameter must be specified.
#max_accel_to_decel:
# A pseudo acceleration (in mm/s^2) controlling how fast the
# toolhead may go from acceleration to deceleration. It is used to
# reduce the top speed of short zig-zag moves (and thus reduce
# printer vibration from these moves). The default is half of
# max_accel.
# print). Although this parameter is described as a "maximum"
# acceleration, in practice most moves that accelerate or decelerate
# will do so at the rate specified here. The value specified here
# may be changed at runtime using the SET_VELOCITY_LIMIT command.
# This parameter must be specified.
#minimum_cruise_ratio: 0.5
# Most moves will accelerate to a cruising speed, travel at that
# cruising speed, and then decelerate. However, some moves that
# travel a short distance could nominally accelerate and then
# immediately decelerate. This option reduces the top speed of these
# moves to ensure there is always a minimum distance traveled at a
# cruising speed. That is, it enforces a minimum distance traveled
# at cruising speed relative to the total distance traveled. It is
# intended to reduce the top speed of short zigzag moves (and thus
# reduce printer vibration from these moves). For example, a
# minimum_cruise_ratio of 0.5 would ensure that a standalone 1.5mm
# move would have a minimum cruising distance of 0.75mm. Specify a
# ratio of 0.0 to disable this feature (there would be no minimum
# cruising distance enforced between acceleration and deceleration).
# The value specified here may be changed at runtime using the
# SET_VELOCITY_LIMIT command. The default is 0.5.
#square_corner_velocity: 5.0
# The maximum velocity (in mm/s) that the toolhead may travel a 90
# degree corner at. A non-zero value can reduce changes in extruder
@@ -107,7 +122,11 @@ max_accel:
# larger than 90 degrees will have a higher cornering velocity while
# corners with angles less than 90 degrees will have a lower
# cornering velocity. If this is set to zero then the toolhead will
# decelerate to zero at each corner. The default is 5mm/s.
# decelerate to zero at each corner. The value specified here may be
# changed at runtime using the SET_VELOCITY_LIMIT command. The
# default is 5mm/s.
#max_accel_to_decel:
# This parameter is deprecated and should no longer be used.
```
### [stepper]
@@ -971,18 +990,21 @@ Visual Examples:
# where Z = 0. When this option is specified the mesh will be offset
# so that zero Z adjustment occurs at this location. The default is
# no zero reference.
#relative_reference_index:
# **DEPRECATED, use the "zero_reference_position" option**
# The legacy option superceded by the "zero reference position".
# Rather than a coordinate this option takes an integer "index" that
# refers to the location of one of the generated points. It is recommended
# to use the "zero_reference_position" instead of this option for new
# configurations. The default is no relative reference index.
#faulty_region_1_min:
#faulty_region_1_max:
# Optional points that define a faulty region. See docs/Bed_Mesh.md
# for details on faulty regions. Up to 99 faulty regions may be added.
# By default no faulty regions are set.
#adaptive_margin:
# An optional margin (in mm) to be added around the bed area used by
# the defined print objects when generating an adaptive mesh.
#scan_overshoot:
# The maximum amount of travel (in mm) available outside of the mesh.
# For rectangular beds this applies to travel on the X axis, and for round beds
# it applies to the entire radius. The tool must be able to travel the amount
# specified outside of the mesh. This value is used to optimize the travel
# path when performing a "rapid scan". The minimum value that may be specified
# is 1. The default is no overshoot.
```
### [bed_tilt]
@@ -1457,7 +1479,8 @@ path:
# be provided.
#on_error_gcode:
# A list of G-Code commands to execute when an error is reported.
# See docs/Command_Templates.md for G-Code format. The default is to
# run TURN_OFF_HEATERS.
```
### [sdcard_loop]
@@ -1652,8 +1675,9 @@ Support for LIS2DW accelerometers.
```
[lis2dw]
cs_pin:
# The SPI enable pin for the sensor. This parameter must be provided.
#cs_pin:
# The SPI enable pin for the sensor. This parameter must be provided
# if using SPI.
#spi_speed: 5000000
# The SPI speed (in hz) to use when communicating with the chip.
# The default is 5000000.
@@ -1663,6 +1687,46 @@ cs_pin:
#spi_software_miso_pin:
# See the "common SPI settings" section for a description of the
# above parameters.
#i2c_address:
# Default is 25 (0x19). If SA0 is high, it would be 24 (0x18) instead.
#i2c_mcu:
#i2c_bus:
#i2c_software_scl_pin:
#i2c_software_sda_pin:
#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.
```
### [lis3dh]
Support for LIS3DH accelerometers.
```
[lis3dh]
#cs_pin:
# The SPI enable pin for the sensor. This parameter must be provided
# if using SPI.
#spi_speed: 5000000
# The SPI speed (in hz) to use when communicating with the chip.
# The default is 5000000.
#spi_bus:
#spi_software_sclk_pin:
#spi_software_mosi_pin:
#spi_software_miso_pin:
# See the "common SPI settings" section for a description of the
# above parameters.
#i2c_address:
# Default is 25 (0x19). If SA0 is high, it would be 24 (0x18) instead.
#i2c_mcu:
#i2c_bus:
#i2c_software_scl_pin:
#i2c_software_sda_pin:
#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.
```
@@ -1726,11 +1790,14 @@ section of the measuring resonances guide for more information on
# auto-calibration (with 'SHAPER_CALIBRATE' command). By default no
# maximum smoothing is specified. Refer to Measuring_Resonances guide
# for more details on using this feature.
#move_speed: 50
# The speed (in mm/s) to move the toolhead to and between test points
# during the calibration. The default is 50.
#min_freq: 5
# Minimum frequency to test for resonances. The default is 5 Hz.
#max_freq: 133.33
# Maximum frequency to test for resonances. The default is 133.33 Hz.
#accel_per_hz: 75
#accel_per_hz: 60
# This parameter is used to determine which acceleration to use to
# test a specific frequency: accel = accel_per_hz * freq. Higher the
# value, the higher is the energy of the oscillations. Can be set to
@@ -1744,6 +1811,13 @@ section of the measuring resonances guide for more information on
# hz_per_sec. Small values make the test slow, and the large values
# will decrease the precision of the test. The default value is 1.0
# (Hz/sec == sec^-2).
#sweeping_accel: 400
# An acceleration of slow sweeping moves. The default is 400 mm/sec^2.
#sweeping_period: 1.2
# A period of slow sweeping moves. Setting this parameter to 0
# disables slow sweeping moves. Avoid setting it to a too small
# non-zero value in order to not poison the measurements.
# The default is 1.2 sec which is a good all-round choice.
```
## Config file helpers
@@ -1980,11 +2054,48 @@ z_offset:
# See the "probe" section for more information on the parameters above.
```
### [probe_eddy_current]
Support for eddy current inductive probes. One may define this section
(instead of a probe section) to enable this probe. See the
[command reference](G-Codes.md#probe_eddy_current) for further information.
```
[probe_eddy_current my_eddy_probe]
sensor_type: ldc1612
# The sensor chip used to perform eddy current measurements. This
# parameter must be provided and must be set to ldc1612.
#intb_pin:
# MCU gpio pin connected to the ldc1612 sensor's INTB pin (if
# available). The default is to not use the INTB pin.
#z_offset:
# The nominal distance (in mm) between the nozzle and bed that a
# probing attempt should stop at. This parameter must be provided.
#i2c_address:
#i2c_mcu:
#i2c_bus:
#i2c_software_scl_pin:
#i2c_software_sda_pin:
#i2c_speed:
# The i2c settings for the sensor chip. See the "common I2C
# settings" section for a description of the above parameters.
#x_offset:
#y_offset:
#speed:
#lift_speed:
#samples:
#sample_retract_dist:
#samples_result:
#samples_tolerance:
#samples_tolerance_retries:
# See the "probe" section for information on these parameters.
```
### [axis_twist_compensation]
A tool to compensate for inaccurate probe readings due to twist in X gantry. See
the [Axis Twist Compensation Guide](Axis_Twist_Compensation.md) for more
detailed information regarding symptoms, configuration and setup.
A tool to compensate for inaccurate probe readings due to twist in X or Y
gantry. See the [Axis Twist Compensation Guide](Axis_Twist_Compensation.md)
for more detailed information regarding symptoms, configuration and setup.
```
[axis_twist_compensation]
@@ -1997,16 +2108,33 @@ detailed information regarding symptoms, configuration and setup.
calibrate_start_x: 20
# Defines the minimum X coordinate of the calibration
# This should be the X coordinate that positions the nozzle at the starting
# calibration position. This parameter must be provided.
# calibration position.
calibrate_end_x: 200
# Defines the maximum X coordinate of the calibration
# This should be the X coordinate that positions the nozzle at the ending
# calibration position. This parameter must be provided.
# calibration position.
calibrate_y: 112.5
# Defines the Y coordinate of the calibration
# This should be the Y coordinate that positions the nozzle during the
# calibration process. This parameter must be provided and is recommended to
# calibration process. This parameter is recommended to
# be near the center of the bed
# For Y-axis twist compensation, specify the following parameters:
calibrate_start_y: ...
# Defines the minimum Y coordinate of the calibration
# This should be the Y coordinate that positions the nozzle at the starting
# calibration position for the Y axis. This parameter must be provided if
# compensating for Y axis twist.
calibrate_end_y: ...
# Defines the maximum Y coordinate of the calibration
# This should be the Y coordinate that positions the nozzle at the ending
# calibration position for the Y axis. This parameter must be provided if
# compensating for Y axis twist.
calibrate_x: ...
# Defines the X coordinate of the calibration for Y axis twist compensation
# This should be the X coordinate that positions the nozzle during the
# calibration process for Y axis twist compensation. This parameter must be
# provided and is recommended to be near the center of the bed.
```
## Additional stepper motors and extruders
@@ -2341,6 +2469,69 @@ temperature sensors that are reported via the M105 command.
# parameter.
```
### [temperature_probe]
Reports probe coil temperature. Includes optional thermal drift
calibration for eddy current based probes. A `[temperature_probe]`
section may be linked to a `[probe_eddy_current]` by using the same
postfix for both sections.
```
[temperature_probe my_probe]
#sensor_type:
#sensor_pin:
#min_temp:
#max_temp:
# Temperature sensor configuration.
# See the "extruder" section for the definition of the above
# parameters.
#smooth_time:
# A time value (in seconds) over which temperature measurements will
# be smoothed to reduce the impact of measurement noise. The default
# is 2.0 seconds.
#gcode_id:
# See the "heater_generic" section for the definition of this
# parameter.
#speed:
# The travel speed [mm/s] for xy moves during calibration. Default
# is the speed defined by the probe.
#horizontal_move_z:
# The z distance [mm] from the bed at which xy moves will occur
# during calibration. Default is 2mm.
#resting_z:
# The z distance [mm] from the bed at which the tool will rest
# to heat the probe coil during calibration. Default is .4mm
#calibration_position:
# The X, Y, Z position where the tool should be moved when
# probe drift calibration initializes. This is the location
# where the first manual probe will occur. If omitted, the
# default behavior is not to move the tool prior to the first
# manual probe.
#calibration_bed_temp:
# The maximum safe bed temperature (in C) used to heat the probe
# during probe drift calibration. When set, the calibration
# procedure will turn on the bed after the first sample is
# taken. When the calibration procedure is complete the bed
# temperature will be set to zero. When omitted the default
# behavior is not to set the bed temperature.
#calibration_extruder_temp:
# The extruder temperature (in C) set probe during drift calibration.
# When this option is supplied the procedure will wait for until the
# specified temperature is reached before requesting the first manual
# probe. When the calibration procedure is complete the extruder
# temperature will be set to 0. When omitted the default behavior is
# not to set the extruder temperature.
#extruder_heating_z: 50.
# The Z location where extruder heating will occur if the
# "calibration_extruder_temp" option is set. Its recommended to heat
# the extruder some distance from the bed to minimize its impact on
# the probe coil temperature. The default is 50.
#max_validation_temp: 60.
# The maximum temperature used to validate the calibration. It is
# recommended to set this to a value between 100 and 120 for enclosed
# printers. The default is 60.
```
## Temperature sensors
Klipper includes definitions for many types of temperature sensors.
@@ -2440,9 +2631,9 @@ sensor_pin:
# name in the above list.
```
### BMP280/BME280/BME680 temperature sensor
### BMP180/BMP280/BME280/BMP388/BME680 temperature sensor
BMP280/BME280/BME680 two wire interface (I2C) environmental sensors.
BMP180/BMP280/BME280/BMP388/BME680 two wire interface (I2C) environmental sensors.
Note that these sensors are not intended for use with extruders and
heater beds, but rather for monitoring ambient temperature (C),
pressure (hPa), relative humidity and in case of the BME680 gas level.
@@ -2453,8 +2644,8 @@ temperature.
```
sensor_type: BME280
#i2c_address:
# Default is 118 (0x76). Some BME280 sensors have an address of 119
# (0x77).
# Default is 118 (0x76). The BMP180, BMP388 and some BME280 sensors
# have an address of 119 (0x77).
#i2c_mcu:
#i2c_bus:
#i2c_software_scl_pin:
@@ -2525,6 +2716,25 @@ sensor_type:
# Interval in seconds between readings. Default is 30
```
### SHT3X sensor
SHT3X family two wire interface (I2C) environmental sensor. These sensors
have a range of -55~125 C, so are usable for e.g. chamber temperature
monitoring. They can also function as simple fan/heater controllers.
```
sensor_type: SHT3X
#i2c_address:
# Default is 68 (0x44).
#i2c_mcu:
#i2c_bus:
#i2c_software_scl_pin:
#i2c_software_sda_pin:
#i2c_speed:
# See the "common I2C settings" section for a description of the
# above parameters.
```
### LM75 temperature sensor
LM75/LM75A two wire (I2C) connected temperature sensors. These sensors
@@ -2551,7 +2761,7 @@ sensor_type: LM75
### Builtin micro-controller temperature sensor
The atsam, atsamd, and stm32 micro-controllers contain an internal
The atsam, atsamd, stm32 and rp2040 micro-controllers contain an internal
temperature sensor. One can use the "temperature_mcu" sensor to
monitor these temperatures.
@@ -3088,24 +3298,12 @@ pin:
# If this is true, the value fields should be between 0 and 1; if it
# is false the value fields should be either 0 or 1. The default is
# False.
#static_value:
# If this is set, then the pin is assigned to this value at startup
# and the pin can not be changed during runtime. A static pin uses
# slightly less ram in the micro-controller. The default is to use
# runtime configuration of pins.
#value:
# The value to initially set the pin to during MCU configuration.
# The default is 0 (for low voltage).
#shutdown_value:
# The value to set the pin to on an MCU shutdown event. The default
# is 0 (for low voltage).
#maximum_mcu_duration:
# The maximum duration a non-shutdown value may be driven by the MCU
# without an acknowledge from the host.
# If host can not keep up with an update, the MCU will shutdown
# and set all pins to their respective shutdown values.
# Default: 0 (disabled)
# Usual values are around 5 seconds.
#cycle_time: 0.100
# The amount of time (in seconds) per PWM cycle. It is recommended
# this be 10 milliseconds or greater when using software based PWM.
@@ -3125,6 +3323,54 @@ pin:
# then the 'value' parameter can be specified using the desired
# amperage for the stepper. The default is to not scale the 'value'
# parameter.
#maximum_mcu_duration:
#static_value:
# These options are deprecated and should no longer be specified.
```
### [pwm_tool]
Pulse width modulation digital output pins capable of high speed
updates (one may define any number of sections with an "output_pin"
prefix). Pins configured here will be setup as output pins and one may
modify them at run-time using "SET_PIN PIN=my_pin VALUE=.1" type
extended [g-code commands](G-Codes.md#output_pin).
```
[pwm_tool my_tool]
pin:
# The pin to configure as an output. This parameter must be provided.
#maximum_mcu_duration:
# The maximum duration a non-shutdown value may be driven by the MCU
# without an acknowledge from the host.
# If host can not keep up with an update, the MCU will shutdown
# and set all pins to their respective shutdown values.
# Default: 0 (disabled)
# Usual values are around 5 seconds.
#value:
#shutdown_value:
#cycle_time: 0.100
#hardware_pwm: False
#scale:
# See the "output_pin" section for the definition of these parameters.
```
### [pwm_cycle_time]
Run-time configurable output pins with dynamic pwm cycle timing (one
may define any number of sections with an "pwm_cycle_time" prefix).
Pins configured here will be setup as output pins and one may modify
them at run-time using "SET_PIN PIN=my_pin VALUE=.1 CYCLE_TIME=0.100"
type extended [g-code commands](G-Codes.md#pwm_cycle_time).
```
[pwm_cycle_time my_pin]
pin:
#value:
#shutdown_value:
#cycle_time: 0.100
#scale:
# See the "output_pin" section for information on these parameters.
```
### [static_digital_output]
@@ -3212,6 +3458,18 @@ run_current:
# set, "stealthChop" mode will be enabled if the stepper motor
# velocity is below this value. The default is 0, which disables
# "stealthChop" mode.
#coolstep_threshold:
# The velocity (in mm/s) to set the TMC driver internal "CoolStep"
# threshold to. If set, the coolstep feature will be enabled when
# the stepper motor velocity is near or above this value. Important
# - if coolstep_threshold is set and "sensorless homing" is used,
# then one must ensure that the homing speed is above the coolstep
# threshold! The default is to not enable the coolstep feature.
#high_velocity_threshold:
# The velocity (in mm/s) to set the TMC driver internal "high
# velocity" threshold (THIGH) to. This is typically used to disable
# the "CoolStep" feature at high speeds. The default is to not set a
# TMC "high velocity" threshold.
#driver_MSLUT0: 2863314260
#driver_MSLUT1: 1251300522
#driver_MSLUT2: 608774441
@@ -3242,11 +3500,19 @@ run_current:
#driver_TOFF: 4
#driver_HEND: 7
#driver_HSTRT: 0
#driver_VHIGHFS: 0
#driver_VHIGHCHM: 0
#driver_PWM_AUTOSCALE: True
#driver_PWM_FREQ: 1
#driver_PWM_GRAD: 4
#driver_PWM_AMPL: 128
#driver_SGT: 0
#driver_SEMIN: 0
#driver_SEUP: 0
#driver_SEMAX: 0
#driver_SEDN: 0
#driver_SEIMIN: 0
#driver_SFILT: 0
# Set the given register during the configuration of the TMC2130
# chip. This may be used to set custom motor parameters. The
# defaults for each parameter are next to the parameter name in the
@@ -3343,6 +3609,13 @@ run_current:
#sense_resistor: 0.110
#stealthchop_threshold: 0
# See the "tmc2208" section for the definition of these parameters.
#coolstep_threshold:
# The velocity (in mm/s) to set the TMC driver internal "CoolStep"
# threshold to. If set, the coolstep feature will be enabled when
# the stepper motor velocity is near or above this value. Important
# - if coolstep_threshold is set and "sensorless homing" is used,
# then one must ensure that the homing speed is above the coolstep
# threshold! The default is to not enable the coolstep feature.
#uart_address:
# The address of the TMC2209 chip for UART messages (an integer
# between 0 and 3). This is typically used when multiple TMC2209
@@ -3362,6 +3635,11 @@ run_current:
#driver_PWM_GRAD: 14
#driver_PWM_OFS: 36
#driver_SGTHRS: 0
#driver_SEMIN: 0
#driver_SEUP: 0
#driver_SEMAX: 0
#driver_SEDN: 0
#driver_SEIMIN: 0
# Set the given register during the configuration of the TMC2209
# chip. This may be used to set custom motor parameters. The
# defaults for each parameter are next to the parameter name in the
@@ -3496,6 +3774,18 @@ run_current:
# set, "stealthChop" mode will be enabled if the stepper motor
# velocity is below this value. The default is 0, which disables
# "stealthChop" mode.
#coolstep_threshold:
# The velocity (in mm/s) to set the TMC driver internal "CoolStep"
# threshold to. If set, the coolstep feature will be enabled when
# the stepper motor velocity is near or above this value. Important
# - if coolstep_threshold is set and "sensorless homing" is used,
# then one must ensure that the homing speed is above the coolstep
# threshold! The default is to not enable the coolstep feature.
#high_velocity_threshold:
# The velocity (in mm/s) to set the TMC driver internal "high
# velocity" threshold (THIGH) to. This is typically used to disable
# the "CoolStep" feature at high speeds. The default is to not set a
# TMC "high velocity" threshold.
#driver_MSLUT0: 2863314260
#driver_MSLUT1: 1251300522
#driver_MSLUT2: 608774441
@@ -3556,6 +3846,7 @@ run_current:
#driver_SEIMIN: 0
#driver_SFILT: 0
#driver_SG4_ANGLE_OFFSET: 1
#driver_SLOPE_CONTROL: 0
# Set the given register during the configuration of the TMC2240
# chip. This may be used to set custom motor parameters. The
# defaults for each parameter are next to the parameter name in the
@@ -3617,6 +3908,18 @@ run_current:
# set, "stealthChop" mode will be enabled if the stepper motor
# velocity is below this value. The default is 0, which disables
# "stealthChop" mode.
#coolstep_threshold:
# The velocity (in mm/s) to set the TMC driver internal "CoolStep"
# threshold to. If set, the coolstep feature will be enabled when
# the stepper motor velocity is near or above this value. Important
# - if coolstep_threshold is set and "sensorless homing" is used,
# then one must ensure that the homing speed is above the coolstep
# threshold! The default is to not enable the coolstep feature.
#high_velocity_threshold:
# The velocity (in mm/s) to set the TMC driver internal "high
# velocity" threshold (THIGH) to. This is typically used to disable
# the "CoolStep" feature at high speeds. The default is to not set a
# TMC "high velocity" threshold.
#driver_MSLUT0: 2863314260
#driver_MSLUT1: 1251300522
#driver_MSLUT2: 608774441
@@ -3847,15 +4150,16 @@ Support for a display attached to the micro-controller.
[display]
lcd_type:
# The type of LCD chip in use. This may be "hd44780", "hd44780_spi",
# "st7920", "emulated_st7920", "uc1701", "ssd1306", or "sh1106".
# "aip31068_spi", "st7920", "emulated_st7920", "uc1701", "ssd1306", or
# "sh1106".
# See the display sections below for information on each type and
# additional parameters they provide. This parameter must be
# provided.
#display_group:
# The name of the display_data group to show on the display. This
# controls the content of the screen (see the "display_data" section
# for more information). The default is _default_20x4 for hd44780
# displays and _default_16x4 for other displays.
# for more information). The default is _default_20x4 for hd44780 or
# aip31068_spi displays and _default_16x4 for other displays.
#menu_timeout:
# Timeout for menu. Being inactive this amount of seconds will
# trigger menu exit or return to root menu when having autorun
@@ -3981,6 +4285,31 @@ spi_software_miso_pin:
...
```
#### aip31068_spi display
Information on configuring an aip31068_spi display - a very similar to hd44780_spi
a 20x04 (20 symbols by 4 lines) display with slightly different internal
protocol.
```
[display]
lcd_type: aip31068_spi
latch_pin:
spi_software_sclk_pin:
spi_software_mosi_pin:
spi_software_miso_pin:
# The pins connected to the shift register controlling the display.
# The spi_software_miso_pin needs to be set to an unused pin of the
# printer mainboard as the shift register does not have a MISO pin,
# but the software spi implementation requires this pin to be
# configured.
#line_length:
# Set the number of characters per line for an hd44780 type lcd.
# Possible values are 20 (default) and 16. The number of lines is
# fixed to 4.
...
```
#### st7920 display
Information on configuring st7920 displays (which is used in
@@ -4400,6 +4729,9 @@ adc2:
# command.
#min_diameter: 1.0
# Minimal diameter for trigger virtual filament_switch_sensor.
#max_diameter:
# Maximum diameter for triggering virtual filament_switch_sensor.
# The default is default_nominal_filament_diameter + max_difference.
#use_current_dia_while_delay: False
# Use the current diameter instead of the nominal diameter while
# the measurement delay has not run through.
@@ -4412,6 +4744,112 @@ adc2:
# above parameters.
```
## Load Cells
### [load_cell]
Load Cell. Uses an ADC sensor attached to a load cell to create a digital
scale.
```
[load_cell]
sensor_type:
# This must be one of the supported sensor types, see below.
```
#### HX711
This is a 24 bit low sample rate chip using "bit-bang" communications. It is
suitable for filament scales.
```
[load_cell]
sensor_type: hx711
sclk_pin:
# The pin connected to the HX711 clock line. This parameter must be provided.
dout_pin:
# The pin connected to the HX711 data output line. This parameter must be
# provided.
#gain: A-128
# Valid values for gain are: A-128, A-64, B-32. The default is A-128.
# 'A' denotes the input channel and the number denotes the gain. Only the 3
# listed combinations are supported by the chip. Note that changing the gain
# setting also selects the channel being read.
#sample_rate: 80
# Valid values for sample_rate are 80 or 10. The default value is 80.
# This must match the wiring of the chip. The sample rate cannot be changed
# in software.
```
#### HX717
This is the 4x higher sample rate version of the HX711, suitable for probing.
```
[load_cell]
sensor_type: hx717
sclk_pin:
# The pin connected to the HX717 clock line. This parameter must be provided.
dout_pin:
# The pin connected to the HX717 data output line. This parameter must be
# provided.
#gain: A-128
# Valid values for gain are A-128, B-64, A-64, B-8.
# 'A' denotes the input channel and the number denotes the gain setting.
# Only the 4 listed combinations are supported by the chip. Note that
# changing the gain setting also selects the channel being read.
#sample_rate: 320
# Valid values for sample_rate are: 10, 20, 80, 320. The default is 320.
# This must match the wiring of the chip. The sample rate cannot be changed
# in software.
```
#### ADS1220
The ADS1220 is a 24 bit ADC supporting up to a 2Khz sample rate configurable in
software.
```
[load_cell]
sensor_type: ads1220
cs_pin:
# The pin connected to the ADS1220 chip select line. This parameter must
# be provided.
#spi_speed: 512000
# This chip supports 2 speeds: 256000 or 512000. The faster speed is only
# enabled when one of the Turbo sample rates is used. The correct spi_speed
# is selected based on the sample rate.
#spi_bus:
#spi_software_sclk_pin:
#spi_software_mosi_pin:
#spi_software_miso_pin:
# See the "common SPI settings" section for a description of the
# above parameters.
data_ready_pin:
# Pin connected to the ADS1220 data ready line. This parameter must be
# provided.
#gain: 128
# Valid gain values are 128, 64, 32, 16, 8, 4, 2, 1
# The default is 128
#pga_bypass: False
# Disable the internal Programmable Gain Amplifier. If
# True the PGA will be disabled for gains 1, 2, and 4. The PGA is always
# enabled for gain settings 8 to 128, regardless of the pga_bypass setting.
# If AVSS is used as an input pga_bypass is forced to True.
# The default is False.
#sample_rate: 660
# This chip supports two ranges of sample rates, Normal and Turbo. In turbo
# mode the chip's internal clock runs twice as fast and the SPI communication
# speed is also doubled.
# Normal sample rates: 20, 45, 90, 175, 330, 600, 1000
# Turbo sample rates: 40, 90, 180, 350, 660, 1200, 2000
# The default is 660
#input_mux:
# Input multiplexer configuration, select a pair of pins to use. The first pin
# is the positive, AINP, and the second pin is the negative, AINN. Valid
# values are: 'AIN0_AIN1', 'AIN0_AIN2', 'AIN0_AIN3', 'AIN1_AIN2', 'AIN1_AIN3',
# 'AIN2_AIN3', 'AIN1_AIN0', 'AIN3_AIN2', 'AIN0_AVSS', 'AIN1_AVSS', 'AIN2_AVSS'
# and 'AIN3_AVSS'. If AVSS is used the PGA is bypassed and the pga_bypass
# setting will be forced to True.
# The default is AIN0_AIN1.
#vref:
# The selected voltage reference. Valid values are: 'internal', 'REF0', 'REF1'
# and 'analog_supply'. Default is 'internal'.
```
## Board specific hardware support
### [sx1509]
@@ -4498,6 +4936,50 @@ vssa_pin:
# noise. The default is 2 seconds.
```
### [ads1x1x]
ADS1013, ADS1014, ADS1015, ADS1113, ADS1114 and ADS1115 are I2C based Analog to
Digital Converters that can be used for temperature sensors. They provide 4
analog input pins either as single line or as differential input.
Note: Use caution if using this sensor to control heaters. The heater min_temp
and max_temp are only verified in the host and only if the host is running and
operating normally. (ADC inputs directly connected to the micro-controller
verify min_temp and max_temp within the micro-controller and do not require a
working connection to the host.)
```
[ads1x1x my_ads1x1x]
chip: ADS1115
#pga: 4.096V
# Default value is 4.096V. The maximum voltage range used for the input. This
# scales all values read from the ADC. Options are: 6.144V, 4.096V, 2.048V,
# 1.024V, 0.512V, 0.256V
#adc_voltage: 3.3
# The suppy voltage for the device. This allows additional software scaling
# for all values read from the ADC.
i2c_mcu: host
i2c_bus: i2c.1
#address_pin: GND
# Default value is GND. There can be up to four addressed devices depending
# upon wiring of the device. Check the datasheet for details. The i2c_address
# can be specified directly instead of using the address_pin.
```
The chip provides pins that can be used on other sensors.
```
sensor_type: ...
# Can be any thermistor or adc_temperature.
sensor_pin: my_ads1x1x:AIN0
# A combination of the name of the ads1x1x chip and the pin. Possible
# pin values are AIN0, AIN1, AIN2 and AIN3 for single ended lines and
# DIFF01, DIFF03, DIFF13 and DIFF23 for differential between their
# correspoding lines. For example
# DIFF03 measures the differential between line 0 and 3. Only specific
# combinations for the differentials are allowed.
```
### [replicape]
Replicape support - see the [beaglebone guide](Beaglebone.md) and the
@@ -4603,8 +5085,9 @@ serial:
### [angle]
Magnetic hall angle sensor support for reading stepper motor angle
shaft measurements using a1333, as5047d, or tle5012b SPI chips. The
measurements are available via the [API Server](API_Server.md) and
shaft measurements using a1333, as5047d, mt6816, mt6826s,
or tle5012b SPI chips.
The measurements are available via the [API Server](API_Server.md) and
[motion analysis tool](Debugging.md#motion-analysis-and-data-logging).
See the [G-Code reference](G-Codes.md#angle) for available commands.
@@ -4612,7 +5095,7 @@ See the [G-Code reference](G-Codes.md#angle) for available commands.
[angle my_angle_sensor]
sensor_type:
# The type of the magnetic hall sensor chip. Available choices are
# "a1333", "as5047d", and "tle5012b". This parameter must be
# "a1333", "as5047d", "mt6816", "mt6826s", and "tle5012b". This parameter must be
# specified.
#sample_period: 0.000400
# The query period (in seconds) to use during measurements. The
@@ -4675,8 +5158,9 @@ Most Klipper micro-controller implementations only support an
micro-controller supports a 400000 speed (*fast mode*, 400kbit/s), but it must be
[set in the operating system](RPi_microcontroller.md#optional-enabling-i2c)
and the `i2c_speed` parameter is otherwise ignored. The Klipper
"RP2040" micro-controller and ATmega AVR family support a rate of 400000
via the `i2c_speed` parameter. All other Klipper micro-controllers use a
"RP2040" micro-controller and ATmega AVR family and some STM32
(F0, G0, G4, L4, F7, H7) support a rate of 400000 via the `i2c_speed` parameter.
All other Klipper micro-controllers use a
100000 rate and ignore the `i2c_speed` parameter.
```

View File

@@ -132,3 +132,10 @@ There are several
you have questions on the code then you can also ask in the
[Klipper Discourse Forum](#discourse-forum) or on the
[Klipper Discord Chat](#discord-chat).
## Professional Services
![](img/klipper-logo-small.png)
Custom software development, software support, and solutions:
[https://ko-fi.com/koconnor](https://ko-fi.com/koconnor)

146
docs/Eddy_Probe.md Normal file
View File

@@ -0,0 +1,146 @@
# Eddy Current Inductive probe
This document describes how to use an
[eddy current](https://en.wikipedia.org/wiki/Eddy_current) inductive
probe in Klipper.
Currently, an eddy current probe can not be used for Z homing. The
sensor can only be used for Z probing.
Start by declaring a
[probe_eddy_current config section](Config_Reference.md#probe_eddy_current)
in the printer.cfg file. It is recommended to set the `z_offset` to
0.5mm. It is typical for the sensor to require an `x_offset` and
`y_offset`. If these values are not known, one should estimate the
values during initial calibration.
The first step in calibration is to determine the appropriate
DRIVE_CURRENT for the sensor. Home the printer and navigate the
toolhead so that the sensor is near the center of the bed and is about
20mm above the bed. Then issue an `LDC_CALIBRATE_DRIVE_CURRENT
CHIP=<config_name>` command. For example, if the config section was
named `[probe_eddy_current my_eddy_probe]` then one would run
`LDC_CALIBRATE_DRIVE_CURRENT CHIP=my_eddy_probe`. This command should
complete in a few seconds. After it completes, issue a `SAVE_CONFIG`
command to save the results to the printer.cfg and restart.
The second step in calibration is to correlate the sensor readings to
the corresponding Z heights. Home the printer and navigate the
toolhead so that the nozzle is near the center of the bed. Then run an
`PROBE_EDDY_CURRENT_CALIBRATE CHIP=my_eddy_probe` command. Once the
tool starts, follow the steps described at
["the paper test"](Bed_Level.md#the-paper-test) to determine the
actual distance between the nozzle and bed at the given location. Once
those steps are complete one can `ACCEPT` the position. The tool will
then move the the toolhead so that the sensor is above the point where
the nozzle used to be and run a series of movements to correlate the
sensor to Z positions. This will take a couple of minutes. After the
tool completes, issue a `SAVE_CONFIG` command to save the results to
the printer.cfg and restart.
After initial calibration it is a good idea to verify that the
`x_offset` and `y_offset` are accurate. Follow the steps to
[calibrate probe x and y offsets](Probe_Calibrate.md#calibrating-probe-x-and-y-offsets).
If either the `x_offset` or `y_offset` is modified then be sure to run
the `PROBE_EDDY_CURRENT_CALIBRATE` command (as described above) after
making the change.
Once calibration is complete, one may use all the standard Klipper
tools that use a Z probe.
Note that eddy current sensors (and inductive probes in general) are
susceptible to "thermal drift". That is, changes in temperature can
result in changes in reported Z height. Changes in either the bed
surface temperature or sensor hardware temperature can skew the
results. It is important that calibration and probing is only done
when the printer is at a stable temperature.
## Thermal Drift Calibration
As with all inductive probes, eddy current probes are subject to
significant thermal drift. If the eddy probe has a temperature
sensor on the coil it is possible to configure a `[temperature_probe]`
to report coil temperature and enable software drift compensation. To
link a temperature probe to an eddy current probe the
`[temperature_probe]` section must share a name with the
`[probe_eddy_current]` section. For example:
```
[probe_eddy_current my_probe]
# eddy probe configuration...
[temperature_probe my_probe]
# temperature probe configuration...
```
See the [configuration reference](Config_Reference.md#temperature_probe)
for further details on how to configure a `temperature_probe`. It is
advised to configure the `calibration_position`,
`calibration_extruder_temp`, `extruder_heating_z`, and
`calibration_bed_temp` options, as doing so will automate some of the
steps outlined below. If the printer to be calibrated is enclosed, it
is strongly recommended to set the `max_validation_temp` option to a value
between 100 and 120.
Eddy probe manufacturers may offer a stock drift calibration that can be
manually added to `drift_calibration` option of the `[probe_eddy_current]`
section. If they do not, or if the stock calibration does not perform well on
your system, the `temperature_probe` module offers a manual calibration
procedure via the `TEMPERATURE_PROBE_CALIBRATE` gcode command.
Prior to performing calibration the user should have an idea of what the
maximum attainable temperature probe coil temperature is. This temperature
should be used to set the `TARGET` parameter of the
`TEMPERATURE_PROBE_CALIBRATE` command. The goal is to calibrate across the
widest temperature range possible, thus its desirable to start with the printer
cold and finish with the coil at the maximum temperature it can reach.
Once a `[temperature_probe]` is configured, the following steps may be taken
to perform thermal drift calibration:
- The probe must be calibrated using `PROBE_EDDY_CURRENT_CALIBRATE`
when a `[temperature_probe]` is configured and linked. This captures
the temperature during calibration which is necessary to perform
thermal drift compensation.
- Make sure the nozzle is free of debris and filament.
- The bed, nozzle, and probe coil should be cold prior to calibration.
- The following steps are required if the `calibration_position`,
`calibration_extruder_temp`, and `extruder_heating_z` options in
`[temperature_probe]` are **NOT** configured:
- Move the tool to the center of the bed. Z should be 30mm+ above the bed.
- Heat the extruder to a temperature above the maximum safe bed temperature.
150-170C should be sufficient for most configurations. The purpose of
heating the extruder is to avoid nozzle expansion during calibration.
- When the extruder temperature has settled, move the Z axis down to about 1mm
above the bed.
- Start drift calibration. If the probe's name is `my_probe` and the maximum
probe temperature we can achieve is 80C, the appropriate gcode command is
`TEMPERATURE_PROBE_CALIBRATE PROBE=my_probe TARGET=80`. If configured, the
tool will move to the X,Y coordinate specified by the `calibration_position`
and the Z value specified by `extruder_heating_z`. After heating the extruder
to the specified temperature the tool will move to the Z value specified
by the`calibration_position`.
- The procedure will request a manual probe. Perform the manual probe with
the paper test and `ACCEPT`. The calibration procedure will take the first
set of samples with the probe then park the probe in the heating position.
- If the `calibration_bed_temp` is **NOT** configured turn on the bed heat
to the maximum safe temperature. Otherwise this step will be performed
automatically.
- By default the calibration procedure will request a manual probe every
2C between samples until the `TARGET` is reached. The temperature delta
between samples can be customized by setting the `STEP` parameter in
`TEMPERATURE_PROBE_CALIBRATE`. Care should be taken when setting a custom
`STEP` value, a value too high may request too few samples resulting in
a poor calibration.
- The following additional gcode commands are available during drift
calibration:
- `TEMPERATURE_PROBE_NEXT` may be used to force a new sample before the step
delta has been reached.
- `TEMPERATURE_PROBE_COMPLETE` may be used to complete calibration before the
`TARGET` has been reached.
- `ABORT` may be used to end calibration and discard results.
- When calibration is finished use `SAVE_CONFIG` to store the drift
calibration.
As one may conclude, the calibration process outlined above is more challenging
and time consuming than most other procedures. It may require practice and several attempts to achieve an optimal calibration.

View File

@@ -190,6 +190,8 @@ represent total number of steps per second on the micro-controller.
| AR100 | 3529K | 2507K |
| STM32F407 | 3652K | 2459K |
| STM32F446 | 3913K | 2634K |
| RP2350 | 4167K | 2663K |
| SAME70 | 6667K | 4737K |
| STM32H743 | 9091K | 6061K |
If unsure of the micro-controller on a particular board, find the

View File

@@ -127,6 +127,14 @@ use this tool the Python "numpy" package must be installed (see the
[measuring resonance document](Measuring_Resonances.md#software-installation)
for more information).
#### ANGLE_CHIP_CALIBRATE
`ANGLE_CHIP_CALIBRATE CHIP=<chip_name>`: Perform internal sensor calibration,
if implemented (MT6826S/MT6835).
- **MT68XX**: The motor should be disconnected
from any printer carriage before performing calibration.
After calibration, the sensor should be reset by disconnecting the power.
#### ANGLE_DEBUG_READ
`ANGLE_DEBUG_READ CHIP=<config_name> REG=<register>`: Queries sensor
register "register" (e.g. 44 or 0x2C). Can be useful for debugging
@@ -139,6 +147,27 @@ Writes raw "value" into register "register". Both "value" and
and refer to sensor data sheet for the reference. This is only
available for tle5012b chips.
### [axis_twist_compensation]
The following commands are available when the
[axis_twist_compensation config
section](Config_Reference.md#axis_twist_compensation) is enabled.
#### AXIS_TWIST_COMPENSATION_CALIBRATE
`AXIS_TWIST_COMPENSATION_CALIBRATE [AXIS=<X|Y>] [AUTO=<True|False>]
[SAMPLE_COUNT=<value>]`
Calibrates axis twist compensation by specifying the target axis or
enabling automatic calibration.
- **AXIS:** Define the axis (`X` or `Y`) for which the twist compensation
will be calibrated. If not specified, the axis defaults to `'X'`.
- **AUTO:** Enables automatic calibration mode. When `AUTO=True`, the
calibration will run for both the X and Y axes. In this mode, `AXIS`
cannot be specified. If both `AXIS` and `AUTO` are provided, an error
will be raised.
### [bed_mesh]
The following commands are available when the
@@ -146,15 +175,21 @@ The following commands are available when the
(also see the [bed mesh guide](Bed_Mesh.md)).
#### BED_MESH_CALIBRATE
`BED_MESH_CALIBRATE [METHOD=manual] [HORIZONTAL_MOVE_Z=<value>]
[<probe_parameter>=<value>] [<mesh_parameter>=<value>]`: This command probes
the bed using generated points specified by the parameters in the config. After
probing, a mesh is generated and z-movement is adjusted according to the mesh.
`BED_MESH_CALIBRATE [PROFILE=<name>] [METHOD=manual] [HORIZONTAL_MOVE_Z=<value>]
[<probe_parameter>=<value>] [<mesh_parameter>=<value>] [ADAPTIVE=1]
[ADAPTIVE_MARGIN=<value>]`: This command probes the bed using generated points
specified by the parameters in the config. After probing, a mesh is generated
and z-movement is adjusted according to the mesh.
The mesh will be saved into a profile specified by the `PROFILE` parameter,
or `default` if unspecified.
See the PROBE command for details on the optional probe parameters. If
METHOD=manual is specified then the manual probing tool is activated - see the
MANUAL_PROBE command above for details on the additional commands available
while this tool is active. The optional `HORIZONTAL_MOVE_Z` value overrides the
`horizontal_move_z` option specified in the config file.
`horizontal_move_z` option specified in the config file. If ADAPTIVE=1 is
specified then the objects defined by the Gcode file being printed will be used
to define the probed area. The optional `ADAPTIVE_MARGIN` value overrides the
`adaptive_margin` option specified in the config file.
#### BED_MESH_OUTPUT
`BED_MESH_OUTPUT PGP=[<0:1>]`: This command outputs the current probed
@@ -184,10 +219,12 @@ SAVE_CONFIG gcode must be run to make the changes to persistent memory
permanent.
#### BED_MESH_OFFSET
`BED_MESH_OFFSET [X=<value>] [Y=<value>]`: Applies X and/or Y offsets
to the mesh lookup. This is useful for printers with independent
extruders, as an offset is necessary to produce correct Z adjustment
after a tool change.
`BED_MESH_OFFSET [X=<value>] [Y=<value>] [ZFADE=<value]`: Applies X, Y,
and/or ZFADE offsets to the mesh lookup. This is useful for printers with
independent extruders, as an offset is necessary to produce correct Z
adjustment after a tool change. Note that a ZFADE offset does not apply
additional z-adjustment directly, it is used to correct the `fade`
calculation when a `gcode offset` has been applied to the Z axis.
### [bed_screws]
@@ -447,12 +484,6 @@ MOTION_QUEUE (as defined in an [extruder](Config_Reference.md#extruder)
config section). If MOTION_QUEUE is an empty string then the stepper
will be desynchronized from all extruder movement.
#### SET_EXTRUDER_STEP_DISTANCE
This command is deprecated and will be removed in the near future.
#### SYNC_STEPPER_TO_EXTRUDER
This command is deprecated and will be removed in the near future.
### [fan_generic]
The following command is available when a
@@ -463,6 +494,20 @@ enabled.
`SET_FAN_SPEED FAN=config_name SPEED=<speed>` This command sets the
speed of a fan. "speed" must be between 0.0 and 1.0.
`SET_FAN_SPEED PIN=config_name TEMPLATE=<template_name>
[<param_x>=<literal>]`: If `TEMPLATE` is specified then it assigns a
[display_template](Config_Reference.md#display_template) to the given
fan. For example, if one defined a `[display_template
my_fan_template]` config section then one could assign
`TEMPLATE=my_fan_template` here. The display_template should produce a
string containing a floating point number with the desired value. The
template will be continuously evaluated and the fan will be
automatically set to the resulting speed. One may set display_template
parameters to use during template evaluation (parameters will be
parsed as Python literals). If TEMPLATE is an empty string then this
command will clear any previous template assigned to the pin (one can
then use `SET_FAN_SPEED` commands to manage the values directly).
### [filament_switch_sensor]
The following command is available when a
@@ -540,15 +585,18 @@ state; issue a G28 afterwards to reset the kinematics. This command is
intended for low-level diagnostics and debugging.
#### SET_KINEMATIC_POSITION
`SET_KINEMATIC_POSITION [X=<value>] [Y=<value>] [Z=<value>]`: Force
the low-level kinematic code to believe the toolhead is at the given
cartesian position. This is a diagnostic and debugging command; use
SET_GCODE_OFFSET and/or G92 for regular axis transformations. If an
axis is not specified then it will default to the position that the
head was last commanded to. Setting an incorrect or invalid position
may lead to internal software errors. This command may invalidate
future boundary checks; issue a G28 afterwards to reset the
kinematics.
`SET_KINEMATIC_POSITION [X=<value>] [Y=<value>] [Z=<value>]
[CLEAR=<[X][Y][Z]>]`: Force the low-level kinematic code to believe the
toolhead is at the given cartesian position. This is a diagnostic and
debugging command; use SET_GCODE_OFFSET and/or G92 for regular axis
transformations. If an axis is not specified then it will default to the
position that the head was last commanded to. Setting an incorrect or
invalid position may lead to internal software errors. Use the CLEAR
parameter to forget the homing state for the given axes. Note that CLEAR
will not override the previous functionality; if an axis is not specified
to CLEAR it will have its kinematic position set as per above. This
command may invalidate future boundary checks; issue a G28 afterwards to
reset the kinematics.
### [gcode]
@@ -834,21 +882,29 @@ commands to manage the LED's color settings).
### [output_pin]
The following command is available when an
[output_pin config section](Config_Reference.md#output_pin) is
[output_pin config section](Config_Reference.md#output_pin) or
[pwm_tool config section](Config_Reference.md#pwm_tool) is
enabled.
#### SET_PIN
`SET_PIN PIN=config_name VALUE=<value> [CYCLE_TIME=<cycle_time>]`: Set
the pin to the given output `VALUE`. VALUE should be 0 or 1 for
"digital" output pins. For PWM pins, set to a value between 0.0 and
1.0, or between 0.0 and `scale` if a scale is configured in the
output_pin config section.
`SET_PIN PIN=config_name VALUE=<value>`: Set the pin to the given
output `VALUE`. VALUE should be 0 or 1 for "digital" output pins. For
PWM pins, set to a value between 0.0 and 1.0, or between 0.0 and
`scale` if a scale is configured in the output_pin config section.
Some pins (currently only "soft PWM" pins) support setting an explicit
cycle time using the CYCLE_TIME parameter (specified in seconds). Note
that the CYCLE_TIME parameter is not stored between SET_PIN commands
(any SET_PIN command without an explicit CYCLE_TIME parameter will use
the `cycle_time` specified in the output_pin config section).
`SET_PIN PIN=config_name TEMPLATE=<template_name> [<param_x>=<literal>]`:
If `TEMPLATE` is specified then it assigns a
[display_template](Config_Reference.md#display_template) to the given
pin. For example, if one defined a `[display_template
my_pin_template]` config section then one could assign
`TEMPLATE=my_pin_template` here. The display_template should produce a
string containing a floating point number with the desired value. The
template will be continuously evaluated and the pin will be
automatically set to the resulting value. One may set display_template
parameters to use during template evaluation (parameters will be
parsed as Python literals). If TEMPLATE is an empty string then this
command will clear any previous template assigned to the pin (one can
then use `SET_PIN` commands to manage the values directly).
### [palette2]
@@ -977,6 +1033,58 @@ babystepping), and subtract if from the probe's z_offset. This acts
to take a frequently used babystepping value, and "make it permanent".
Requires a `SAVE_CONFIG` to take effect.
### [probe_eddy_current]
The following commands are available when a
[probe_eddy_current config section](Config_Reference.md#probe_eddy_current)
is enabled.
#### PROBE_EDDY_CURRENT_CALIBRATE
`PROBE_EDDY_CURRENT_CALIBRATE CHIP=<config_name>`: This starts a tool
that calibrates the sensor resonance frequencies to corresponding Z
heights. The tool will take a couple of minutes to complete. After
completion, use the SAVE_CONFIG command to store the results in the
printer.cfg file.
#### LDC_CALIBRATE_DRIVE_CURRENT
`LDC_CALIBRATE_DRIVE_CURRENT CHIP=<config_name>` This tool will
calibrate the ldc1612 DRIVE_CURRENT0 register. Prior to using this
tool, move the sensor so that it is near the center of the bed and
about 20mm above the bed surface. Run this command to determine an
appropriate DRIVE_CURRENT for the sensor. After running this command
use the SAVE_CONFIG command to store that new setting in the
printer.cfg config file.
### [pwm_cycle_time]
The following command is available when a
[pwm_cycle_time config section](Config_Reference.md#pwm_cycle_time)
is enabled.
#### SET_PIN
`SET_PIN PIN=config_name VALUE=<value> [CYCLE_TIME=<cycle_time>]`:
This command works similarly to [output_pin](#output_pin) SET_PIN
commands. The command here supports setting an explicit cycle time
using the CYCLE_TIME parameter (specified in seconds). Note that the
CYCLE_TIME parameter is not stored between SET_PIN commands (any
SET_PIN command without an explicit CYCLE_TIME parameter will use the
`cycle_time` specified in the pwm_cycle_time config section).
### [quad_gantry_level]
The following commands are available when the
[quad_gantry_level config section](Config_Reference.md#quad_gantry_level)
is enabled.
#### QUAD_GANTRY_LEVEL
`QUAD_GANTRY_LEVEL [RETRIES=<value>] [RETRY_TOLERANCE=<value>]
[HORIZONTAL_MOVE_Z=<value>] [<probe_parameter>=<value>]`: This command
will probe the points specified in the config and then make
independent adjustments to each Z stepper to compensate for tilt. See
the PROBE command for details on the optional probe parameters. The
optional `RETRIES`, `RETRY_TOLERANCE`, and `HORIZONTAL_MOVE_Z` values
override those options specified in the config file.
### [query_adc]
The query_adc module is automatically loaded.
@@ -1012,20 +1120,19 @@ is enabled (also see the
all enabled accelerometer chips.
#### 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>]
[HZ_PER_SEC=<hz_per_sec>] [CHIPS=<adxl345_chip_name>]
[POINT=x,y,z] [INPUT_SHAPING=[<0:1>]]`: Runs the resonance
[ACCEL_PER_HZ=<accel_per_hz>] [HZ_PER_SEC=<hz_per_sec>] [CHIPS=<chip_name>]
[POINT=x,y,z] [INPUT_SHAPING=<0:1>]`: Runs the resonance
test in all configured probe points for the requested "axis" and
measures the acceleration using the accelerometer chips configured for
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
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`
and `AXIS=-dx,-dy` is equivalent. `adxl345_chip_name` can be one or
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)
and `AXIS=-dx,-dy` is equivalent. `chip_name` can be one or
more configured accel chips, delimited with comma, for example
`CHIPS="adxl345, adxl345 rpi"`. 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
@@ -1042,8 +1149,9 @@ frequency response is calculated (across all probe points) and written into
#### SHAPER_CALIBRATE
`SHAPER_CALIBRATE [AXIS=<axis>] [NAME=<name>] [FREQ_START=<min_freq>]
[FREQ_END=<max_freq>] [HZ_PER_SEC=<hz_per_sec>] [CHIPS=<adxl345_chip_name>]
[MAX_SMOOTHING=<max_smoothing>]`: Similarly to `TEST_RESONANCES`, runs
[FREQ_END=<max_freq>] [ACCEL_PER_HZ=<accel_per_hz>][HZ_PER_SEC=<hz_per_sec>]
[CHIPS=<chip_name>] [MAX_SMOOTHING=<max_smoothing>] [INPUT_SHAPING=<0:1>]`:
Similarly to `TEST_RESONANCES`, runs
the resonance test as configured, and tries to find the optimal
parameters for the input shaper for the requested axis (or both X and
Y axes if `AXIS` parameter is unset). If `MAX_SMOOTHING` is unset, its
@@ -1280,8 +1388,11 @@ The toolhead module is automatically loaded.
#### SET_VELOCITY_LIMIT
`SET_VELOCITY_LIMIT [VELOCITY=<value>] [ACCEL=<value>]
[ACCEL_TO_DECEL=<value>] [SQUARE_CORNER_VELOCITY=<value>]`: Modify the
printer's velocity limits.
[MINIMUM_CRUISE_RATIO=<value>] [SQUARE_CORNER_VELOCITY=<value>]`: This
command can alter the velocity limits that were specified in the
printer config file. See the
[printer config section](Config_Reference.md#printer) for a
description of each parameter.
### [tuning_tower]
@@ -1339,17 +1450,6 @@ print.
#### SDCARD_RESET_FILE
`SDCARD_RESET_FILE`: Unload file and clear SD state.
### [axis_twist_compensation]
The following commands are available when the
[axis_twist_compensation config
section](Config_Reference.md#axis_twist_compensation) is enabled.
#### AXIS_TWIST_COMPENSATION_CALIBRATE
`AXIS_TWIST_COMPENSATION_CALIBRATE [SAMPLE_COUNT=<value>]`: Initiates the X
twist calibration wizard. `SAMPLE_COUNT` specifies the number of points along
the X axis to calibrate at and defaults to 3.
### [z_thermal_adjust]
The following commands are available when the
@@ -1374,8 +1474,46 @@ The following commands are available when the
[z_tilt config section](Config_Reference.md#z_tilt) is enabled.
#### Z_TILT_ADJUST
`Z_TILT_ADJUST [HORIZONTAL_MOVE_Z=<value>] [<probe_parameter>=<value>]`: This
command will probe the points specified in the config and then make independent
adjustments to each Z stepper to compensate for tilt. See the PROBE command for
details on the optional probe parameters. The optional `HORIZONTAL_MOVE_Z`
value overrides the `horizontal_move_z` option specified in the config file.
`Z_TILT_ADJUST [RETRIES=<value>] [RETRY_TOLERANCE=<value>]
[HORIZONTAL_MOVE_Z=<value>] [<probe_parameter>=<value>]`: This command
will probe the points specified in the config and then make
independent adjustments to each Z stepper to compensate for tilt. See
the PROBE command for details on the optional probe parameters. The
optional `RETRIES`, `RETRY_TOLERANCE`, and `HORIZONTAL_MOVE_Z` values
override those options specified in the config file.
### [temperature_probe]
The following commands are available when a
[temperature_probe config section](Config_Reference.md#temperature_probe)
is enabled.
#### TEMPERATURE_PROBE_CALIBRATE
`TEMPERATURE_PROBE_CALIBRATE [PROBE=<probe name>] [TARGET=<value>] [STEP=<value>]`:
Initiates probe drift calibration for eddy current based probes. The `TARGET`
is a target temperature for the last sample. When the temperature recorded
during a sample exceeds the `TARGET` calibration will complete. The `STEP`
parameter sets temperature delta (in C) between samples. After a sample has
been taken, this delta is used to schedule a call to `TEMPERATURE_PROBE_NEXT`.
The default `STEP` is 2.
#### TEMPERATURE_PROBE_NEXT
`TEMPERATURE_PROBE_NEXT`: After calibration has started this command is run to
take the next sample. It is automatically scheduled to run when the delta
specified by `STEP` has been reached, however its also possible to manually run
this command to force a new sample. This command is only available during
calibration.
#### TEMPERATURE_PROBE_COMPLETE:
`TEMPERATURE_PROBE_COMPLETE`: Can be used to end calibration and save the
current result before the `TARGET` temperature is reached. This command
is only available during calibration.
#### ABORT
`ABORT`: Aborts the calibration process, discarding the current results.
This command is only available during drift calibration.
### TEMPERATURE_PROBE_ENABLE
`TEMPERATURE_PROBE_ENABLE ENABLE=[0|1]`: Sets temperature drift
compensation on or off. If ENABLE is set to 0, drift compensation
will be disabled, if set to 1 it is enabled.

View File

@@ -1,15 +1,20 @@
# Installation
These instructions assume the software will run on a Raspberry Pi
computer in conjunction with OctoPrint. It is recommended that a
Raspberry Pi 2, 3, or 4 computer be used as the host machine (see the
These instructions assume the software will run on a linux based host
running a Klipper compatible front end. It is recommended that a
SBC(Small Board Computer) such as a Raspberry Pi or Debian based Linux
device be used as the host machine (see the
[FAQ](FAQ.md#can-i-run-klipper-on-something-other-than-a-raspberry-pi-3)
for other machines).
for other options).
For the purposes of these instructions host relates to the Linux device and
mcu relates to the printboard. SBC relates to the term Small Board Computer
such as the Raspberry Pi.
## Obtain a Klipper Configuration File
Most Klipper settings are determined by a "printer configuration file"
that will be stored on the Raspberry Pi. An appropriate configuration
printer.cfg, that will be stored on the host. An appropriate configuration
file can often be found by looking in the Klipper
[config directory](../config/) for a file starting with a "printer-"
prefix that corresponds to the target printer. The Klipper
@@ -35,38 +40,51 @@ printer configuration file, then start with the closest example
[config file](../config/) and use the Klipper
[config reference](Config_Reference.md) for further information.
## Prepping an OS image
## Interacting with Klipper
Start by installing [OctoPi](https://github.com/guysoft/OctoPi) on the
Raspberry Pi computer. Use OctoPi v0.17.0 or later - see the
[OctoPi releases](https://github.com/guysoft/OctoPi/releases) for
release information. One should verify that OctoPi boots and that the
OctoPrint web server works. After connecting to the OctoPrint web
page, follow the prompt to upgrade OctoPrint to v1.4.2 or later.
Klipper is a 3d printer firmware, so it needs some way for the user to
interact with it.
After installing OctoPi and upgrading OctoPrint, it will be necessary
to ssh into the target machine to run a handful of system commands. If
using a Linux or MacOS desktop, then the "ssh" software should already
be installed on the desktop. There are free ssh clients available for
other desktops (eg,
[PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/)). Use the
ssh utility to connect to the Raspberry Pi (ssh pi@octopi -- password
is "raspberry") and run the following commands:
Currently the best choices are front ends that retrieve information through
the [Moonraker web API](https://moonraker.readthedocs.io/) and there is also
the option to use [Octoprint](https://octoprint.org/) to control Klipper.
```
git clone https://github.com/Klipper3d/klipper
./klipper/scripts/install-octopi.sh
```
The choice is up to the user on what to use, but the underlying Klipper is the
same in all cases. We encourage users to research the options available and
make an informed decision.
The above will download Klipper, install some system dependencies,
setup Klipper to run at system startup, and start the Klipper host
software. It will require an internet connection and it may take a few
minutes to complete.
## Obtaining an OS image for SBC's
There are many ways to obtain an OS image for Klipper for SBC use, most depend on
what front end you wish to use. Some manafactures of these SBC boards also provide
their own Klipper-centric images.
The two main Moonraker based front ends are [Fluidd](https://docs.fluidd.xyz/)
and [Mainsail](https://docs.mainsail.xyz/), the latter of which has a premade install
image ["MainsailOS"](http://docs.mainsailOS.xyz), this has the option for Raspberry Pi
and some OrangePi varianta.
Fluidd can be installed via KIAUH(Klipper Install And Update Helper), which
is explained below and is a 3rd party installer for all things Klipper.
OctoPrint can be installed via the popular OctoPi image or via KIAUH, this
process is explained in [OctoPrint.md](OctoPrint.md)
## Installing via KIAUH
Normally you would start with a base image for your SBC, RPiOS Lite for example,
or in the case of a x86 Linux device, Ubuntu Server. Please note that Desktop
variants are not recommended due to certain helper programs that can stop some
Klipper functions working and even mask access to some print boards.
KIAUH can be used to install Klipper and its associated programs on a variety
of Linux based systems that run a form of Debian. More information can be found
at https://github.com/dw-0/kiauh
## Building and flashing the micro-controller
To compile the micro-controller code, start by running these commands
on the Raspberry Pi:
on your host device:
```
cd ~/klipper/
@@ -108,10 +126,21 @@ It should report something similar to the following:
It's common for each printer to have its own unique serial port name.
This unique name will be used when flashing the micro-controller. It's
possible there may be multiple lines in the above output - if so,
choose the line corresponding to the micro-controller (see the
choose the line corresponding to the micro-controller. If many
items are listed and the choice is ambiguous, unplug the board and
run the command again, the missing item will be your print board(see the
[FAQ](FAQ.md#wheres-my-serial-port) for more information).
For common micro-controllers, the code can be flashed with something
For common micro-controllers with STM32 or clone chips, LPC chips and
others it is usual that these need an initial Klipper flash via SD card.
When flashing with this method, it is important to make sure that the
print board is not connected with USB to the host, due to some boards
being able to feed power back to the board and stopping a flash from
occuring.
For common micro-controllers using Atmega chips, for example the 2560,
the code can be flashed with something
similar to:
```
@@ -123,53 +152,38 @@ sudo service klipper start
Be sure to update the FLASH_DEVICE with the printer's unique serial
port name.
When flashing for the first time, make sure that OctoPrint is not
connected directly to the printer (from the OctoPrint web page, under
the "Connection" section, click "Disconnect").
For common micro-controllers using RP2040 chips, the code can be flashed
with something similar to:
## Configuring OctoPrint to use Klipper
```
sudo service klipper stop
make flash FLASH_DEVICE=first
sudo service klipper start
```
The OctoPrint web server needs to be configured to communicate with
the Klipper host software. Using a web browser, login to the OctoPrint
web page and then configure the following items:
It is important to note that RP2040 chips may need to be put into Boot mode
before this operation.
Navigate to the Settings tab (the wrench icon at the top of the
page). Under "Serial Connection" in "Additional serial ports" add
"/tmp/printer". Then click "Save".
Enter the Settings tab again and under "Serial Connection" change the
"Serial Port" setting to "/tmp/printer".
In the Settings tab, navigate to the "Behavior" sub-tab and select the
"Cancel any ongoing prints but stay connected to the printer"
option. Click "Save".
From the main page, under the "Connection" section (at the top left of
the page) make sure the "Serial Port" is set to "/tmp/printer" and
click "Connect". (If "/tmp/printer" is not an available selection then
try reloading the page.)
Once connected, navigate to the "Terminal" tab and type "status"
(without the quotes) into the command entry box and click "Send". The
terminal window will likely report there is an error opening the
config file - that means OctoPrint is successfully communicating with
Klipper. Proceed to the next section.
## Configuring Klipper
The next step is to copy the
[printer configuration file](#obtain-a-klipper-configuration-file) to
the Raspberry Pi.
the host.
Arguably the easiest way to set the Klipper configuration file is to
use a desktop editor that supports editing files over the "scp" and/or
"sftp" protocols. There are freely available tools that support this
(eg, Notepad++, WinSCP, and Cyberduck). Load the printer config file
in the editor and then save it as a file named "printer.cfg" in the
home directory of the pi user (ie, /home/pi/printer.cfg).
Arguably the easiest way to set the Klipper configuration file is using the
built in editors in Mainsail or Fluidd. These will allow the user to open
the configuration examples and save them to be printer.cfg.
Another option is to use a desktop editor that supports editing files
over the "scp" and/or "sftp" protocols. There are freely available tools
that support this (eg, Notepad++, WinSCP, and Cyberduck).
Load the printer config file in the editor and then save it as a file
named "printer.cfg" in the home directory of the pi user
(ie, /home/pi/printer.cfg).
Alternatively, one can also copy and edit the file directly on the
Raspberry Pi via ssh. That may look something like the following (be
host via ssh. That may look something like the following (be
sure to update the command to use the appropriate printer config
filename):
@@ -201,7 +215,7 @@ serial: /dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0
```
After creating and editing the file it will be necessary to issue a
"restart" command in the OctoPrint web terminal to load the config. A
"restart" command in the command console to load the config. A
"status" command will report the printer is ready if the Klipper
config file is successfully read and the micro-controller is
successfully found and configured.
@@ -211,10 +225,10 @@ Klipper to report a configuration error. If an error occurs, make any
necessary corrections to the printer config file and issue "restart"
until "status" reports the printer is ready.
Klipper reports error messages via the OctoPrint terminal tab. The
"status" command can be used to re-report error messages. The default
Klipper startup script also places a log in **/tmp/klippy.log** which
provides more detailed information.
Klipper reports error messages via the command console and via pop up in
Fluidd and Mainsail. The "status" command can be used to re-report error
messages. A log is available and usually located in ~/printer_data/logs
this is named klippy.log
After Klipper reports that the printer is ready, proceed to the
[config check document](Config_checks.md) to perform some basic checks

View File

@@ -96,7 +96,7 @@ Key formula for look-ahead:
end_velocity^2 = start_velocity^2 + 2*accel*move_distance
```
### Smoothed look-ahead
### Minimum cruise ratio
Klipper also implements a mechanism for smoothing out the motions of
short "zigzag" moves. Consider the following moves:
@@ -105,21 +105,27 @@ short "zigzag" moves. Consider the following moves:
In the above, the frequent changes from acceleration to deceleration
can cause the machine to vibrate which causes stress on the machine
and increases the noise. To reduce this, Klipper tracks both regular
move acceleration as well as a virtual "acceleration to deceleration"
rate. Using this system, the top speed of these short "zigzag" moves
are limited to smooth out the printer motion:
and increases the noise. Klipper implements a mechanism to ensure
there is always some movement at a cruising speed between acceleration
and deceleration. This is done by reducing the top speed of some moves
(or sequence of moves) to ensure there is a minimum distance traveled
at cruising speed relative to the distance traveled during
acceleration and deceleration.
Klipper implements this feature by tracking both a regular move
acceleration as well as a virtual "acceleration to deceleration" rate:
![smoothed](img/smoothed.svg.png)
Specifically, the code calculates what the velocity of each move would
be if it were limited to this virtual "acceleration to deceleration"
rate (half the normal acceleration rate by default). In the above
picture the dashed gray lines represent this virtual acceleration rate
for the first move. If a move can not reach its full cruising speed
using this virtual acceleration rate then its top speed is reduced to
the maximum speed it could obtain at this virtual acceleration
rate. For most moves the limit will be at or above the move's existing
rate. In the above picture the dashed gray lines represent this
virtual acceleration rate for the first move. If a move can not reach
its full cruising speed using this virtual acceleration rate then its
top speed is reduced to the maximum speed it could obtain at this
virtual acceleration rate.
For most moves the limit will be at or above the move's existing
limits and no change in behavior is induced. For short zigzag moves,
however, this limit reduces the top speed. Note that it does not
change the actual acceleration within the move - the move continues to

View File

@@ -1,24 +1,26 @@
# Measuring Resonances
Klipper has built-in support for the ADXL345, MPU-9250 and LIS2DW compatible
Klipper has built-in support for the ADXL345, MPU-9250, LIS2DW and LIS3DH compatible
accelerometers which can be used to measure resonance frequencies of the printer
for different axes, and auto-tune [input shapers](Resonance_Compensation.md) to
compensate for resonances. Note that using accelerometers requires some
soldering and crimping. The ADXL345/LIS2DW can be connected to the SPI interface
soldering and crimping. The ADXL345 can be connected to the SPI interface
of a Raspberry Pi or MCU board (it needs to be reasonably fast). The MPU family can
be connected to the I2C interface of a Raspberry Pi directly, or to an I2C
interface of an MCU board that supports 400kbit/s *fast mode* in Klipper.
interface of an MCU board that supports 400kbit/s *fast mode* in Klipper. The
LIS2DW and LIS3DH can be connected to either SPI or I2C with the same considerations
as above.
When sourcing accelerometers, be aware that there are a variety of different PCB
board designs and different clones of them. If it is going to be connected to a
5V printer MCU ensure it has a voltage regulator and level shifters.
For ADXL345s/LIS2DWs, make sure that the board supports SPI mode (a small number of
For ADXL345s, make sure that the board supports SPI mode (a small number of
boards appear to be hard-configured for I2C by pulling SDO to GND).
For MPU-9250/MPU-9255/MPU-6515/MPU-6050/MPU-6500s there are also a variety of
board designs and clones with different I2C pull-up resistors which will need
supplementing.
For MPU-9250/MPU-9255/MPU-6515/MPU-6050/MPU-6500s and LIS2DW/LIS3DH there are also
a variety of board designs and clones with different I2C pull-up resistors which
will need supplementing.
## MCUs with Klipper I2C *fast-mode* Support
@@ -27,6 +29,7 @@ supplementing.
| Raspberry Pi | 3B+, Pico | 3A, 3A+, 3B, 4 |
| AVR ATmega | ATmega328p | ATmega32u4, ATmega128, ATmega168, ATmega328, ATmega644p, ATmega1280, ATmega1284, ATmega2560 |
| AVR AT90 | - | AT90usb646, AT90usb1286 |
| SAMD | SAMC21G18 | SAMC21G18, SAMD21G18, SAMD21E18, SAMD21J18, SAMD21E15, SAMD51G19, SAMD51J19, SAMD51N19, SAMD51P20, SAME51J19, SAME51N19, SAME54P20 |
## Installation instructions
@@ -207,17 +210,25 @@ software dependencies not installed by default. First, run on your Raspberry Pi
the following commands:
```
sudo apt update
sudo apt install python3-numpy python3-matplotlib libatlas-base-dev
sudo apt install python3-numpy python3-matplotlib libatlas-base-dev libopenblas-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<1.26"
```
Note that, depending on the performance of the CPU, it may take *a lot*
of time, up to 10-20 minutes. Be patient and wait for the completion of
the installation. On some occasions, if the board has 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. Also note
the forced version, due to newer versions of NumPY having requirements
that may not be satisfied in some klipper python environments.
Once installed please check that no errors show from the command:
```
~/klippy-env/bin/python -c 'import numpy;'
```
The correct output should simply be a new line.
#### Configure ADXL345 With RPi
@@ -305,7 +316,7 @@ you'll also want to modify your `printer.cfg` file to include this:
Restart Klipper via the `RESTART` command.
#### Configure LIS2DW series
#### Configure LIS2DW series over SPI
```
[mcu lis]
@@ -450,7 +461,11 @@ TEST_RESONANCES AXIS=Y
```
This will generate 2 CSV files (`/tmp/resonances_x_*.csv` and
`/tmp/resonances_y_*.csv`). These files can be processed with the stand-alone
script on a Raspberry Pi. To do that, run the following commands:
script on a Raspberry Pi. This script is intended to be run with a single CSV
file for each axis measured, although it can be used with multiple CSV files
if you desire to average the results. Averaging results can be useful, for
example, if resonance tests were done at multiple test points. Delete the extra
CSV files if you do not desire to average them.
```
~/klipper/scripts/calibrate_shaper.py /tmp/resonances_x_*.csv -o /tmp/shaper_calibrate_x.png
~/klipper/scripts/calibrate_shaper.py /tmp/resonances_y_*.csv -o /tmp/shaper_calibrate_y.png
@@ -662,10 +677,41 @@ The same notice applies to the input shaper
`max_accel` value after the auto-calibration, and the suggested acceleration
limits will not be applied automatically.
Keep in mind that the maximum acceleration without too much smoothing depends
on the `square_corner_velocity`. The general recommendation is not to change
it from its default value 5.0, and this is the value used by default by the
`calibrate_shaper.py` script. If you did change it though, you should inform
the script about it by passing `--square_corner_velocity=...` parameter, e.g.
```
~/klipper/scripts/calibrate_shaper.py /tmp/resonances_x_*.csv -o /tmp/shaper_calibrate_x.png --square_corner_velocity=10.0
```
so that it can calculate the maximum acceleration recommendations correctly.
Note that the `SHAPER_CALIBRATE` command already takes the configured
`square_corner_velocity` parameter into account, and there is no need
to specify it explicitly.
If you are doing a shaper re-calibration and the reported smoothing for the
suggested shaper configuration is almost the same as what you got during the
previous calibration, this step can be skipped.
### Unreliable measurements of resonance frequencies
Sometimes the resonance measurements can produce bogus results, leading to
the incorrect suggestions for the input shapers. This can be caused by a
variety of reasons, including running fans on the toolhead, incorrect
position or non-rigid mounting of the accelerometer, or mechanical problems
such as loose belts or binding or bumpy axis. Keep in mind that all fans
should be disabled for resonance testing, especially the noisy ones, and
that the accelerometer should be rigidly mounted on the corresponding
moving part (e.g. on the bed itself for the bed slinger, or on the extruder
of the printer itself and not the carriage, and some people get better
results by mounting the accelerometer on the nozzle itself). As for
mechanical problems, the user should inspect if there is any fault that
can be fixed with a moving axis (e.g. linear guide rails cleaned up and
lubricated and V-slot wheels tension adjusted correctly). If none of that
helps, a user may try the other shapers from the produced list besides the
one recommended by default.
### Testing custom axes
`TEST_RESONANCES` command supports custom axes. While this is not really

View File

@@ -31,9 +31,15 @@ overshoot and account for it in its calculations. However, it is
important that the hardware design is capable of handling overshoot
without causing damage to the machine.
Should Klipper detect a communication issue between micro-controllers
during multi-mcu homing then it will raise a "Communication timeout
during homing" error.
In order to use this "multi-mcu homing" capability the hardware must
have predictably low latency between the host computer and all of the
micro-controllers. Typically the round-trip time must be consistently
less than 10ms. High latency (even for short periods) is likely to
result in homing failures.
Should high latency result in a failure (or if some other
communication issue is detected) then Klipper will raise a
"Communication timeout during homing" error.
Note that an axis with multiple steppers (eg, `stepper_z` and
`stepper_z1`) need to be on the same micro-controller in order to use

79
docs/OctoPrint.md Normal file
View File

@@ -0,0 +1,79 @@
# OctoPrint for Klipper
Klipper has a few options for its front ends, Octoprint was the first
and original front end for Klipper. This document will give
a brief overview of installing with this option.
## Install with OctoPi
Start by installing [OctoPi](https://github.com/guysoft/OctoPi) on the
Raspberry Pi computer. Use OctoPi v0.17.0 or later - see the
[OctoPi releases](https://github.com/guysoft/OctoPi/releases) for
release information.
One should verify that OctoPi boots and that the
OctoPrint web server works. After connecting to the OctoPrint web
page, follow the prompt to upgrade OctoPrint if needed.
After installing OctoPi and upgrading OctoPrint, it will be necessary
to ssh into the target machine to run a handful of system commands.
Start by running these commands on your host device:
__If you do not have git installed, please do so with:__
```
sudo apt install git
```
then proceed:
```
cd ~
git clone https://github.com/Klipper3d/klipper
./klipper/scripts/install-octopi.sh
```
The above will download Klipper, install the needed system dependencies,
setup Klipper to run at system startup, and start the Klipper host
software. It will require an internet connection and it may take a few
minutes to complete.
## Installing with KIAUH
KIAUH can be used to install OctoPrint on a variety of Linux based systems
that run a form of Debian. More information can be found
at https://github.com/dw-0/kiauh
## Configuring OctoPrint to use Klipper
The OctoPrint web server needs to be configured to communicate with the Klipper
host software. Using a web browser, login to the OctoPrint web page and then
configure the following items:
Navigate to the Settings tab (the wrench icon at the top of the page).
Under "Serial Connection" in "Additional serial ports" add:
```
~/printer_data/comms/klippy.serial
```
Then click "Save".
_In some older setups this address may be `/tmp/printer`_
Enter the Settings tab again and under "Serial Connection" change the "Serial Port"
setting to the one added above.
In the Settings tab, navigate to the "Behavior" sub-tab and select the
"Cancel any ongoing prints but stay connected to the printer" option. Click "Save".
From the main page, under the "Connection" section (at the top left of the page)
make sure the "Serial Port" is set to the new additional one added
and click "Connect". (If it is not in the available selection then
try reloading the page.)
Once connected, navigate to the "Terminal" tab and type "status" (without the quotes)
into the command entry box and click "Send". The terminal window will likely report
there is an error opening the config file - that means OctoPrint is successfully
communicating with Klipper.
Please proceed to [Installation.md](Installation.md) and the
_Building and flashing the micro-controller_ section

View File

@@ -17,6 +17,7 @@ communication with the Klipper developers.
## Installation and Configuration
- [Installation](Installation.md): Guide to installing Klipper.
- [Octoprint](OctoPrint.md): Guide to installing Octoprint with Klipper.
- [Config Reference](Config_Reference.md): Description of config
parameters.
- [Rotation Distance](Rotation_Distance.md): Calculating the
@@ -99,3 +100,4 @@ communication with the Klipper developers.
troubleshooting CAN bus.
- [TSL1401CL filament width sensor](TSL1401CL_Filament_Width_Sensor.md)
- [Hall filament width sensor](Hall_Filament_Width_Sensor.md)
- [Eddy Current Inductive probe](Eddy_Probe.md)

View File

@@ -64,7 +64,7 @@ automatic probe point, then `ABORT` the manual probe tool and perform
the XY probe offset calibration described above.
Once the manual probe tool starts, follow the steps described at
["the paper test"](Bed_Level.md#the-paper-test)) to determine the
["the paper test"](Bed_Level.md#the-paper-test) to determine the
actual distance between the nozzle and bed at the given location. Once
those steps are complete one can `ACCEPT` the position and save the
results to the config file with:

View File

@@ -48,8 +48,8 @@ First, measure the **ringing frequency**.
to 5.0. It is not advised to increase it when using input shaper
because it can cause more smoothing in parts - it is better to use
higher acceleration value instead.
2. Increase `max_accel_to_decel` by issuing the following command:
`SET_VELOCITY_LIMIT ACCEL_TO_DECEL=7000`
2. Disable the `minimum_cruise_ratio` feature by issuing the following
command: `SET_VELOCITY_LIMIT MINIMUM_CRUISE_RATIO=0`
3. Disable Pressure Advance: `SET_PRESSURE_ADVANCE ADVANCE=0`
4. If you have already added `[input_shaper]` section to the printer.cfg,
execute `SET_INPUT_SHAPER SHAPER_FREQ_X=0 SHAPER_FREQ_Y=0` command. If you
@@ -149,7 +149,7 @@ a few other related parameters.
Print the ringing test model as follows:
1. Restart the firmware: `RESTART`
2. Prepare for test: `SET_VELOCITY_LIMIT ACCEL_TO_DECEL=7000`
2. Prepare for test: `SET_VELOCITY_LIMIT MINIMUM_CRUISE_RATIO=0`
3. Disable Pressure Advance: `SET_PRESSURE_ADVANCE ADVANCE=0`
4. Execute: `SET_INPUT_SHAPER SHAPER_TYPE=MZV`
5. Execute the command:
@@ -270,7 +270,7 @@ frequencies after enabling [input_shaper], this section will not help with that.
Assuming that you have sliced the ringing model with suggested
parameters, complete the following steps for each of the axes X and Y:
1. Prepare for test: `SET_VELOCITY_LIMIT ACCEL_TO_DECEL=7000`
1. Prepare for test: `SET_VELOCITY_LIMIT MINIMUM_CRUISE_RATIO=0`
2. Make sure Pressure Advance is disabled: `SET_PRESSURE_ADVANCE ADVANCE=0`
3. Execute: `SET_INPUT_SHAPER SHAPER_TYPE=ZV`
4. From the existing ringing test model with your chosen input shaper select
@@ -331,7 +331,7 @@ with suggested parameters, print the test model 3 times as
follows. First time, prior to printing, run
1. `RESTART`
2. `SET_VELOCITY_LIMIT ACCEL_TO_DECEL=7000`
2. `SET_VELOCITY_LIMIT MINIMUM_CRUISE_RATIO=0`
3. `SET_PRESSURE_ADVANCE ADVANCE=0`
4. `SET_INPUT_SHAPER SHAPER_TYPE=2HUMP_EI SHAPER_FREQ_X=60 SHAPER_FREQ_Y=60`
5. `TUNING_TOWER COMMAND=SET_VELOCITY_LIMIT PARAMETER=ACCEL START=1500 STEP_DELTA=500 STEP_HEIGHT=5`

View File

@@ -21,7 +21,7 @@ or by issuing a `SET_SKEW CLEAR=1` gcode.
## Take your measurements
The `[skew_correcton]` module requires 3 measurements for each plane you want
The `[skew_correction]` module requires 3 measurements for each plane you want
to correct; the length from Corner A to Corner C, the length from Corner B
to Corner D, and the length from Corner A to Corner D. When measuring length
AD do not include the flats on the corners that some test objects provide.

View File

@@ -17,7 +17,6 @@ serve the 3D printing community better. Follow them on
## Sponsors
[<img src="./img/sponsors/obico-light-horizontal.png" width="200" style="margin:25px" />](https://obico.io/klipper.html?source=klipper_sponsor)
[<img src="./img/sponsors/peopoly-logo.png" width="200" style="margin:25px" />](https://peopoly.net)
## Klipper Developers

View File

@@ -168,6 +168,12 @@ The following information is available in the
module. These settings may differ from the config file if a
`SET_RETRACTION` command alters them.
## gcode
The following information is available in the `gcode` object:
- `commands`: Returns a list of all currently available commands. For each
command, if a help string is defined it will also be provided.
## gcode_button
The following information is available in
@@ -318,7 +324,8 @@ is defined):
## output_pin
The following information is available in
[output_pin some_name](Config_Reference.md#output_pin) objects:
[output_pin some_name](Config_Reference.md#output_pin) and
[pwm_tool some_name](Config_Reference.md#pwm_tool) objects:
- `value`: The "value" of the pin, as set by a `SET_PIN` command.
## palette2
@@ -367,6 +374,13 @@ is defined):
template expansion, the PROBE (or similar) command must be run prior
to the macro containing this reference.
## pwm_cycle_time
The following information is available in
[pwm_cycle_time some_name](Config_Reference.md#pwm_cycle_time)
objects:
- `value`: The "value" of the pin, as set by a `SET_PIN` command.
## quad_gantry_level
The following information is available in the `quad_gantry_level` object
@@ -431,6 +445,7 @@ The following information is available in
[bme280 config_section_name](Config_Reference.md#bmp280bme280bme680-temperature-sensor),
[htu21d config_section_name](Config_Reference.md#htu21d-sensor),
[sht3x config_section_name](Config_Reference.md#sht31-sensor),
[lm75 config_section_name](Config_Reference.md#lm75-temperature-sensor),
[temperature_host config_section_name](Config_Reference.md#host-temperature-sensor)
and
@@ -438,7 +453,7 @@ and
objects:
- `temperature`: The last read temperature from the sensor.
- `humidity`, `pressure`, `gas`: The last read values from the sensor
(only on bme280, htu21d, and lm75 sensors).
(only on bme280, htu21d, sht3x and lm75 sensors).
## temperature_fan
@@ -497,7 +512,7 @@ The following information is available in the `toolhead` object
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`, `minimum_cruise_ratio`,
`square_corner_velocity`: The current printing limits that are in
effect. This may differ from the config file settings if a
`SET_VELOCITY_LIMIT` (or `M204`) command alters them at run-time.

View File

@@ -1,7 +1,7 @@
# Using PWM tools
This document describes how to setup a PWM-controlled laser or spindle
using `output_pin` and some macros.
using `pwm_tool` and some macros.
## How does it work?
@@ -26,14 +26,6 @@ so that when your host or MCU encounters an error, the tool will stop.
For an example configuration, see [config/sample-pwm-tool.cfg](/config/sample-pwm-tool.cfg).
## Current Limitations
There is a limitation of how frequent PWM updates may occur.
While being very precise, a PWM update may only occur every 0.1 seconds,
rendering it almost useless for raster engraving.
However, there exists an [experimental branch](https://github.com/Cirromulus/klipper/tree/laser_tool) with its own tradeoffs.
In long term, it is planned to add this functionality to main-line klipper.
## Commands
`M3/M4 S<value>` : Set PWM duty-cycle. Values between 0 and 255.

View File

@@ -1,6 +1,6 @@
# Python virtualenv module requirements for mkdocs
jinja2==3.0.3
mkdocs==1.2.3
jinja2==3.1.4
mkdocs==1.2.4
mkdocs-material==8.1.3
mkdocs-simple-hooks==0.1.3
mkdocs-exclude==1.0.2

View File

@@ -71,7 +71,7 @@ extra:
# https://squidfunk.github.io/mkdocs-material/setup/setting-up-site-analytics/#site-search-tracking
analytics:
provider: google
property: UA-138371409-1
property: G-VEN1PGNQL4
# Language Selection
alternate:
- name: English
@@ -88,7 +88,9 @@ nav:
- Config_Changes.md
- Contact.md
- Installation and Configuration:
- Installation.md
- Installation:
- Installation.md
- OctoPrint.md
- Configuration Reference:
- Config_Reference.md
- Rotation_Distance.md
@@ -138,4 +140,5 @@ nav:
- CANBUS_Troubleshooting.md
- TSL1401CL_Filament_Width_Sensor.md
- Hall_Filament_Width_Sensor.md
- Eddy_Probe.md
- Sponsors.md

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -6,13 +6,16 @@ title: Welcome
![](img/klipper-logo.png){ .center-image }
Klipper is a 3d-Printer firmware. It combines the power of a general
purpose computer with one or more micro-controllers. See the
[features](Features.md) document for more information on why you
should use Klipper.
The Klipper firmware controls 3d-Printers. It combines the power of a
general purpose computer with one or more micro-controllers. See the
[features document](https://www.klipper3d.org/Features.html) for more
information on why you should use the Klipper software.
To begin using Klipper start by [installing](Installation.md) it.
Start by [installing Klipper software](https://www.klipper3d.org/Installation.html).
Klipper is Free Software. Read the [documentation](Overview.md) or
view [the Klipper code on github](https://github.com/Klipper3d/klipper).
We depend on the generous support from our [sponsors](Sponsors.md).
Klipper software is Free Software. Read the
[documentation](https://www.klipper3d.org/Overview.html), see the
[license](COPYING), or
[download](https://github.com/Klipper3d/Klipper) the software. We
depend on the generous support from our
[sponsors](https://www.klipper3d.org/Sponsors.html).

View File

@@ -49,6 +49,8 @@ defs_stepcompress = """
, uint64_t clock);
int stepcompress_queue_msg(struct stepcompress *sc
, uint32_t *data, int len);
int stepcompress_queue_mq_msg(struct stepcompress *sc, uint64_t req_clock
, uint32_t *data, int len);
int stepcompress_extract_old(struct stepcompress *sc
, struct pull_history_steps *p, int max
, uint64_t start_clock, uint64_t end_clock);
@@ -58,7 +60,8 @@ defs_stepcompress = """
void steppersync_free(struct steppersync *ss);
void steppersync_set_time(struct steppersync *ss
, double time_offset, double mcu_freq);
int steppersync_flush(struct steppersync *ss, uint64_t move_clock);
int steppersync_flush(struct steppersync *ss, uint64_t move_clock
, uint64_t clear_history_clock);
"""
defs_itersolve = """
@@ -92,7 +95,8 @@ defs_trapq = """
, double start_pos_x, double start_pos_y, double start_pos_z
, double axes_r_x, double axes_r_y, double axes_r_z
, double start_v, double cruise_v, double accel);
void trapq_finalize_moves(struct trapq *tq, double print_time);
void trapq_finalize_moves(struct trapq *tq, double print_time
, double clear_history_time);
void trapq_set_position(struct trapq *tq, double print_time
, double pos_x, double pos_y, double pos_z);
int trapq_extract_old(struct trapq *tq, struct pull_move *p, int max
@@ -138,8 +142,9 @@ defs_kin_winch = """
defs_kin_extruder = """
struct stepper_kinematics *extruder_stepper_alloc(void);
void extruder_stepper_free(struct stepper_kinematics *sk);
void extruder_set_pressure_advance(struct stepper_kinematics *sk
, double pressure_advance, double smooth_time);
, double print_time, double pressure_advance, double smooth_time);
"""
defs_kin_shaper = """

View File

@@ -9,9 +9,15 @@
#include <string.h> // memset
#include "compiler.h" // __visible
#include "itersolve.h" // struct stepper_kinematics
#include "list.h" // list_node
#include "pyhelper.h" // errorf
#include "trapq.h" // move_get_distance
struct pa_params {
double pressure_advance, active_print_time;
struct list_node node;
};
// Without pressure advance, the extruder stepper position is:
// extruder_position(t) = nominal_position(t)
// When pressure advance is enabled, additional filament is pushed
@@ -52,17 +58,25 @@ extruder_integrate_time(double base, double start_v, double half_accel
// Calculate the definitive integral of extruder for a given move
static double
pa_move_integrate(struct move *m, double pressure_advance
pa_move_integrate(struct move *m, struct list_head *pa_list
, double base, double start, double end, double time_offset)
{
if (start < 0.)
start = 0.;
if (end > m->move_t)
end = m->move_t;
// Calculate base position and velocity with pressure advance
// Determine pressure_advance value
int can_pressure_advance = m->axes_r.y != 0.;
if (!can_pressure_advance)
pressure_advance = 0.;
double pressure_advance = 0.;
if (can_pressure_advance) {
struct pa_params *pa = list_last_entry(pa_list, struct pa_params, node);
while (unlikely(pa->active_print_time > m->print_time) &&
!list_is_first(&pa->node, pa_list)) {
pa = list_prev_entry(pa, node);
}
pressure_advance = pa->pressure_advance;
}
// Calculate base position and velocity with pressure advance
base += pressure_advance * m->start_v;
double start_v = m->start_v + pressure_advance * 2. * m->half_accel;
// Calculate definitive integral
@@ -75,20 +89,20 @@ pa_move_integrate(struct move *m, double pressure_advance
// Calculate the definitive integral of the extruder over a range of moves
static double
pa_range_integrate(struct move *m, double move_time
, double pressure_advance, double hst)
, struct list_head *pa_list, double hst)
{
// Calculate integral for the current move
double res = 0., start = move_time - hst, end = move_time + hst;
double start_base = m->start_pos.x;
res += pa_move_integrate(m, pressure_advance, 0., start, move_time, start);
res -= pa_move_integrate(m, pressure_advance, 0., move_time, end, end);
res += pa_move_integrate(m, pa_list, 0., start, move_time, start);
res -= pa_move_integrate(m, pa_list, 0., move_time, end, end);
// Integrate over previous moves
struct move *prev = m;
while (unlikely(start < 0.)) {
prev = list_prev_entry(prev, node);
start += prev->move_t;
double base = prev->start_pos.x - start_base;
res += pa_move_integrate(prev, pressure_advance, base, start
res += pa_move_integrate(prev, pa_list, base, start
, prev->move_t, start);
}
// Integrate over future moves
@@ -96,14 +110,15 @@ pa_range_integrate(struct move *m, double move_time
end -= m->move_t;
m = list_next_entry(m, node);
double base = m->start_pos.x - start_base;
res -= pa_move_integrate(m, pressure_advance, base, 0., end, end);
res -= pa_move_integrate(m, pa_list, base, 0., end, end);
}
return res;
}
struct extruder_stepper {
struct stepper_kinematics sk;
double pressure_advance, half_smooth_time, inv_half_smooth_time2;
struct list_head pa_list;
double half_smooth_time, inv_half_smooth_time2;
};
static double
@@ -116,22 +131,45 @@ extruder_calc_position(struct stepper_kinematics *sk, struct move *m
// Pressure advance not enabled
return m->start_pos.x + move_get_distance(m, move_time);
// Apply pressure advance and average over smooth_time
double area = pa_range_integrate(m, move_time, es->pressure_advance, hst);
double area = pa_range_integrate(m, move_time, &es->pa_list, hst);
return m->start_pos.x + area * es->inv_half_smooth_time2;
}
void __visible
extruder_set_pressure_advance(struct stepper_kinematics *sk
extruder_set_pressure_advance(struct stepper_kinematics *sk, double print_time
, double pressure_advance, double smooth_time)
{
struct extruder_stepper *es = container_of(sk, struct extruder_stepper, sk);
double hst = smooth_time * .5;
double hst = smooth_time * .5, old_hst = es->half_smooth_time;
es->half_smooth_time = hst;
es->sk.gen_steps_pre_active = es->sk.gen_steps_post_active = hst;
// Cleanup old pressure advance parameters
double cleanup_time = sk->last_flush_time - (old_hst > hst ? old_hst : hst);
struct pa_params *first_pa = list_first_entry(
&es->pa_list, struct pa_params, node);
while (!list_is_last(&first_pa->node, &es->pa_list)) {
struct pa_params *next_pa = list_next_entry(first_pa, node);
if (next_pa->active_print_time >= cleanup_time) break;
list_del(&first_pa->node);
first_pa = next_pa;
}
if (! hst)
return;
es->inv_half_smooth_time2 = 1. / (hst * hst);
es->pressure_advance = pressure_advance;
if (list_last_entry(&es->pa_list, struct pa_params, node)->pressure_advance
== pressure_advance) {
// Retain old pa_params
return;
}
// Add new pressure advance parameters
struct pa_params *pa = malloc(sizeof(*pa));
memset(pa, 0, sizeof(*pa));
pa->pressure_advance = pressure_advance;
pa->active_print_time = print_time;
list_add_tail(&pa->node, &es->pa_list);
}
struct stepper_kinematics * __visible
@@ -141,5 +179,22 @@ extruder_stepper_alloc(void)
memset(es, 0, sizeof(*es));
es->sk.calc_position_cb = extruder_calc_position;
es->sk.active_flags = AF_X;
list_init(&es->pa_list);
struct pa_params *pa = malloc(sizeof(*pa));
memset(pa, 0, sizeof(*pa));
list_add_tail(&pa->node, &es->pa_list);
return &es->sk;
}
void __visible
extruder_stepper_free(struct stepper_kinematics *sk)
{
struct extruder_stepper *es = container_of(sk, struct extruder_stepper, sk);
while (!list_empty(&es->pa_list)) {
struct pa_params *pa = list_first_entry(
&es->pa_list, struct pa_params, node);
list_del(&pa->node);
free(pa);
}
free(sk);
}

View File

@@ -222,12 +222,11 @@ handle_message(struct serialqueue *sq, double eventtime, int len)
pthread_mutex_lock(&sq->lock);
// Calculate receive sequence number
uint64_t rseq = ((sq->receive_seq & ~MESSAGE_SEQ_MASK)
| (sq->input_buf[MESSAGE_POS_SEQ] & MESSAGE_SEQ_MASK));
uint32_t rseq_delta = ((sq->input_buf[MESSAGE_POS_SEQ] - sq->receive_seq)
& MESSAGE_SEQ_MASK);
uint64_t rseq = sq->receive_seq + rseq_delta;
if (rseq != sq->receive_seq) {
// New sequence number
if (rseq < sq->receive_seq)
rseq += MESSAGE_SEQ_MASK+1;
if (rseq > sq->send_seq && sq->receive_seq != 1) {
// An ack for a message not sent? Out of order message?
sq->bytes_invalid += len;

View File

@@ -54,8 +54,6 @@ struct step_move {
int16_t add;
};
#define HISTORY_EXPIRE (30.0)
struct history_steps {
struct list_node node;
uint64_t first_clock, last_clock;
@@ -292,6 +290,13 @@ free_history(struct stepcompress *sc, uint64_t end_clock)
}
}
// Expire the stepcompress history older than the given clock
static void
stepcompress_history_expire(struct stepcompress *sc, uint64_t end_clock)
{
free_history(sc, end_clock);
}
// Free memory associated with a 'stepcompress' object
void __visible
stepcompress_free(struct stepcompress *sc)
@@ -322,9 +327,6 @@ calc_last_step_print_time(struct stepcompress *sc)
{
double lsc = sc->last_step_clock;
sc->last_step_print_time = sc->mcu_time_offset + (lsc - .5) / sc->mcu_freq;
if (lsc > sc->mcu_freq * HISTORY_EXPIRE)
free_history(sc, lsc - sc->mcu_freq * HISTORY_EXPIRE);
}
// Set the conversion rate of 'print_time' to mcu clock
@@ -623,6 +625,21 @@ stepcompress_queue_msg(struct stepcompress *sc, uint32_t *data, int len)
return 0;
}
// Queue an mcu command that will consume space in the mcu move queue
int __visible
stepcompress_queue_mq_msg(struct stepcompress *sc, uint64_t req_clock
, uint32_t *data, int len)
{
int ret = stepcompress_flush(sc, UINT64_MAX);
if (ret)
return ret;
struct queue_message *qm = message_alloc_and_encode(data, len);
qm->min_clock = qm->req_clock = req_clock;
list_add_tail(&qm->node, &sc->msg_queue);
return 0;
}
// Return history of queue_step commands
int __visible
stepcompress_extract_old(struct stepcompress *sc, struct pull_history_steps *p
@@ -716,6 +733,18 @@ steppersync_set_time(struct steppersync *ss, double time_offset
}
}
// Expire the stepcompress history before the given clock time
static void
steppersync_history_expire(struct steppersync *ss, uint64_t end_clock)
{
int i;
for (i = 0; i < ss->sc_num; i++)
{
struct stepcompress *sc = ss->sc_list[i];
stepcompress_history_expire(sc, end_clock);
}
}
// Implement a binary heap algorithm to track when the next available
// 'struct move' in the mcu will be available
static void
@@ -743,7 +772,8 @@ heap_replace(struct steppersync *ss, uint64_t req_clock)
// Find and transmit any scheduled steps prior to the given 'move_clock'
int __visible
steppersync_flush(struct steppersync *ss, uint64_t move_clock)
steppersync_flush(struct steppersync *ss, uint64_t move_clock
, uint64_t clear_history_clock)
{
// Flush each stepcompress to the specified move_clock
int i;
@@ -791,5 +821,7 @@ steppersync_flush(struct steppersync *ss, uint64_t move_clock)
// Transmit commands
if (!list_empty(&msgs))
serialqueue_send_batch(ss->sq, ss->cq, &msgs);
steppersync_history_expire(ss, clear_history_clock);
return 0;
}

View File

@@ -29,6 +29,8 @@ int stepcompress_set_last_position(struct stepcompress *sc, uint64_t clock
int64_t stepcompress_find_past_position(struct stepcompress *sc
, uint64_t clock);
int stepcompress_queue_msg(struct stepcompress *sc, uint32_t *data, int len);
int stepcompress_queue_mq_msg(struct stepcompress *sc, uint64_t req_clock
, uint32_t *data, int len);
int stepcompress_extract_old(struct stepcompress *sc
, struct pull_history_steps *p, int max
, uint64_t start_clock, uint64_t end_clock);
@@ -40,6 +42,7 @@ struct steppersync *steppersync_alloc(
void steppersync_free(struct steppersync *ss);
void steppersync_set_time(struct steppersync *ss, double time_offset
, double mcu_freq);
int steppersync_flush(struct steppersync *ss, uint64_t move_clock);
int steppersync_flush(struct steppersync *ss, uint64_t move_clock
, uint64_t clear_history_clock);
#endif // stepcompress.h

View File

@@ -163,11 +163,10 @@ trapq_append(struct trapq *tq, double print_time
}
}
#define HISTORY_EXPIRE (30.0)
// Expire any moves older than `print_time` from the trapezoid velocity queue
void __visible
trapq_finalize_moves(struct trapq *tq, double print_time)
trapq_finalize_moves(struct trapq *tq, double print_time
, double clear_history_time)
{
struct move *head_sentinel = list_first_entry(&tq->moves, struct move,node);
struct move *tail_sentinel = list_last_entry(&tq->moves, struct move, node);
@@ -190,10 +189,9 @@ trapq_finalize_moves(struct trapq *tq, double print_time)
if (list_empty(&tq->history))
return;
struct move *latest = list_first_entry(&tq->history, struct move, node);
double expire_time = latest->print_time + latest->move_t - HISTORY_EXPIRE;
for (;;) {
struct move *m = list_last_entry(&tq->history, struct move, node);
if (m == latest || m->print_time + m->move_t > expire_time)
if (m == latest || m->print_time + m->move_t > clear_history_time)
break;
list_del(&m->node);
free(m);
@@ -206,7 +204,7 @@ trapq_set_position(struct trapq *tq, double print_time
, double pos_x, double pos_y, double pos_z)
{
// Flush all moves from trapq
trapq_finalize_moves(tq, NEVER_TIME);
trapq_finalize_moves(tq, NEVER_TIME, 0);
// Prune any moves in the trapq history that were interrupted
while (!list_empty(&tq->history)) {

View File

@@ -43,7 +43,8 @@ void trapq_append(struct trapq *tq, double print_time
, double start_pos_x, double start_pos_y, double start_pos_z
, double axes_r_x, double axes_r_y, double axes_r_z
, double start_v, double cruise_v, double accel);
void trapq_finalize_moves(struct trapq *tq, double print_time);
void trapq_finalize_moves(struct trapq *tq, double print_time
, double clear_history_time);
void trapq_set_position(struct trapq *tq, double print_time
, double pos_x, double pos_y, double pos_z);
int trapq_extract_old(struct trapq *tq, struct pull_move *p, int max

View File

@@ -66,10 +66,8 @@ class ClockSync:
self.queries_pending = 0
# Extend clock to 64bit
last_clock = self.last_clock
clock = (last_clock & ~0xffffffff) | params['clock']
if clock < last_clock:
clock += 0x100000000
self.last_clock = clock
clock_delta = (params['clock'] - last_clock) & 0xffffffff
self.last_clock = clock = last_clock + clock_delta
# Check if this is the best round-trip-time seen so far
sent_time = params['#sent_time']
if not sent_time:
@@ -138,10 +136,9 @@ class ClockSync:
# misc commands
def clock32_to_clock64(self, clock32):
last_clock = self.last_clock
clock_diff = (last_clock - clock32) & 0xffffffff
if clock_diff & 0x80000000:
return last_clock + 0x100000000 - clock_diff
return last_clock - clock_diff
clock_diff = (clock32 - last_clock) & 0xffffffff
clock_diff -= (clock_diff & 0x80000000) << 1
return last_clock + clock_diff
def is_active(self):
return self.queries_pending <= 4
def dump_debug(self):

View File

@@ -1,12 +1,17 @@
# Code for reading and writing the Klipper config file
#
# Copyright (C) 2016-2021 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2016-2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import sys, os, glob, re, time, logging, configparser, io
error = configparser.Error
######################################################################
# Config section parsing helper
######################################################################
class sentinel:
pass
@@ -69,6 +74,8 @@ class ConfigWrapper:
return self._get_wrapper(self.fileconfig.getboolean, option, default,
note_valid=note_valid)
def getchoice(self, option, choices, default=sentinel, note_valid=True):
if type(choices) == type([]):
choices = {i: i for i in choices}
if choices and type(list(choices.keys())[0]) == int:
c = self.getint(option, default, note_valid=note_valid)
else:
@@ -132,28 +139,13 @@ class ConfigWrapper:
pconfig = self.printer.lookup_object("configfile")
pconfig.deprecate(self.section, option, value, msg)
AUTOSAVE_HEADER = """
#*# <---------------------- SAVE_CONFIG ---------------------->
#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
#*#
"""
class PrinterConfig:
def __init__(self, printer):
self.printer = printer
self.autosave = None
self.deprecated = {}
self.status_raw_config = {}
self.status_save_pending = {}
self.status_settings = {}
self.status_warnings = []
self.save_config_pending = False
gcode = self.printer.lookup_object('gcode')
gcode.register_command("SAVE_CONFIG", self.cmd_SAVE_CONFIG,
desc=self.cmd_SAVE_CONFIG_help)
def get_printer(self):
return self.printer
def _read_config_file(self, filename):
######################################################################
# Config file parsing (with include file support)
######################################################################
class ConfigFileReader:
def read_config_file(self, filename):
try:
f = open(filename, 'r')
data = f.read()
@@ -163,6 +155,102 @@ class PrinterConfig:
logging.exception(msg)
raise error(msg)
return data.replace('\r\n', '\n')
def build_config_string(self, fileconfig):
sfile = io.StringIO()
fileconfig.write(sfile)
return sfile.getvalue().strip()
def append_fileconfig(self, fileconfig, data, filename):
if not data:
return
# Strip trailing comments
lines = data.split('\n')
for i, line in enumerate(lines):
pos = line.find('#')
if pos >= 0:
lines[i] = line[:pos]
sbuffer = io.StringIO('\n'.join(lines))
if sys.version_info.major >= 3:
fileconfig.read_file(sbuffer, filename)
else:
fileconfig.readfp(sbuffer, filename)
def _create_fileconfig(self):
if sys.version_info.major >= 3:
fileconfig = configparser.RawConfigParser(
strict=False, inline_comment_prefixes=(';', '#'))
else:
fileconfig = configparser.RawConfigParser()
return fileconfig
def build_fileconfig(self, data, filename):
fileconfig = self._create_fileconfig()
self.append_fileconfig(fileconfig, data, filename)
return fileconfig
def _resolve_include(self, source_filename, include_spec, fileconfig,
visited):
dirname = os.path.dirname(source_filename)
include_spec = include_spec.strip()
include_glob = os.path.join(dirname, include_spec)
include_filenames = glob.glob(include_glob)
if not include_filenames and not glob.has_magic(include_glob):
# Empty set is OK if wildcard but not for direct file reference
raise error("Include file '%s' does not exist" % (include_glob,))
include_filenames.sort()
for include_filename in include_filenames:
include_data = self.read_config_file(include_filename)
self._parse_config(include_data, include_filename, fileconfig,
visited)
return include_filenames
def _parse_config(self, data, filename, fileconfig, visited):
path = os.path.abspath(filename)
if path in visited:
raise error("Recursive include of config file '%s'" % (filename))
visited.add(path)
lines = data.split('\n')
# Buffer lines between includes and parse as a unit so that overrides
# in includes apply linearly as they do within a single file
buf = []
for line in lines:
# Strip trailing comment
pos = line.find('#')
if pos >= 0:
line = line[:pos]
# Process include or buffer line
mo = configparser.RawConfigParser.SECTCRE.match(line)
header = mo and mo.group('header')
if header and header.startswith('include '):
self.append_fileconfig(fileconfig, '\n'.join(buf), filename)
del buf[:]
include_spec = header[8:].strip()
self._resolve_include(filename, include_spec, fileconfig,
visited)
else:
buf.append(line)
self.append_fileconfig(fileconfig, '\n'.join(buf), filename)
visited.remove(path)
def build_fileconfig_with_includes(self, data, filename):
fileconfig = self._create_fileconfig()
self._parse_config(data, filename, fileconfig, set())
return fileconfig
######################################################################
# Config auto save helper
######################################################################
AUTOSAVE_HEADER = """
#*# <---------------------- SAVE_CONFIG ---------------------->
#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
#*#
"""
class ConfigAutoSave:
def __init__(self, printer):
self.printer = printer
self.fileconfig = None
self.status_save_pending = {}
self.save_config_pending = False
gcode = self.printer.lookup_object('gcode')
gcode.register_command("SAVE_CONFIG", self.cmd_SAVE_CONFIG,
desc=self.cmd_SAVE_CONFIG_help)
def _find_autosave_data(self, data):
regular_data = data
autosave_data = ""
@@ -171,25 +259,24 @@ class PrinterConfig:
regular_data = data[:pos]
autosave_data = data[pos + len(AUTOSAVE_HEADER):].strip()
# Check for errors and strip line prefixes
if "\n#*# " in regular_data:
logging.warn("Can't read autosave from config file"
" - autosave state corrupted")
if "\n#*# " in regular_data or autosave_data.find(AUTOSAVE_HEADER) >= 0:
logging.warning("Can't read autosave from config file"
" - autosave state corrupted")
return data, ""
out = [""]
for line in autosave_data.split('\n'):
if ((not line.startswith("#*#")
or (len(line) >= 4 and not line.startswith("#*# ")))
and autosave_data):
logging.warn("Can't read autosave from config file"
" - modifications after header")
logging.warning("Can't read autosave from config file"
" - modifications after header")
return data, ""
out.append(line[4:])
out.append("")
return regular_data, "\n".join(out)
comment_r = re.compile('[#;].*$')
value_r = re.compile('[^A-Za-z0-9_].*$')
def _strip_duplicates(self, data, config):
fileconfig = config.fileconfig
def _strip_duplicates(self, data, fileconfig):
# Comment out fields in 'data' that are defined in 'config'
lines = data.split('\n')
section = None
@@ -207,143 +294,31 @@ class PrinterConfig:
section = pruned_line[1:-1].strip()
continue
field = self.value_r.sub('', pruned_line)
if config.fileconfig.has_option(section, field):
if fileconfig.has_option(section, field):
is_dup_field = True
lines[lineno] = '#' + lines[lineno]
return "\n".join(lines)
def _parse_config_buffer(self, buffer, filename, fileconfig):
if not buffer:
return
data = '\n'.join(buffer)
del buffer[:]
sbuffer = io.StringIO(data)
fileconfig.readfp(sbuffer, filename)
def _resolve_include(self, source_filename, include_spec, fileconfig,
visited):
dirname = os.path.dirname(source_filename)
include_spec = include_spec.strip()
include_glob = os.path.join(dirname, include_spec)
include_filenames = glob.glob(include_glob)
if not include_filenames and not glob.has_magic(include_glob):
# Empty set is OK if wildcard but not for direct file reference
raise error("Include file '%s' does not exist" % (include_glob,))
include_filenames.sort()
for include_filename in include_filenames:
include_data = self._read_config_file(include_filename)
self._parse_config(include_data, include_filename, fileconfig,
visited)
return include_filenames
def _parse_config(self, data, filename, fileconfig, visited):
path = os.path.abspath(filename)
if path in visited:
raise error("Recursive include of config file '%s'" % (filename))
visited.add(path)
lines = data.split('\n')
# Buffer lines between includes and parse as a unit so that overrides
# in includes apply linearly as they do within a single file
buffer = []
for line in lines:
# Strip trailing comment
pos = line.find('#')
if pos >= 0:
line = line[:pos]
# Process include or buffer line
mo = configparser.RawConfigParser.SECTCRE.match(line)
header = mo and mo.group('header')
if header and header.startswith('include '):
self._parse_config_buffer(buffer, filename, fileconfig)
include_spec = header[8:].strip()
self._resolve_include(filename, include_spec, fileconfig,
visited)
else:
buffer.append(line)
self._parse_config_buffer(buffer, filename, fileconfig)
visited.remove(path)
def _build_config_wrapper(self, data, filename):
if sys.version_info.major >= 3:
fileconfig = configparser.RawConfigParser(
strict=False, inline_comment_prefixes=(';', '#'))
else:
fileconfig = configparser.RawConfigParser()
self._parse_config(data, filename, fileconfig, set())
return ConfigWrapper(self.printer, fileconfig, {}, 'printer')
def _build_config_string(self, config):
sfile = io.StringIO()
config.fileconfig.write(sfile)
return sfile.getvalue().strip()
def read_config(self, filename):
return self._build_config_wrapper(self._read_config_file(filename),
filename)
def read_main_config(self):
def load_main_config(self):
filename = self.printer.get_start_args()['config_file']
data = self._read_config_file(filename)
cfgrdr = ConfigFileReader()
data = cfgrdr.read_config_file(filename)
regular_data, autosave_data = self._find_autosave_data(data)
regular_config = self._build_config_wrapper(regular_data, filename)
autosave_data = self._strip_duplicates(autosave_data, regular_config)
self.autosave = self._build_config_wrapper(autosave_data, filename)
cfg = self._build_config_wrapper(regular_data + autosave_data, filename)
return cfg
def check_unused_options(self, config):
fileconfig = config.fileconfig
objects = dict(self.printer.lookup_objects())
# Determine all the fields that have been accessed
access_tracking = dict(config.access_tracking)
for section in self.autosave.fileconfig.sections():
for option in self.autosave.fileconfig.options(section):
access_tracking[(section.lower(), option.lower())] = 1
# Validate that there are no undefined parameters in the config file
valid_sections = { s: 1 for s, o in access_tracking }
for section_name in fileconfig.sections():
section = section_name.lower()
if section not in valid_sections and section not in objects:
raise error("Section '%s' is not a valid config section"
% (section,))
for option in fileconfig.options(section_name):
option = option.lower()
if (section, option) not in access_tracking:
raise error("Option '%s' is not valid in section '%s'"
% (option, section))
# Setup get_status()
self._build_status(config)
def log_config(self, config):
lines = ["===== Config file =====",
self._build_config_string(config),
"======================="]
self.printer.set_rollover_info("config", "\n".join(lines))
# Status reporting
def deprecate(self, section, option, value=None, msg=None):
self.deprecated[(section, option, value)] = msg
def _build_status(self, config):
self.status_raw_config.clear()
for section in config.get_prefix_sections(''):
self.status_raw_config[section.get_name()] = section_status = {}
for option in section.get_prefix_options(''):
section_status[option] = section.get(option, note_valid=False)
self.status_settings = {}
for (section, option), value in config.access_tracking.items():
self.status_settings.setdefault(section, {})[option] = value
self.status_warnings = []
for (section, option, value), msg in self.deprecated.items():
if value is None:
res = {'type': 'deprecated_option'}
else:
res = {'type': 'deprecated_value', 'value': value}
res['message'] = msg
res['section'] = section
res['option'] = option
self.status_warnings.append(res)
regular_fileconfig = cfgrdr.build_fileconfig_with_includes(
regular_data, filename)
autosave_data = self._strip_duplicates(autosave_data,
regular_fileconfig)
self.fileconfig = cfgrdr.build_fileconfig(autosave_data, filename)
cfgrdr.append_fileconfig(regular_fileconfig,
autosave_data, '*AUTOSAVE*')
return regular_fileconfig, self.fileconfig
def get_status(self, eventtime):
return {'config': self.status_raw_config,
'settings': self.status_settings,
'warnings': self.status_warnings,
'save_config_pending': self.save_config_pending,
return {'save_config_pending': self.save_config_pending,
'save_config_pending_items': self.status_save_pending}
# Autosave functions
def set(self, section, option, value):
if not self.autosave.fileconfig.has_section(section):
self.autosave.fileconfig.add_section(section)
if not self.fileconfig.has_section(section):
self.fileconfig.add_section(section)
svalue = str(value)
self.autosave.fileconfig.set(section, option, svalue)
self.fileconfig.set(section, option, svalue)
pending = dict(self.status_save_pending)
if not section in pending or pending[section] is None:
pending[section] = {}
@@ -354,8 +329,8 @@ class PrinterConfig:
self.save_config_pending = True
logging.info("save_config: set [%s] %s = %s", section, option, svalue)
def remove_section(self, section):
if self.autosave.fileconfig.has_section(section):
self.autosave.fileconfig.remove_section(section)
if self.fileconfig.has_section(section):
self.fileconfig.remove_section(section)
pending = dict(self.status_save_pending)
pending[section] = None
self.status_save_pending = pending
@@ -366,21 +341,20 @@ class PrinterConfig:
del pending[section]
self.status_save_pending = pending
self.save_config_pending = True
def _disallow_include_conflicts(self, regular_data, cfgname, gcode):
config = self._build_config_wrapper(regular_data, cfgname)
for section in self.autosave.fileconfig.sections():
for option in self.autosave.fileconfig.options(section):
if config.fileconfig.has_option(section, option):
def _disallow_include_conflicts(self, regular_fileconfig):
for section in self.fileconfig.sections():
for option in self.fileconfig.options(section):
if regular_fileconfig.has_option(section, option):
msg = ("SAVE_CONFIG section '%s' option '%s' conflicts "
"with included value" % (section, option))
raise gcode.error(msg)
raise self.printer.command_error(msg)
cmd_SAVE_CONFIG_help = "Overwrite config file and restart"
def cmd_SAVE_CONFIG(self, gcmd):
if not self.autosave.fileconfig.sections():
if not self.fileconfig.sections():
return
gcode = self.printer.lookup_object('gcode')
# Create string containing autosave data
autosave_data = self._build_config_string(self.autosave)
cfgrdr = ConfigFileReader()
autosave_data = cfgrdr.build_config_string(self.fileconfig)
lines = [('#*# ' + l).strip()
for l in autosave_data.split('\n')]
lines.insert(0, "\n" + AUTOSAVE_HEADER.rstrip())
@@ -389,16 +363,27 @@ class PrinterConfig:
# Read in and validate current config file
cfgname = self.printer.get_start_args()['config_file']
try:
data = self._read_config_file(cfgname)
regular_data, old_autosave_data = self._find_autosave_data(data)
config = self._build_config_wrapper(regular_data, cfgname)
data = cfgrdr.read_config_file(cfgname)
except error as e:
msg = "Unable to read existing config on SAVE_CONFIG"
logging.exception(msg)
raise gcmd.error(msg)
regular_data, old_autosave_data = self._find_autosave_data(data)
regular_data = self._strip_duplicates(regular_data, self.fileconfig)
data = regular_data.rstrip() + autosave_data
new_regular_data, new_autosave_data = self._find_autosave_data(data)
if not new_autosave_data:
raise gcmd.error(
"Existing config autosave is corrupted."
" Can't complete SAVE_CONFIG")
try:
regular_fileconfig = cfgrdr.build_fileconfig_with_includes(
new_regular_data, cfgname)
except error as e:
msg = "Unable to parse existing config on SAVE_CONFIG"
logging.exception(msg)
raise gcode.error(msg)
regular_data = self._strip_duplicates(regular_data, self.autosave)
self._disallow_include_conflicts(regular_data, cfgname, gcode)
data = regular_data.rstrip() + autosave_data
raise gcmd.error(msg)
self._disallow_include_conflicts(regular_fileconfig)
# Determine filenames
datestr = time.strftime("-%Y%m%d_%H%M%S")
backup_name = cfgname + datestr
@@ -418,6 +403,135 @@ class PrinterConfig:
except:
msg = "Unable to write config file during SAVE_CONFIG"
logging.exception(msg)
raise gcode.error(msg)
raise gcmd.error(msg)
# Request a restart
gcode = self.printer.lookup_object('gcode')
gcode.request_restart('restart')
######################################################################
# Config validation (check for undefined options)
######################################################################
class ConfigValidate:
def __init__(self, printer):
self.printer = printer
self.status_settings = {}
self.access_tracking = {}
self.autosave_options = {}
def start_access_tracking(self, autosave_fileconfig):
# Note autosave options for use during undefined options check
self.autosave_options = {}
for section in autosave_fileconfig.sections():
for option in autosave_fileconfig.options(section):
self.autosave_options[(section.lower(), option.lower())] = 1
self.access_tracking = {}
return self.access_tracking
def check_unused(self, fileconfig):
# Don't warn on fields set in autosave segment
access_tracking = dict(self.access_tracking)
access_tracking.update(self.autosave_options)
# Note locally used sections
valid_sections = { s: 1 for s, o in self.printer.lookup_objects() }
valid_sections.update({ s: 1 for s, o in access_tracking })
# Validate that there are no undefined parameters in the config file
for section_name in fileconfig.sections():
section = section_name.lower()
if section not in valid_sections:
raise error("Section '%s' is not a valid config section"
% (section,))
for option in fileconfig.options(section_name):
option = option.lower()
if (section, option) not in access_tracking:
raise error("Option '%s' is not valid in section '%s'"
% (option, section))
# Setup get_status()
self._build_status_settings()
# Clear tracking state
self.access_tracking.clear()
self.autosave_options.clear()
def _build_status_settings(self):
self.status_settings = {}
for (section, option), value in self.access_tracking.items():
self.status_settings.setdefault(section, {})[option] = value
def get_status(self, eventtime):
return {'settings': self.status_settings}
######################################################################
# Main printer config tracking
######################################################################
class PrinterConfig:
def __init__(self, printer):
self.printer = printer
self.autosave = ConfigAutoSave(printer)
self.validate = ConfigValidate(printer)
self.deprecated = {}
self.runtime_warnings = []
self.deprecate_warnings = []
self.status_raw_config = {}
self.status_warnings = []
def get_printer(self):
return self.printer
def read_config(self, filename):
cfgrdr = ConfigFileReader()
data = cfgrdr.read_config_file(filename)
fileconfig = cfgrdr.build_fileconfig(data, filename)
return ConfigWrapper(self.printer, fileconfig, {}, 'printer')
def read_main_config(self):
fileconfig, autosave_fileconfig = self.autosave.load_main_config()
access_tracking = self.validate.start_access_tracking(
autosave_fileconfig)
config = ConfigWrapper(self.printer, fileconfig,
access_tracking, 'printer')
self._build_status_config(config)
return config
def log_config(self, config):
cfgrdr = ConfigFileReader()
lines = ["===== Config file =====",
cfgrdr.build_config_string(config.fileconfig),
"======================="]
self.printer.set_rollover_info("config", "\n".join(lines))
def check_unused_options(self, config):
self.validate.check_unused(config.fileconfig)
# Deprecation warnings
def runtime_warning(self, msg):
logging.warning(msg)
res = {'type': 'runtime_warning', 'message': msg}
self.runtime_warnings.append(res)
self.status_warnings = self.runtime_warnings + self.deprecate_warnings
def deprecate(self, section, option, value=None, msg=None):
key = (section, option, value)
if key in self.deprecated and self.deprecated[key] == msg:
return
self.deprecated[key] = msg
self.deprecate_warnings = []
for (section, option, value), msg in self.deprecated.items():
if value is None:
res = {'type': 'deprecated_option'}
else:
res = {'type': 'deprecated_value', 'value': value}
res['message'] = msg
res['section'] = section
res['option'] = option
self.deprecate_warnings.append(res)
self.status_warnings = self.runtime_warnings + self.deprecate_warnings
# Status reporting
def _build_status_config(self, config):
self.status_raw_config = {}
for section in config.get_prefix_sections(''):
self.status_raw_config[section.get_name()] = section_status = {}
for option in section.get_prefix_options(''):
section_status[option] = section.get(option, note_valid=False)
def get_status(self, eventtime):
status = {'config': self.status_raw_config,
'warnings': self.status_warnings}
status.update(self.autosave.get_status(eventtime))
status.update(self.validate.get_status(eventtime))
return status
# Autosave functions
def set(self, section, option, value):
self.autosave.set(section, option, value)
def remove_section(self, section):
self.autosave.remove_section(section)

View File

@@ -7,7 +7,6 @@
SAMPLE_TIME = 0.001
SAMPLE_COUNT = 8
REPORT_TIME = 0.300
RANGE_CHECK_COUNT = 4
class MCU_scaled_adc:
def __init__(self, main, pin_params):
@@ -18,7 +17,7 @@ class MCU_scaled_adc:
qname = main.name + ":" + pin_params['pin']
query_adc.register_adc(qname, self._mcu_adc)
self._callback = None
self.setup_minmax = self._mcu_adc.setup_minmax
self.setup_adc_sample = self._mcu_adc.setup_adc_sample
self.get_mcu = self._mcu_adc.get_mcu
def _handle_callback(self, read_time, read_value):
max_adc = self._main.last_vref[1]
@@ -54,8 +53,7 @@ class PrinterADCScaled:
ppins = self.printer.lookup_object('pins')
mcu_adc = ppins.setup_pin('adc', pin_name)
mcu_adc.setup_adc_callback(REPORT_TIME, callback)
mcu_adc.setup_minmax(SAMPLE_TIME, SAMPLE_COUNT, minval=0., maxval=1.,
range_check_count=RANGE_CHECK_COUNT)
mcu_adc.setup_adc_sample(SAMPLE_TIME, SAMPLE_COUNT)
query_adc = config.get_printer().load_object(config, 'query_adc')
query_adc.register_adc(self.name + ":" + name, mcu_adc)
return mcu_adc

View File

@@ -1,6 +1,6 @@
# Obtain temperature using linear interpolation of ADC values
#
# Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2016-2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging, bisect
@@ -22,8 +22,8 @@ class PrinterADCtoTemperature:
ppins = config.get_printer().lookup_object('pins')
self.mcu_adc = ppins.setup_pin('adc', config.get('sensor_pin'))
self.mcu_adc.setup_adc_callback(REPORT_TIME, self.adc_callback)
query_adc = config.get_printer().load_object(config, 'query_adc')
query_adc.register_adc(config.get_name(), self.mcu_adc)
self.diag_helper = HelperTemperatureDiagnostics(
config, self.mcu_adc, adc_convert.calc_temp)
def setup_callback(self, temperature_callback):
self.temperature_callback = temperature_callback
def get_report_time_delta(self):
@@ -32,10 +32,44 @@ class PrinterADCtoTemperature:
temp = self.adc_convert.calc_temp(read_value)
self.temperature_callback(read_time + SAMPLE_COUNT * SAMPLE_TIME, temp)
def setup_minmax(self, min_temp, max_temp):
adc_range = [self.adc_convert.calc_adc(t) for t in [min_temp, max_temp]]
self.mcu_adc.setup_minmax(SAMPLE_TIME, SAMPLE_COUNT,
minval=min(adc_range), maxval=max(adc_range),
range_check_count=RANGE_CHECK_COUNT)
arange = [self.adc_convert.calc_adc(t) for t in [min_temp, max_temp]]
min_adc, max_adc = sorted(arange)
self.mcu_adc.setup_adc_sample(SAMPLE_TIME, SAMPLE_COUNT,
minval=min_adc, maxval=max_adc,
range_check_count=RANGE_CHECK_COUNT)
self.diag_helper.setup_diag_minmax(min_temp, max_temp, min_adc, max_adc)
# Tool to register with query_adc and report extra info on ADC range errors
class HelperTemperatureDiagnostics:
def __init__(self, config, mcu_adc, calc_temp_cb):
self.printer = config.get_printer()
self.name = config.get_name()
self.mcu_adc = mcu_adc
self.calc_temp_cb = calc_temp_cb
self.min_temp = self.max_temp = self.min_adc = self.max_adc = None
query_adc = self.printer.load_object(config, 'query_adc')
query_adc.register_adc(self.name, self.mcu_adc)
error_mcu = self.printer.load_object(config, 'error_mcu')
error_mcu.add_clarify("ADC out of range", self._clarify_adc_range)
def setup_diag_minmax(self, min_temp, max_temp, min_adc, max_adc):
self.min_temp, self.max_temp = min_temp, max_temp
self.min_adc, self.max_adc = min_adc, max_adc
def _clarify_adc_range(self, msg, details):
if self.min_temp is None:
return None
last_value, last_read_time = self.mcu_adc.get_last_value()
if not last_read_time:
return None
if last_value >= self.min_adc and last_value <= self.max_adc:
return None
tempstr = "?"
try:
last_temp = self.calc_temp_cb(last_value)
tempstr = "%.3f" % (last_temp,)
except e:
logging.exception("Error in calc_temp callback")
return ("Sensor '%s' temperature %s not in range %.3f:%.3f"
% (self.name, tempstr, self.min_temp, self.max_temp))
######################################################################
@@ -95,8 +129,8 @@ class LinearVoltage:
for temp, volt in params:
adc = (volt - voltage_offset) / adc_voltage
if adc < 0. or adc > 1.:
logging.warn("Ignoring adc sample %.3f/%.3f in heater %s",
temp, volt, config.get_name())
logging.warning("Ignoring adc sample %.3f/%.3f in heater %s",
temp, volt, config.get_name())
continue
samples.append((adc, temp))
try:

216
klippy/extras/ads1220.py Normal file
View File

@@ -0,0 +1,216 @@
# ADS1220 Support
#
# Copyright (C) 2024 Gareth Farrington <gareth@waves.ky>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
from . import bulk_sensor, bus
#
# Constants
#
BYTES_PER_SAMPLE = 4 # samples are 4 byte wide unsigned integers
MAX_SAMPLES_PER_MESSAGE = bulk_sensor.MAX_BULK_MSG_SIZE // BYTES_PER_SAMPLE
UPDATE_INTERVAL = 0.10
RESET_CMD = 0x06
START_SYNC_CMD = 0x08
RREG_CMD = 0x20
WREG_CMD = 0x40
NOOP_CMD = 0x0
RESET_STATE = bytearray([0x0, 0x0, 0x0, 0x0])
# turn bytearrays into pretty hex strings: [0xff, 0x1]
def hexify(byte_array):
return "[%s]" % (", ".join([hex(b) for b in byte_array]))
class ADS1220:
def __init__(self, config):
self.printer = printer = config.get_printer()
self.name = config.get_name().split()[-1]
self.last_error_count = 0
self.consecutive_fails = 0
# Chip options
# Gain
self.gain_options = {'1': 0x0, '2': 0x1, '4': 0x2, '8': 0x3, '16': 0x4,
'32': 0x5, '64': 0x6, '128': 0x7}
self.gain = config.getchoice('gain', self.gain_options, default='128')
# Sample rate
self.sps_normal = {'20': 20, '45': 45, '90': 90, '175': 175,
'330': 330, '600': 600, '1000': 1000}
self.sps_turbo = {'40': 40, '90': 90, '180': 180, '350': 350,
'660': 660, '1200': 1200, '2000': 2000}
self.sps_options = self.sps_normal.copy()
self.sps_options.update(self.sps_turbo)
self.sps = config.getchoice('sample_rate', self.sps_options,
default='660')
self.is_turbo = str(self.sps) in self.sps_turbo
# Input multiplexer: AINP and AINN
mux_options = {'AIN0_AIN1': 0b0000, 'AIN0_AIN2': 0b0001,
'AIN0_AIN3': 0b0010, 'AIN1_AIN2': 0b0011,
'AIN1_AIN3': 0b0100, 'AIN2_AIN3': 0b0101,
'AIN1_AIN0': 0b0110, 'AIN3_AIN2': 0b0111,
'AIN0_AVSS': 0b1000, 'AIN1_AVSS': 0b1001,
'AIN2_AVSS': 0b1010, 'AIN3_AVSS': 0b1011}
self.mux = config.getchoice('input_mux', mux_options,
default='AIN0_AIN1')
# PGA Bypass
self.pga_bypass = config.getboolean('pga_bypass', default=False)
# bypass PGA when AVSS is the negative input
force_pga_bypass = self.mux >= 0b1000
self.pga_bypass = force_pga_bypass or self.pga_bypass
# Voltage Reference
self.vref_options = {'internal': 0b0, 'REF0': 0b01, 'REF1': 0b10,
'analog_supply': 0b11}
self.vref = config.getchoice('vref', self.vref_options,
default='internal')
# check for conflict between REF1 and AIN0/AIN3
mux_conflict = [0b0000, 0b0001, 0b0010, 0b0100, 0b0101, 0b0110, 0b0111,
0b1000, 0b1011]
if self.vref == 0b10 and self.mux in mux_conflict:
raise config.error("ADS1220 config error: AIN0/REFP1 and AIN3/REFN1"
" cant be used as a voltage reference and"
" an input at the same time")
# SPI Setup
spi_speed = 512000 if self.is_turbo else 256000
self.spi = bus.MCU_SPI_from_config(config, 1, default_speed=spi_speed)
self.mcu = mcu = self.spi.get_mcu()
self.oid = mcu.create_oid()
# Data Ready (DRDY) Pin
drdy_pin = config.get('data_ready_pin')
ppins = printer.lookup_object('pins')
drdy_ppin = ppins.lookup_pin(drdy_pin)
self.data_ready_pin = drdy_ppin['pin']
drdy_pin_mcu = drdy_ppin['chip']
if drdy_pin_mcu != self.mcu:
raise config.error("ADS1220 config error: SPI communication and"
" data_ready_pin must be on the same MCU")
# Bulk Sensor Setup
self.bulk_queue = bulk_sensor.BulkDataQueue(self.mcu, oid=self.oid)
# Clock tracking
chip_smooth = self.sps * UPDATE_INTERVAL * 2
# Measurement conversion
self.ffreader = bulk_sensor.FixedFreqReader(mcu, chip_smooth, "<i")
# Process messages in batches
self.batch_bulk = bulk_sensor.BatchBulkHelper(
self.printer, self._process_batch, self._start_measurements,
self._finish_measurements, UPDATE_INTERVAL)
# publish raw samples to the socket
hdr = {'header': ('time', 'counts', 'value')}
self.batch_bulk.add_mux_endpoint("ads1220/dump_ads1220", "sensor",
self.name, hdr)
# Command Configuration
mcu.add_config_cmd(
"config_ads1220 oid=%d spi_oid=%d data_ready_pin=%s"
% (self.oid, self.spi.get_oid(), self.data_ready_pin))
mcu.add_config_cmd("query_ads1220 oid=%d rest_ticks=0"
% (self.oid,), on_restart=True)
mcu.register_config_callback(self._build_config)
self.query_ads1220_cmd = None
def _build_config(self):
cmdqueue = self.spi.get_command_queue()
self.query_ads1220_cmd = self.mcu.lookup_command(
"query_ads1220 oid=%c rest_ticks=%u", cq=cmdqueue)
self.ffreader.setup_query_command("query_ads1220_status oid=%c",
oid=self.oid, cq=cmdqueue)
def get_mcu(self):
return self.mcu
def get_samples_per_second(self):
return self.sps
# returns a tuple of the minimum and maximum value of the sensor, used to
# detect if a data value is saturated
def get_range(self):
return -0x800000, 0x7FFFFF
# add_client interface, direct pass through to bulk_sensor API
def add_client(self, callback):
self.batch_bulk.add_client(callback)
# Measurement decoding
def _convert_samples(self, samples):
adc_factor = 1. / (1 << 23)
count = 0
for ptime, val in samples:
samples[count] = (round(ptime, 6), val, round(val * adc_factor, 9))
count += 1
del samples[count:]
# Start, stop, and process message batches
def _start_measurements(self):
self.last_error_count = 0
self.consecutive_fails = 0
# Start bulk reading
self.reset_chip()
self.setup_chip()
rest_ticks = self.mcu.seconds_to_clock(1. / (10. * self.sps))
self.query_ads1220_cmd.send([self.oid, rest_ticks])
logging.info("ADS1220 starting '%s' measurements", self.name)
# Initialize clock tracking
self.ffreader.note_start()
def _finish_measurements(self):
# don't use serial connection after shutdown
if self.printer.is_shutdown():
return
# Halt bulk reading
self.query_ads1220_cmd.send_wait_ack([self.oid, 0])
self.ffreader.note_end()
logging.info("ADS1220 finished '%s' measurements", self.name)
def _process_batch(self, eventtime):
samples = self.ffreader.pull_samples()
self._convert_samples(samples)
return {'data': samples, 'errors': self.last_error_count,
'overflows': self.ffreader.get_last_overflows()}
def reset_chip(self):
# the reset command takes 50us to complete
self.send_command(RESET_CMD)
# read startup register state and validate
val = self.read_reg(0x0, 4)
if val != RESET_STATE:
raise self.printer.command_error(
"Invalid ads1220 reset state (got %s vs %s).\n"
"This is generally indicative of connection problems\n"
"(e.g. faulty wiring) or a faulty ADS1220 chip."
% (hexify(val), hexify(RESET_STATE)))
def setup_chip(self):
continuous = 0x1 # enable continuous conversions
mode = 0x2 if self.is_turbo else 0x0 # turbo mode
sps_list = self.sps_turbo if self.is_turbo else self.sps_normal
data_rate = list(sps_list.keys()).index(str(self.sps))
reg_values = [(self.mux << 4) | (self.gain << 1) | int(self.pga_bypass),
(data_rate << 5) | (mode << 3) | (continuous << 2),
(self.vref << 6),
0x0]
self.write_reg(0x0, reg_values)
# start measurements immediately
self.send_command(START_SYNC_CMD)
def read_reg(self, reg, byte_count):
read_command = [RREG_CMD | (reg << 2) | (byte_count - 1)]
read_command += [NOOP_CMD] * byte_count
params = self.spi.spi_transfer(read_command)
return bytearray(params['response'][1:])
def send_command(self, cmd):
self.spi.spi_send([cmd])
def write_reg(self, reg, register_bytes):
write_command = [WREG_CMD | (reg << 2) | (len(register_bytes) - 1)]
write_command.extend(register_bytes)
self.spi.spi_send(write_command)
stored_val = self.read_reg(reg, len(register_bytes))
if bytearray(register_bytes) != stored_val:
raise self.printer.command_error(
"Failed to set ADS1220 register [0x%x] to %s: got %s. "
"This may be a connection problem (e.g. faulty wiring)" % (
reg, hexify(register_bytes), hexify(stored_val)))
ADS1220_SENSOR_TYPE = {"ads1220": ADS1220}

393
klippy/extras/ads1x1x.py Normal file
View File

@@ -0,0 +1,393 @@
# Support for I2C based ADS1013, ADS1014, ADS1015, ADS1113, ADS1114 and ADS1115
#
# Copyright (C) 2024 Konstantin Koch <korsarnek@gmail.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
import pins
from . import bus
# Supported chip types
ADS1X1X_CHIP_TYPE = {
'ADS1013': 3,
'ADS1014': 4,
'ADS1015': 5,
'ADS1113': 13,
'ADS1114': 14,
'ADS1115': 15
}
def isADS101X(chip):
return (chip == ADS1X1X_CHIP_TYPE['ADS1013'] \
or chip == ADS1X1X_CHIP_TYPE['ADS1014'] \
or chip == ADS1X1X_CHIP_TYPE['ADS1015'])
def isADS111X(chip):
return (chip == ADS1X1X_CHIP_TYPE['ADS1113'] \
or chip == ADS1X1X_CHIP_TYPE['ADS1114'] \
or chip == ADS1X1X_CHIP_TYPE['ADS1115'])
# Address is defined by how the address pin is wired
ADS1X1X_CHIP_ADDR = {
'GND': 0x48,
'VCC': 0x49,
'SDA': 0x4a,
'SCL': 0x4b
}
# Chip "pointer" registers
ADS1X1X_REG_POINTER_MASK = 0x03
ADS1X1X_REG_POINTER = {
'CONVERSION': 0x00,
'CONFIG': 0x01,
'LO_THRESH': 0x02,
'HI_THRESH': 0x03
}
# Config register masks
ADS1X1X_REG_CONFIG = {
'OS_MASK': 0x8000,
'MULTIPLEXER_MASK': 0x7000,
'PGA_MASK': 0x0E00,
'MODE_MASK': 0x0100,
'DATA_RATE_MASK': 0x00E0,
'COMPARATOR_MODE_MASK': 0x0010,
'COMPARATOR_POLARITY_MASK': 0x0008,
# Determines if ALERT/RDY pin latches once asserted
'COMPARATOR_LATCHING_MASK': 0x0004,
'COMPARATOR_QUEUE_MASK': 0x0003
}
#
# The following enums are to be used with the configuration functions.
#
ADS1X1X_OS = {
'OS_IDLE': 0x8000, # Device is not performing a conversion
'OS_SINGLE': 0x8000 # Single-conversion
}
ADS1X1X_MUX = {
'DIFF01': 0x0000, # Differential P = AIN0, N = AIN1 0
'DIFF03': 0x1000, # Differential P = AIN0, N = AIN3 4096
'DIFF13': 0x2000, # Differential P = AIN1, N = AIN3 8192
'DIFF23': 0x3000, # Differential P = AIN2, N = AIN3 12288
'AIN0': 0x4000, # Single-ended (ADS1015: AIN0 16384)
'AIN1': 0x5000, # Single-ended (ADS1015: AIN1 20480)
'AIN2': 0x6000, # Single-ended (ADS1015: AIN2 24576)
'AIN3': 0x7000 # Single-ended (ADS1015: AIN3 28672)
}
ADS1X1X_PGA = {
'6.144V': 0x0000, # +/-6.144V range = Gain 2/3
'4.096V': 0x0200, # +/-4.096V range = Gain 1
'2.048V': 0x0400, # +/-2.048V range = Gain 2
'1.024V': 0x0600, # +/-1.024V range = Gain 4
'0.512V': 0x0800, # +/-0.512V range = Gain 8
'0.256V': 0x0A00 # +/-0.256V range = Gain 16
}
ADS1X1X_PGA_VALUE = {
0x0000: 6.144,
0x0200: 4.096,
0x0400: 2.048,
0x0600: 1.024,
0x0800: 0.512,
0x0A00: 0.256,
}
ADS111X_RESOLUTION = 32767.0
ADS111X_PGA_SCALAR = {
0x0000: 6.144 / ADS111X_RESOLUTION, # +/-6.144V range = Gain 2/3
0x0200: 4.096 / ADS111X_RESOLUTION, # +/-4.096V range = Gain 1
0x0400: 2.048 / ADS111X_RESOLUTION, # +/-2.048V range = Gain 2
0x0600: 1.024 / ADS111X_RESOLUTION, # +/-1.024V range = Gain 4
0x0800: 0.512 / ADS111X_RESOLUTION, # +/-0.512V range = Gain 8
0x0A00: 0.256 / ADS111X_RESOLUTION # +/-0.256V range = Gain 16
}
ADS101X_RESOLUTION = 2047.0
ADS101X_PGA_SCALAR = {
0x0000: 6.144 / ADS101X_RESOLUTION, # +/-6.144V range = Gain 2/3
0x0200: 4.096 / ADS101X_RESOLUTION, # +/-4.096V range = Gain 1
0x0400: 2.048 / ADS101X_RESOLUTION, # +/-2.048V range = Gain 2
0x0600: 1.024 / ADS101X_RESOLUTION, # +/-1.024V range = Gain 4
0x0800: 0.512 / ADS101X_RESOLUTION, # +/-0.512V range = Gain 8
0x0A00: 0.256 / ADS101X_RESOLUTION # +/-0.256V range = Gain 16
}
ADS1X1X_MODE = {
'continuous': 0x0000, # Continuous conversion mode
'single': 0x0100 # Power-down single-shot mode
}
# Lesser samples per second means it takes and averages more samples before
# returning a result.
ADS101X_SAMPLES_PER_SECOND = {
'128': 0x0000, # 128 samples per second
'250': 0x0020, # 250 samples per second
'490': 0x0040, # 490 samples per second
'920': 0x0060, # 920 samples per second
'1600': 0x0080, # 1600 samples per second
'2400': 0x00a0, # 2400 samples per second
'3300': 0x00c0, # 3300 samples per second
}
ADS111X_SAMPLES_PER_SECOND = {
'8': 0x0000, # 8 samples per second
'16': 0x0020, # 16 samples per second
'32': 0x0040, # 32 samples per second
'64': 0x0060, # 64 samples per second
'128': 0x0080, # 128 samples per second
'250': 0x00a0, # 250 samples per second
'475': 0x00c0, # 475 samples per second
'860': 0x00e0 # 860 samples per second
}
ADS1X1X_COMPARATOR_MODE = {
'TRADITIONAL': 0x0000, # Traditional comparator with hysteresis
'WINDOW': 0x0010 # Window comparator
}
ADS1X1X_COMPARATOR_POLARITY = {
'ACTIVE_LO': 0x0000, # ALERT/RDY pin is low when active
'ACTIVE_HI': 0x0008 # ALERT/RDY pin is high when active
}
ADS1X1X_COMPARATOR_LATCHING = {
'NON_LATCHING': 0x0000, # Non-latching comparator
'LATCHING': 0x0004 # Latching comparator
}
ADS1X1X_COMPARATOR_QUEUE = {
'QUEUE_1': 0x0000, # Assert ALERT/RDY after one conversions
'QUEUE_2': 0x0001, # Assert ALERT/RDY after two conversions
'QUEUE_4': 0x0002, # Assert ALERT/RDY after four conversions
'QUEUE_NONE': 0x0003 # Disable the comparator and put ALERT/RDY
# in high state
}
ADS1X1_OPERATIONS = {
'SET_MUX': 0,
'READ_CONVERSION': 1
}
class ADS1X1X_chip:
def __init__(self, config):
self._printer = config.get_printer()
self._reactor = self._printer.get_reactor()
self.name = config.get_name().split()[-1]
self.chip = config.getchoice('chip', ADS1X1X_CHIP_TYPE)
address = ADS1X1X_CHIP_ADDR['GND']
# If none is specified, i2c_address can be used for a specific address
if config.get('address_pin', None) is not None:
address = config.getchoice('address_pin', ADS1X1X_CHIP_ADDR)
self._ppins = self._printer.lookup_object("pins")
self._ppins.register_chip(self.name, self)
self.pga = config.getchoice('pga', ADS1X1X_PGA, '4.096V')
self.adc_voltage = config.getfloat('adc_voltage', above=0., default=3.3)
# Comparators are not implemented, they would only be useful if the
# alert pin is used, which we haven't made configurable.
# But that wouldn't be useful for a normal temperature sensor anyway.
self.comp_mode = ADS1X1X_COMPARATOR_MODE['TRADITIONAL']
self.comp_polarity = ADS1X1X_COMPARATOR_POLARITY['ACTIVE_LO']
self.comp_latching = ADS1X1X_COMPARATOR_LATCHING['NON_LATCHING']
self.comp_queue = ADS1X1X_COMPARATOR_QUEUE['QUEUE_NONE']
self._i2c = bus.MCU_I2C_from_config(config, address)
self.mcu = self._i2c.get_mcu()
self._printer.add_object("ads1x1x " + self.name, self)
self._printer.register_event_handler("klippy:connect", \
self._handle_connect)
self._pins = {}
self._mutex = self._reactor.mutex()
def setup_pin(self, pin_type, pin_params):
pin = pin_params['pin']
if pin_type == 'adc':
if (pin not in ADS1X1X_MUX):
raise pins.error('ADS1x1x pin %s is not valid' % \
pin_params['pin'])
config = 0
config |= (ADS1X1X_OS['OS_SINGLE'] & \
ADS1X1X_REG_CONFIG['OS_MASK'])
config |= (ADS1X1X_MUX[pin_params['pin']] & \
ADS1X1X_REG_CONFIG['MULTIPLEXER_MASK'])
config |= (self.pga & ADS1X1X_REG_CONFIG['PGA_MASK'])
# Have to use single mode, because in continuous, it never reaches
# idle state, which we use to determine if the sampling is done.
config |= (ADS1X1X_MODE['single'] & \
ADS1X1X_REG_CONFIG['MODE_MASK'])
# lowest sample rate per default, until report time has been set in
# setup_adc_sample
config |= (self.comp_mode \
& ADS1X1X_REG_CONFIG['COMPARATOR_MODE_MASK'])
config |= (self.comp_polarity \
& ADS1X1X_REG_CONFIG['COMPARATOR_POLARITY_MASK'])
config |= (self.comp_latching \
& ADS1X1X_REG_CONFIG['COMPARATOR_LATCHING_MASK'])
config |= (self.comp_queue \
& ADS1X1X_REG_CONFIG['COMPARATOR_QUEUE_MASK'])
pin_obj = ADS1X1X_pin(self, config)
if pin in self._pins:
raise pins.error(
'pin %s for chip %s is used multiple times' \
% (pin, self.name))
self._pins[pin] = pin_obj
return pin_obj
raise pins.error('Wrong pin or incompatible type: %s with type %s! ' % (
pin, pin_type))
def _handle_connect(self):
try:
# Init all devices on bus for this kind of device
self._i2c.i2c_write([0x06, 0x00, 0x00])
except Exception:
logging.exception("ADS1X1X: error while resetting device")
def is_ready(self):
config = self._read_register(ADS1X1X_REG_POINTER['CONFIG'])
return bool((config & ADS1X1X_REG_CONFIG['OS_MASK']) == \
ADS1X1X_OS['OS_IDLE'])
def calculate_sample_rate(self):
pin_count = len(self._pins)
lowest_report_time = 1
for pin in self._pins.values():
lowest_report_time = min(lowest_report_time, pin.report_time)
sample_rate = 1 / lowest_report_time * pin_count
samples_per_second = ADS111X_SAMPLES_PER_SECOND
if isADS101X(self.chip):
samples_per_second = ADS101X_SAMPLES_PER_SECOND
# make sure the samples list is sorted correctly by number.
samples_per_second = sorted(samples_per_second.items(), \
key=lambda t: int(t[0]))
for rate, bits in samples_per_second:
rate_number = int(rate)
if sample_rate <= rate_number:
return (rate_number, bits)
logging.warning(
"ADS1X1X: requested sample rate %s is higher than supported by %s."\
% (sample_rate, self.name))
return (rate_number, bits)
def handle_report_time_update(self):
(sample_rate, sample_rate_bits) = self.calculate_sample_rate()
for pin in self._pins.values():
pin.config = (pin.config & ~ADS1X1X_REG_CONFIG['DATA_RATE_MASK']) \
| (sample_rate_bits & ADS1X1X_REG_CONFIG['DATA_RATE_MASK'])
self.delay = 1 / float(sample_rate)
def sample(self, pin):
with self._mutex:
try:
self._write_register(ADS1X1X_REG_POINTER['CONFIG'], pin.config)
self._reactor.pause(self._reactor.monotonic() + self.delay)
start_time = self._reactor.monotonic()
while not self.is_ready():
self._reactor.pause(self._reactor.monotonic() + 0.001)
# if we waited twice the expected time, mark this an error
if start_time + self.delay < self._reactor.monotonic():
logging.warning("ADS1X1X: timeout during sampling")
return None
return self._read_register(ADS1X1X_REG_POINTER['CONVERSION'])
except Exception as e:
logging.exception("ADS1X1X: error while sampling: %s" % str(e))
return None
def _read_register(self, reg):
# read a single register
params = self._i2c.i2c_read([reg], 2)
buff = bytearray(params['response'])
return (buff[0]<<8 | buff[1])
def _write_register(self, reg, data):
data = [
(reg & 0xFF), # Control register
((data>>8) & 0xFF), # High byte
(data & 0xFF), # Lo byte
]
self._i2c.i2c_write(data)
class ADS1X1X_pin:
def __init__(self, chip, config):
self.mcu = chip.mcu
self.chip = chip
self.config = config
self.invalid_count = 0
self.chip._printer.register_event_handler("klippy:connect", \
self._handle_connect)
def _handle_connect(self):
self._reactor = self.chip._printer.get_reactor()
self._sample_timer = \
self._reactor.register_timer(self._process_sample, \
self._reactor.NOW)
def _process_sample(self, eventtime):
sample = self.chip.sample(self)
if sample is not None:
# The sample is encoded in the top 12 or full 16 bits
# Value's meaning is defined by ADS1X1X_REG_CONFIG['PGA_MASK']
if isADS101X(self.chip.chip):
sample >>= 4
target_value = sample / ADS101X_RESOLUTION
else:
target_value = sample / ADS111X_RESOLUTION
# Thermistors expect a value between 0 and 1 to work. If we use a
# PGA with 4.096V but supply only 3.3V, the reference voltage for
# voltage divider is only 3.3V, not 4.096V. So we remap the range
# from what the PGA allows as range to end up between 0 and 1 for
# the thermistor logic to work as expected.
target_value = target_value * (ADS1X1X_PGA_VALUE[self.chip.pga] / \
self.chip.adc_voltage)
if target_value > self.maxval or target_value < self.minval:
self.invalid_count = self.invalid_count + 1
logging.warning("ADS1X1X: temperature outside range")
self.check_invalid()
else:
self.invalid_count = 0
# Publish result
measured_time = self._reactor.monotonic()
self.callback(self.chip.mcu.estimated_print_time(measured_time),
target_value)
else:
self.invalid_count = self.invalid_count + 1
self.check_invalid()
return eventtime + self.report_time
def check_invalid(self):
if self.invalid_count > self.range_check_count:
self.chip._printer.invoke_shutdown(
"ADS1X1X temperature check failed")
def get_mcu(self):
return self.mcu
def setup_adc_callback(self, report_time, callback):
self.report_time = report_time
self.callback = callback
self.chip.handle_report_time_update()
def setup_adc_sample(self, sample_time, sample_count,
minval=0., maxval=1., range_check_count=0):
self.minval = minval
self.maxval = maxval
self.range_check_count = range_check_count
def load_config_prefix(config):
return ADS1X1X_chip(config)

View File

@@ -1,10 +1,10 @@
# Support for reading acceleration data from an adxl345 chip
#
# Copyright (C) 2020-2021 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2020-2023 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
import logging, time, collections, multiprocessing, os
from . import bus, bulk_sensor
# ADXL345 registers
REG_DEVID = 0x00
@@ -32,26 +32,29 @@ Accel_Measurement = collections.namedtuple(
# Helper class to obtain measurements
class AccelQueryHelper:
def __init__(self, printer, cconn):
def __init__(self, printer):
self.printer = printer
self.cconn = cconn
self.is_finished = False
print_time = printer.lookup_object('toolhead').get_last_move_time()
self.request_start_time = self.request_end_time = print_time
self.samples = self.raw_samples = []
self.msgs = []
self.samples = []
def finish_measurements(self):
toolhead = self.printer.lookup_object('toolhead')
self.request_end_time = toolhead.get_last_move_time()
toolhead.wait_moves()
self.cconn.finalize()
def _get_raw_samples(self):
raw_samples = self.cconn.get_messages()
if raw_samples:
self.raw_samples = raw_samples
return self.raw_samples
self.is_finished = True
def handle_batch(self, msg):
if self.is_finished:
return False
if len(self.msgs) >= 10000:
# Avoid filling up memory with too many samples
return False
self.msgs.append(msg)
return True
def has_valid_samples(self):
raw_samples = self._get_raw_samples()
for msg in raw_samples:
data = msg['params']['data']
for msg in self.msgs:
data = msg['data']
first_sample_time = data[0][0]
last_sample_time = data[-1][0]
if (first_sample_time > self.request_end_time
@@ -60,21 +63,20 @@ class AccelQueryHelper:
# The time intervals [first_sample_time, last_sample_time]
# and [request_start_time, request_end_time] have non-zero
# intersection. It is still theoretically possible that none
# of the samples from raw_samples fall into the time interval
# of the samples from msgs fall into the time interval
# [request_start_time, request_end_time] if it is too narrow
# or on very heavy data losses. In practice, that interval
# is at least 1 second, so this possibility is negligible.
return True
return False
def get_samples(self):
raw_samples = self._get_raw_samples()
if not raw_samples:
if not self.msgs:
return self.samples
total = sum([len(m['params']['data']) for m in raw_samples])
total = sum([len(m['data']) for m in self.msgs])
count = 0
self.samples = samples = [None] * total
for msg in raw_samples:
for samp_time, x, y, z in msg['params']['data']:
for msg in self.msgs:
for samp_time, x, y, z in msg['data']:
if samp_time < self.request_start_time:
continue
if samp_time > self.request_end_time:
@@ -173,112 +175,54 @@ class AccelCommandHelper:
val = gcmd.get("VAL", minval=0, maxval=255, parser=lambda x: int(x, 0))
self.chip.set_reg(reg, val)
# Helper class for chip clock synchronization via linear regression
class ClockSyncRegression:
def __init__(self, mcu, chip_clock_smooth, decay = 1. / 20.):
self.mcu = mcu
self.chip_clock_smooth = chip_clock_smooth
self.decay = decay
self.last_chip_clock = self.last_exp_mcu_clock = 0.
self.mcu_clock_avg = self.mcu_clock_variance = 0.
self.chip_clock_avg = self.chip_clock_covariance = 0.
def reset(self, mcu_clock, chip_clock):
self.mcu_clock_avg = self.last_mcu_clock = mcu_clock
self.chip_clock_avg = chip_clock
self.mcu_clock_variance = self.chip_clock_covariance = 0.
self.last_chip_clock = self.last_exp_mcu_clock = 0.
def update(self, mcu_clock, chip_clock):
# Update linear regression
decay = self.decay
diff_mcu_clock = mcu_clock - self.mcu_clock_avg
self.mcu_clock_avg += decay * diff_mcu_clock
self.mcu_clock_variance = (1. - decay) * (
self.mcu_clock_variance + diff_mcu_clock**2 * decay)
diff_chip_clock = chip_clock - self.chip_clock_avg
self.chip_clock_avg += decay * diff_chip_clock
self.chip_clock_covariance = (1. - decay) * (
self.chip_clock_covariance + diff_mcu_clock*diff_chip_clock*decay)
def set_last_chip_clock(self, chip_clock):
base_mcu, base_chip, inv_cfreq = self.get_clock_translation()
self.last_chip_clock = chip_clock
self.last_exp_mcu_clock = base_mcu + (chip_clock-base_chip) * inv_cfreq
def get_clock_translation(self):
inv_chip_freq = self.mcu_clock_variance / self.chip_clock_covariance
if not self.last_chip_clock:
return self.mcu_clock_avg, self.chip_clock_avg, inv_chip_freq
# Find mcu clock associated with future chip_clock
s_chip_clock = self.last_chip_clock + self.chip_clock_smooth
scdiff = s_chip_clock - self.chip_clock_avg
s_mcu_clock = self.mcu_clock_avg + scdiff * inv_chip_freq
# Calculate frequency to converge at future point
mdiff = s_mcu_clock - self.last_exp_mcu_clock
s_inv_chip_freq = mdiff / self.chip_clock_smooth
return self.last_exp_mcu_clock, self.last_chip_clock, s_inv_chip_freq
def get_time_translation(self):
base_mcu, base_chip, inv_cfreq = self.get_clock_translation()
clock_to_print_time = self.mcu.clock_to_print_time
base_time = clock_to_print_time(base_mcu)
inv_freq = clock_to_print_time(base_mcu + inv_cfreq) - base_time
return base_time, base_chip, inv_freq
# Helper to read the axes_map parameter from the config
def read_axes_map(config, scale_x, scale_y, scale_z):
am = {'x': (0, scale_x), 'y': (1, scale_y), 'z': (2, scale_z),
'-x': (0, -scale_x), '-y': (1, -scale_y), '-z': (2, -scale_z)}
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 axes_map parameter")
return [am[a.strip()] for a in axes_map]
MIN_MSG_TIME = 0.100
BYTES_PER_SAMPLE = 5
SAMPLES_PER_BLOCK = 10
BATCH_UPDATES = 0.100
# Printer class that controls ADXL345 chip
class ADXL345:
def __init__(self, config):
self.printer = config.get_printer()
AccelCommandHelper(config, self)
self.query_rate = 0
am = {'x': (0, SCALE_XY), 'y': (1, SCALE_XY), 'z': (2, SCALE_Z),
'-x': (0, -SCALE_XY), '-y': (1, -SCALE_XY), '-z': (2, -SCALE_Z)}
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 adxl345 axes_map parameter")
self.axes_map = [am[a.strip()] for a in axes_map]
self.axes_map = read_axes_map(config, SCALE_XY, SCALE_XY, SCALE_Z)
self.data_rate = config.getint('rate', 3200)
if self.data_rate not in QUERY_RATES:
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_adxl345 bulk query code
self.spi = bus.MCU_SPI_from_config(config, 3, default_speed=5000000)
self.mcu = mcu = self.spi.get_mcu()
self.oid = oid = mcu.create_oid()
self.query_adxl345_cmd = self.query_adxl345_end_cmd = None
self.query_adxl345_status_cmd = None
self.query_adxl345_cmd = None
mcu.add_config_cmd("config_adxl345 oid=%d spi_oid=%d"
% (oid, self.spi.get_oid()))
mcu.add_config_cmd("query_adxl345 oid=%d clock=0 rest_ticks=0"
mcu.add_config_cmd("query_adxl345 oid=%d rest_ticks=0"
% (oid,), on_restart=True)
mcu.register_config_callback(self._build_config)
mcu.register_response(self._handle_adxl345_data, "adxl345_data", oid)
# Clock tracking
self.last_sequence = self.max_query_duration = 0
self.last_limit_count = self.last_error_count = 0
self.clock_sync = ClockSyncRegression(self.mcu, 640)
# API server endpoints
self.api_dump = motion_report.APIDumpHelper(
self.printer, self._api_update, self._api_startstop, 0.100)
# Bulk sample message reading
chip_smooth = self.data_rate * BATCH_UPDATES * 2
self.ffreader = bulk_sensor.FixedFreqReader(mcu, chip_smooth, "BBBBB")
self.last_error_count = 0
# Process messages in batches
self.batch_bulk = bulk_sensor.BatchBulkHelper(
self.printer, self._process_batch,
self._start_measurements, self._finish_measurements, BATCH_UPDATES)
self.name = config.get_name().split()[-1]
wh = self.printer.lookup_object('webhooks')
wh.register_mux_endpoint("adxl345/dump_adxl345", "sensor", self.name,
self._handle_dump_adxl345)
hdr = ('time', 'x_acceleration', 'y_acceleration', 'z_acceleration')
self.batch_bulk.add_mux_endpoint("adxl345/dump_adxl345", "sensor",
self.name, {'header': hdr})
def _build_config(self):
cmdqueue = self.spi.get_command_queue()
self.query_adxl345_cmd = self.mcu.lookup_command(
"query_adxl345 oid=%c clock=%u rest_ticks=%u", cq=cmdqueue)
self.query_adxl345_end_cmd = self.mcu.lookup_query_command(
"query_adxl345 oid=%c clock=%u rest_ticks=%u",
"adxl345_status oid=%c clock=%u query_ticks=%u next_sequence=%hu"
" buffered=%c fifo=%c limit_count=%hu", oid=self.oid, cq=cmdqueue)
self.query_adxl345_status_cmd = self.mcu.lookup_query_command(
"query_adxl345_status oid=%c",
"adxl345_status oid=%c clock=%u query_ticks=%u next_sequence=%hu"
" buffered=%c fifo=%c limit_count=%hu", oid=self.oid, cq=cmdqueue)
"query_adxl345 oid=%c rest_ticks=%u", cq=cmdqueue)
self.ffreader.setup_query_command("query_adxl345_status oid=%c",
oid=self.oid, cq=cmdqueue)
def read_reg(self, reg):
params = self.spi.spi_transfer([reg | REG_MOD_READ, 0x00])
response = bytearray(params['response'])
@@ -292,83 +236,31 @@ class ADXL345:
"This is generally indicative of connection problems "
"(e.g. faulty wiring) or a faulty adxl345 chip." % (
reg, val, stored_val))
# Measurement collection
def is_measuring(self):
return self.query_rate > 0
def _handle_adxl345_data(self, params):
with self.lock:
self.raw_samples.append(params)
def _extract_samples(self, raw_samples):
# Load variables to optimize inner loop below
def start_internal_client(self):
aqh = AccelQueryHelper(self.printer)
self.batch_bulk.add_client(aqh.handle_batch)
return aqh
# Measurement decoding
def _convert_samples(self, samples):
(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]
xlow, ylow, zlow, xzhigh, yzhigh = d_xyz
if yzhigh & 0x80:
self.last_error_count += 1
continue
rx = (xlow | ((xzhigh & 0x1f) << 8)) - ((xzhigh & 0x10) << 9)
ry = (ylow | ((yzhigh & 0x1f) << 8)) - ((yzhigh & 0x10) << 9)
rz = ((zlow | ((xzhigh & 0xe0) << 3) | ((yzhigh & 0xe0) << 6))
- ((yzhigh & 0x40) << 7))
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)
count = 0
for ptime, xlow, ylow, zlow, xzhigh, yzhigh in samples:
if yzhigh & 0x80:
self.last_error_count += 1
continue
rx = (xlow | ((xzhigh & 0x1f) << 8)) - ((xzhigh & 0x10) << 9)
ry = (ylow | ((yzhigh & 0x1f) << 8)) - ((yzhigh & 0x10) << 9)
rz = ((zlow | ((xzhigh & 0xe0) << 3) | ((yzhigh & 0xe0) << 6))
- ((yzhigh & 0x40) << 7))
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)
samples[count] = (round(ptime, 6), x, y, z)
count += 1
del samples[count:]
return samples
def _update_clock(self, minclock=0):
# Query current state
for retry in range(5):
params = self.query_adxl345_status_cmd.send([self.oid],
minclock=minclock)
fifo = params['fifo'] & 0x7f
if fifo <= 32:
break
else:
raise self.printer.command_error("Unable to query adxl345 fifo")
mcu_clock = self.mcu.clock32_to_clock64(params['clock'])
sequence = (self.last_sequence & ~0xffff) | params['next_sequence']
if sequence < self.last_sequence:
sequence += 0x10000
self.last_sequence = sequence
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 adxl345 hw processing time.
chip_clock = msg_count + 1
self.clock_sync.update(mcu_clock + duration // 2, chip_clock)
# Start, stop, and process message batches
def _start_measurements(self):
if self.is_measuring():
return
# In case of miswiring, testing ADXL345 device ID prevents treating
# noise or wrong signal as a correctly initialized device
dev_id = self.read_reg(REG_DEVID)
@@ -384,59 +276,27 @@ class ADXL345:
self.set_reg(REG_FIFO_CTL, 0x00)
self.set_reg(REG_BW_RATE, QUERY_RATES[self.data_rate])
self.set_reg(REG_FIFO_CTL, SET_FIFO_CTL)
# 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(4. / self.data_rate)
self.query_rate = self.data_rate
self.query_adxl345_cmd.send([self.oid, reqclock, rest_ticks],
reqclock=reqclock)
self.query_adxl345_cmd.send([self.oid, rest_ticks])
self.set_reg(REG_POWER_CTL, 0x08)
logging.info("ADXL345 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
self.ffreader.note_start()
self.last_error_count = 0
def _finish_measurements(self):
if not self.is_measuring():
return
# Halt bulk reading
params = self.query_adxl345_end_cmd.send([self.oid, 0, 0])
self.query_rate = 0
with self.lock:
self.raw_samples = []
self.set_reg(REG_POWER_CTL, 0x00)
self.query_adxl345_cmd.send_wait_ack([self.oid, 0])
self.ffreader.note_end()
logging.info("ADXL345 finished '%s' measurements", self.name)
# 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)
def _process_batch(self, eventtime):
samples = self.ffreader.pull_samples()
self._convert_samples(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_adxl345(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 AccelQueryHelper(self.printer, cconn)
'overflows': self.ffreader.get_last_overflows()}
def load_config(config):
return ADXL345(config)

View File

@@ -3,8 +3,8 @@
# Copyright (C) 2021,2022 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging, math, threading
from . import bus, motion_report
import logging, math
from . import bus, bulk_sensor
MIN_MSG_TIME = 0.100
TCODE_ERROR = 0xff
@@ -85,9 +85,9 @@ class AngleCalibration:
cal2 = calibration[bucket + 1]
adj = (angle & interp_mask) * (cal2 - cal1)
adj = cal1 + ((adj + interp_round) >> interp_bits)
angle_diff = (angle - adj) & 0xffff
angle_diff = (adj - angle) & 0xffff
angle_diff -= (angle_diff & 0x8000) << 1
new_angle = angle - angle_diff
new_angle = angle + angle_diff
if calibration_reversed:
new_angle = -new_angle
samples[i] = (samp_time, new_angle)
@@ -157,8 +157,14 @@ class AngleCalibration:
def do_calibration_moves(self):
move = self.printer.lookup_object('force_move').manual_move
# Start data collection
angle_sensor = self.printer.lookup_object(self.name)
cconn = angle_sensor.start_internal_client()
msgs = []
is_finished = False
def handle_batch(msg):
if is_finished:
return False
msgs.append(msg)
return True
self.printer.lookup_object(self.name).add_client(handle_batch)
# Move stepper several turns (to allow internal sensor calibration)
microsteps, full_steps = self.get_microsteps()
mcu_stepper = self.mcu_stepper
@@ -190,13 +196,12 @@ class AngleCalibration:
move(mcu_stepper, .5*rotation_dist + align_dist, move_speed)
toolhead.wait_moves()
# Finish data collection
cconn.finalize()
msgs = cconn.get_messages()
is_finished = True
# Correlate query responses
cal = {}
step = 0
for msg in msgs:
for query_time, pos in msg['params']['data']:
for query_time, pos in msg['data']:
# Add to step tracking
while step < len(times) and query_time > times[step][1]:
step += 1
@@ -375,9 +380,9 @@ class HelperTLE5012B:
mcu_clock, chip_clock = self._query_clock()
mdiff = mcu_clock - self.last_chip_mcu_clock
chip_mclock = self.last_chip_clock + int(mdiff * self.chip_freq + .5)
cdiff = (chip_mclock - chip_clock) & 0xffff
cdiff = (chip_clock - chip_mclock) & 0xffff
cdiff -= (cdiff & 0x8000) << 1
new_chip_clock = chip_mclock - cdiff
new_chip_clock = chip_mclock + cdiff
self.chip_freq = float(new_chip_clock - self.last_chip_clock) / mdiff
self.last_chip_clock = new_chip_clock
self.last_chip_mcu_clock = mcu_clock
@@ -406,7 +411,201 @@ class HelperTLE5012B:
parser=lambda x: int(x, 0))
self._write_reg(reg, val)
class HelperMT6816:
SPI_MODE = 3
SPI_SPEED = 10000000
def __init__(self, config, spi, oid):
self.printer = config.get_printer()
self.spi = spi
self.oid = oid
self.mcu = spi.get_mcu()
self.mcu.register_config_callback(self._build_config)
self.spi_angle_transfer_cmd = None
self.is_tcode_absolute = False
self.last_temperature = None
name = config.get_name().split()[-1]
gcode = self.printer.lookup_object("gcode")
gcode.register_mux_command("ANGLE_DEBUG_READ", "CHIP", name,
self.cmd_ANGLE_DEBUG_READ,
desc=self.cmd_ANGLE_DEBUG_READ_help)
def _build_config(self):
cmdqueue = self.spi.get_command_queue()
self.spi_angle_transfer_cmd = self.mcu.lookup_query_command(
"spi_angle_transfer oid=%c data=%*s",
"spi_angle_transfer_response oid=%c clock=%u response=%*s",
oid=self.oid, cq=cmdqueue)
def _send_spi(self, msg):
return self.spi.spi_transfer(msg)
def get_static_delay(self):
return .000001
def _read_reg(self, reg):
msg = [reg, 0, 0]
params = self._send_spi(msg)
resp = bytearray(params['response'])
val = (resp[1] << 8) | resp[2]
return val
def start(self):
pass
cmd_ANGLE_DEBUG_READ_help = "Query low-level angle sensor register"
def cmd_ANGLE_DEBUG_READ(self, gcmd):
reg = 0x83
val = self._read_reg(reg)
gcmd.respond_info("ANGLE REG[0x%02x] = 0x%04x" % (reg, val))
angle = val >> 2
parity = bin(val >> 1).count("1") % 2
gcmd.respond_info("Angle %i ~ %.2f" % (angle, angle * 360 / (1 << 14)))
gcmd.respond_info("No Mag: %i" % (val >> 1 & 0x1))
gcmd.respond_info("Parity: %i == %i" % (parity, val & 0x1))
class HelperMT6826S:
SPI_MODE = 3
SPI_SPEED = 10000000
def __init__(self, config, spi, oid):
self.printer = config.get_printer()
self.stepper_name = config.get('stepper', None)
self.spi = spi
self.oid = oid
self.mcu = spi.get_mcu()
self.mcu.register_config_callback(self._build_config)
self.spi_angle_transfer_cmd = None
self.is_tcode_absolute = False
self.last_temperature = None
name = config.get_name().split()[-1]
gcode = self.printer.lookup_object("gcode")
gcode.register_mux_command("ANGLE_DEBUG_READ", "CHIP", name,
self.cmd_ANGLE_DEBUG_READ,
desc=self.cmd_ANGLE_DEBUG_READ_help)
gcode.register_mux_command("ANGLE_CHIP_CALIBRATE", "CHIP", name,
self.cmd_ANGLE_CHIP_CALIBRATE,
desc=self.cmd_ANGLE_CHIP_CALIBRATE_help)
self.status_map = {
0: "No Calibration",
1: "Running Calibration",
2: "Calibration Failed",
3: "Calibration Successful"
}
def _build_config(self):
cmdqueue = self.spi.get_command_queue()
self.spi_angle_transfer_cmd = self.mcu.lookup_query_command(
"spi_angle_transfer oid=%c data=%*s",
"spi_angle_transfer_response oid=%c clock=%u response=%*s",
oid=self.oid, cq=cmdqueue)
def _send_spi(self, msg):
params = self.spi.spi_transfer(msg)
return params
def get_static_delay(self):
return .00001
def _read_reg(self, reg):
reg = 0x3000 | reg
msg = [reg >> 8, reg & 0xff, 0]
params = self._send_spi(msg)
resp = bytearray(params['response'])
return resp[2]
def _write_reg(self, reg, data):
reg = 0x6000 | reg
msg = [reg >> 8, reg & 0xff, data]
self._send_spi(msg)
def crc8(self, data):
polynomial = 0x07
crc = 0x00
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x80:
crc = (crc << 1) ^ polynomial
else:
crc <<= 1
crc &= 0xFF
return crc
def _read_angle(self, reg):
reg = 0x3000 | reg
msg = [reg >> 8, reg & 0xff, 0, 0, 0, 0]
params = self._send_spi(msg)
resp = bytearray(params['response'])
angle = (resp[2] << 7) | (resp[3] >> 1)
status = resp[4]
crc_computed = self.crc8([resp[2], resp[3], resp[4]])
crc = resp[5]
return angle, status, crc, crc_computed
def start(self):
val = self._read_reg(0x00d)
# Set histeresis to 0.003 degree
self._write_reg(0x00d, (val & 0xf8) | 0x5)
def get_microsteps(self):
configfile = self.printer.lookup_object('configfile')
sconfig = configfile.get_status(None)['settings']
stconfig = sconfig.get(self.stepper_name, {})
microsteps = stconfig['microsteps']
full_steps = stconfig['full_steps_per_rotation']
return microsteps, full_steps
cmd_ANGLE_CHIP_CALIBRATE_help = "Run MT6826s calibration sequence"
def cmd_ANGLE_CHIP_CALIBRATE(self, gcmd):
fmove = self.printer.lookup_object('force_move')
mcu_stepper = fmove.lookup_stepper(self.stepper_name)
if self.stepper_name is None:
gcmd.respond_info("stepper not defined")
return
gcmd.respond_info("MT6826S Run calibration sequence")
gcmd.respond_info("Motor will do 18+ rotations -" +
" ensure pulley is disconnected")
req_freq = self._read_reg(0x00e) >> 4 & 0x7
# Minimal calibration speed
rpm = (3200 >> req_freq) + 1
rps = rpm / 60
move = fmove.manual_move
# Move stepper several turns (to allow internal sensor calibration)
microsteps, full_steps = self.get_microsteps()
step_dist = mcu_stepper.get_step_dist()
full_step_dist = step_dist * microsteps
rotation_dist = full_steps * full_step_dist
move(mcu_stepper, 2 * rotation_dist, rps * rotation_dist)
self._write_reg(0x155, 0x5e)
move(mcu_stepper, 20 * rotation_dist, rps * rotation_dist)
val = self._read_reg(0x113)
code = val >> 6
gcmd.respond_info("Status: %s" % (self.status_map[code]))
while code == 1:
move(mcu_stepper, 5 * rotation_dist, rps * rotation_dist)
val = self._read_reg(0x113)
code = val >> 6
gcmd.respond_info("Status: %s" % (self.status_map[code]))
if code == 2:
gcmd.respond_info("Calibration failed")
if code == 3:
gcmd.respond_info("Calibration success, please poweroff sensor")
cmd_ANGLE_DEBUG_READ_help = "Query low-level angle sensor register"
def cmd_ANGLE_DEBUG_READ(self, gcmd):
reg = gcmd.get("REG", minval=0, maxval=0x155,
parser=lambda x: int(x, 0))
if reg == 0x003:
angle, status, crc1, crc2 = self._read_angle(reg)
gcmd.respond_info("ANGLE REG[0x003] = 0x%02x" %
(angle >> 7))
gcmd.respond_info("ANGLE REG[0x004] = 0x%02x" %
((angle << 1) & 0xff))
gcmd.respond_info("Angle %i ~ %.2f" % (angle,
angle * 360 / (1 << 15)))
gcmd.respond_info("Weak Mag: %i" % (status >> 1 & 0x1))
gcmd.respond_info("Under Voltage: %i" % (status >> 2 & 0x1))
gcmd.respond_info("CRC: 0x%02x == 0x%02x" % (crc1, crc2))
elif reg == 0x00e:
val = self._read_reg(reg)
gcmd.respond_info("GPIO_DS = %i" % (val >> 7))
gcmd.respond_info("AUTOCAL_FREQ = %i" % (val >> 4 & 0x7))
elif reg == 0x113:
val = self._read_reg(reg)
gcmd.respond_info("Status: %s" % (self.cal_status[val >> 6]))
else:
val = self._read_reg(reg)
gcmd.respond_info("REG[0x%04x] = 0x%02x" % (reg, val))
BYTES_PER_SAMPLE = 3
SAMPLES_PER_BLOCK = bulk_sensor.MAX_BULK_MSG_SIZE // BYTES_PER_SAMPLE
SAMPLE_PERIOD = 0.000400
BATCH_UPDATES = 0.100
class Angle:
def __init__(self, config):
@@ -417,12 +616,12 @@ class Angle:
# Measurement conversion
self.start_clock = self.time_shift = self.sample_ticks = 0
self.last_sequence = self.last_angle = 0
# Measurement storage (accessed from background thread)
self.lock = threading.Lock()
self.raw_samples = []
# Sensor type
sensors = { "a1333": HelperA1333, "as5047d": HelperAS5047D,
"tle5012b": HelperTLE5012B }
sensors = { "a1333": HelperA1333,
"as5047d": HelperAS5047D,
"tle5012b": HelperTLE5012B,
"mt6816": HelperMT6816,
"mt6826s": HelperMT6826S }
sensor_type = config.getchoice('sensor_type', {s: s for s in sensors})
sensor_class = sensors[sensor_type]
self.spi = bus.MCU_SPI_from_config(config, sensor_class.SPI_MODE,
@@ -431,7 +630,7 @@ class Angle:
self.oid = oid = mcu.create_oid()
self.sensor_helper = sensor_class(config, self.spi, oid)
# Setup mcu sensor_spi_angle bulk query code
self.query_spi_angle_cmd = self.query_spi_angle_end_cmd = None
self.query_spi_angle_cmd = None
mcu.add_config_cmd(
"config_spi_angle oid=%d spi_oid=%d spi_angle_type=%s"
% (oid, self.spi.get_oid(), sensor_type))
@@ -439,15 +638,15 @@ class Angle:
"query_spi_angle oid=%d clock=0 rest_ticks=0 time_shift=0"
% (oid,), on_restart=True)
mcu.register_config_callback(self._build_config)
mcu.register_response(self._handle_spi_angle_data,
"spi_angle_data", oid)
# API server endpoints
self.api_dump = motion_report.APIDumpHelper(
self.printer, self._api_update, self._api_startstop, 0.100)
self.bulk_queue = bulk_sensor.BulkDataQueue(mcu, oid=oid)
# Process messages in batches
self.batch_bulk = bulk_sensor.BatchBulkHelper(
self.printer, self._process_batch,
self._start_measurements, self._finish_measurements, BATCH_UPDATES)
self.name = config.get_name().split()[1]
wh = self.printer.lookup_object('webhooks')
wh.register_mux_endpoint("angle/dump_angle", "sensor", self.name,
self._handle_dump_angle)
api_resp = {'header': ('time', 'angle')}
self.batch_bulk.add_mux_endpoint("angle/dump_angle",
"sensor", self.name, api_resp)
def _build_config(self):
freq = self.mcu.seconds_to_clock(1.)
while float(TCODE_ERROR << self.time_shift) / freq < 0.002:
@@ -456,17 +655,11 @@ class Angle:
self.query_spi_angle_cmd = self.mcu.lookup_command(
"query_spi_angle oid=%c clock=%u rest_ticks=%u time_shift=%c",
cq=cmdqueue)
self.query_spi_angle_end_cmd = self.mcu.lookup_query_command(
"query_spi_angle oid=%c clock=%u rest_ticks=%u time_shift=%c",
"spi_angle_end oid=%c sequence=%hu", oid=self.oid, cq=cmdqueue)
def get_status(self, eventtime=None):
return {'temperature': self.sensor_helper.last_temperature}
# Measurement collection
def is_measuring(self):
return self.start_clock != 0
def _handle_spi_angle_data(self, params):
with self.lock:
self.raw_samples.append(params)
def add_client(self, client_cb):
self.batch_bulk.add_client(client_cb)
# Measurement decoding
def _extract_samples(self, raw_samples):
# Load variables to optimize inner loop below
sample_ticks = self.sample_ticks
@@ -487,23 +680,23 @@ class Angle:
static_delay = self.sensor_helper.get_static_delay()
# Process every message in raw_samples
count = error_count = 0
samples = [None] * (len(raw_samples) * 16)
samples = [None] * (len(raw_samples) * SAMPLES_PER_BLOCK)
for params in raw_samples:
seq = (last_sequence & ~0xffff) | params['sequence']
if seq < last_sequence:
seq += 0x10000
last_sequence = seq
seq_diff = (params['sequence'] - last_sequence) & 0xffff
last_sequence += seq_diff
samp_count = last_sequence * SAMPLES_PER_BLOCK
msg_mclock = start_clock + samp_count*sample_ticks
d = bytearray(params['data'])
msg_mclock = start_clock + seq*16*sample_ticks
for i in range(len(d) // 3):
tcode = d[i*3]
for i in range(len(d) // BYTES_PER_SAMPLE):
d_ta = d[i*BYTES_PER_SAMPLE:(i+1)*BYTES_PER_SAMPLE]
tcode = d_ta[0]
if tcode == TCODE_ERROR:
error_count += 1
continue
raw_angle = d[i*3 + 1] | (d[i*3 + 2] << 8)
angle_diff = (last_angle - raw_angle) & 0xffff
raw_angle = d_ta[1] | (d_ta[2] << 8)
angle_diff = (raw_angle - last_angle) & 0xffff
angle_diff -= (angle_diff & 0x8000) << 1
last_angle -= angle_diff
last_angle += angle_diff
mclock = msg_mclock + i*sample_ticks
if is_tcode_absolute:
# tcode is tle5012b frame counter
@@ -522,29 +715,14 @@ class Angle:
self.last_angle = last_angle
del samples[count:]
return samples, error_count
# API interface
def _api_update(self, eventtime):
if self.sensor_helper.is_tcode_absolute:
self.sensor_helper.update_clock()
with self.lock:
raw_samples = self.raw_samples
self.raw_samples = []
if not raw_samples:
return {}
samples, error_count = self._extract_samples(raw_samples)
if not samples:
return {}
offset = self.calibration.apply_calibration(samples)
return {'data': samples, 'errors': error_count,
'position_offset': offset}
# Start, stop, and process message batches
def _is_measuring(self):
return self.start_clock != 0
def _start_measurements(self):
if self.is_measuring():
return
logging.info("Starting angle '%s' measurements", self.name)
self.sensor_helper.start()
# Start bulk reading
with self.lock:
self.raw_samples = []
self.bulk_queue.clear_queue()
self.last_sequence = 0
systime = self.printer.get_reactor().monotonic()
print_time = self.mcu.estimated_print_time(systime) + MIN_MSG_TIME
@@ -554,26 +732,23 @@ class Angle:
self.query_spi_angle_cmd.send([self.oid, reqclock, rest_ticks,
self.time_shift], reqclock=reqclock)
def _finish_measurements(self):
if not self.is_measuring():
return
# Halt bulk reading
params = self.query_spi_angle_end_cmd.send([self.oid, 0, 0, 0])
self.start_clock = 0
with self.lock:
self.raw_samples = []
self.query_spi_angle_cmd.send_wait_ack([self.oid, 0, 0, 0])
self.bulk_queue.clear_queue()
self.sensor_helper.last_temperature = None
logging.info("Stopped angle '%s' measurements", self.name)
def _api_startstop(self, is_start):
if is_start:
self._start_measurements()
else:
self._finish_measurements()
def _handle_dump_angle(self, web_request):
self.api_dump.add_client(web_request)
hdr = ('time', 'angle')
web_request.send({'header': hdr})
def start_internal_client(self):
return self.api_dump.add_internal_client()
def _process_batch(self, eventtime):
if self.sensor_helper.is_tcode_absolute:
self.sensor_helper.update_clock()
raw_samples = self.bulk_queue.pull_queue()
if not raw_samples:
return {}
samples, error_count = self._extract_samples(raw_samples)
if not samples:
return {}
offset = self.calibration.apply_calibration(samples)
return {'data': samples, 'errors': error_count,
'position_offset': offset}
def load_config_prefix(config):
return Angle(config)

View File

@@ -5,7 +5,7 @@
# This file may be distributed under the terms of the GNU GPLv3 license.
import math
from . import manual_probe as ManualProbe, bed_mesh as BedMesh
from . import manual_probe, bed_mesh, probe
DEFAULT_SAMPLE_COUNT = 3
@@ -23,45 +23,75 @@ class AxisTwistCompensation:
self.horizontal_move_z = config.getfloat('horizontal_move_z',
DEFAULT_HORIZONTAL_MOVE_Z)
self.speed = config.getfloat('speed', DEFAULT_SPEED)
self.calibrate_start_x = config.getfloat('calibrate_start_x')
self.calibrate_end_x = config.getfloat('calibrate_end_x')
self.calibrate_y = config.getfloat('calibrate_y')
self.calibrate_start_x = config.getfloat('calibrate_start_x',
default=None)
self.calibrate_end_x = config.getfloat('calibrate_end_x', default=None)
self.calibrate_y = config.getfloat('calibrate_y', default=None)
self.z_compensations = config.getlists('z_compensations',
default=[], parser=float)
self.compensation_start_x = config.getfloat('compensation_start_x',
default=None)
self.compensation_end_x = config.getfloat('compensation_start_y',
self.compensation_end_x = config.getfloat('compensation_end_x',
default=None)
self.m = None
self.b = None
self.calibrate_start_y = config.getfloat('calibrate_start_y',
default=None)
self.calibrate_end_y = config.getfloat('calibrate_end_y', default=None)
self.calibrate_x = config.getfloat('calibrate_x', default=None)
self.compensation_start_y = config.getfloat('compensation_start_y',
default=None)
self.compensation_end_y = config.getfloat('compensation_end_y',
default=None)
self.zy_compensations = config.getlists('zy_compensations',
default=[], parser=float)
# setup calibrater
self.calibrater = Calibrater(self, config)
# register events
self.printer.register_event_handler("probe:update_results",
self._update_z_compensation_value)
def get_z_compensation_value(self, pos):
if not self.z_compensations:
return 0
def _update_z_compensation_value(self, pos):
if self.z_compensations:
pos[2] += self._get_interpolated_z_compensation(
pos[0], self.z_compensations,
self.compensation_start_x,
self.compensation_end_x
)
if self.zy_compensations:
pos[2] += self._get_interpolated_z_compensation(
pos[1], self.zy_compensations,
self.compensation_start_y,
self.compensation_end_y
)
def _get_interpolated_z_compensation(
self, coord, z_compensations,
comp_start,
comp_end
):
x_coord = pos[0]
z_compensations = self.z_compensations
sample_count = len(z_compensations)
spacing = ((self.calibrate_end_x - self.calibrate_start_x)
spacing = ((comp_end - comp_start)
/ (sample_count - 1))
interpolate_t = (x_coord - self.calibrate_start_x) / spacing
interpolate_t = (coord - comp_start) / spacing
interpolate_i = int(math.floor(interpolate_t))
interpolate_i = BedMesh.constrain(interpolate_i, 0, sample_count - 2)
interpolate_i = bed_mesh.constrain(interpolate_i, 0, sample_count - 2)
interpolate_t -= interpolate_i
interpolated_z_compensation = BedMesh.lerp(
interpolated_z_compensation = bed_mesh.lerp(
interpolate_t, z_compensations[interpolate_i],
z_compensations[interpolate_i + 1])
return interpolated_z_compensation
def clear_compensations(self):
self.z_compensations = []
self.m = None
self.b = None
def clear_compensations(self, axis=None):
if axis is None:
self.z_compensations = []
self.zy_compensations = []
elif axis == 'X':
self.z_compensations = []
elif axis == 'Y':
self.zy_compensations = []
class Calibrater:
def __init__(self, compensation, config):
@@ -77,10 +107,14 @@ class Calibrater:
self._handle_connect)
self.speed = compensation.speed
self.horizontal_move_z = compensation.horizontal_move_z
self.start_point = (compensation.calibrate_start_x,
self.x_start_point = (compensation.calibrate_start_x,
compensation.calibrate_y)
self.end_point = (compensation.calibrate_end_x,
self.x_end_point = (compensation.calibrate_end_x,
compensation.calibrate_y)
self.y_start_point = (compensation.calibrate_x,
compensation.calibrate_start_y)
self.y_end_point = (compensation.calibrate_x,
compensation.calibrate_end_y)
self.results = None
self.current_point_index = None
self.gcmd = None
@@ -95,7 +129,7 @@ class Calibrater:
config = self.printer.lookup_object('configfile')
raise config.error(
"AXIS_TWIST_COMPENSATION requires [probe] to be defined")
self.lift_speed = self.probe.get_lift_speed()
self.lift_speed = self.probe.get_probe_params()['lift_speed']
self.probe_x_offset, self.probe_y_offset, _ = \
self.probe.get_offsets()
@@ -116,39 +150,246 @@ class Calibrater:
def cmd_AXIS_TWIST_COMPENSATION_CALIBRATE(self, gcmd):
self.gcmd = gcmd
sample_count = gcmd.get_int('SAMPLE_COUNT', DEFAULT_SAMPLE_COUNT)
axis = gcmd.get('AXIS', None)
auto = gcmd.get('AUTO', False)
if axis is not None and auto:
raise self.gcmd.error(
"Cannot use both 'AXIS' and 'AUTO' at the same time."
)
if auto:
self._start_autocalibration(sample_count)
return
if axis is None and not auto:
axis = 'X'
# check for valid sample_count
if sample_count < 2:
raise self.gcmd.error(
"SAMPLE_COUNT to probe must be at least 2")
# calculate the points to put the probe at, returned as a list of tuples
nozzle_points = []
if axis == 'X':
self.compensation.clear_compensations('X')
if not all([
self.x_start_point[0],
self.x_end_point[0],
self.x_start_point[1]
]):
raise self.gcmd.error(
"""AXIS_TWIST_COMPENSATION for X axis requires
calibrate_start_x, calibrate_end_x and calibrate_y
to be defined
"""
)
start_point = self.x_start_point
end_point = self.x_end_point
x_axis_range = end_point[0] - start_point[0]
interval_dist = x_axis_range / (sample_count - 1)
for i in range(sample_count):
x = start_point[0] + i * interval_dist
y = start_point[1]
nozzle_points.append((x, y))
elif axis == 'Y':
self.compensation.clear_compensations('Y')
if not all([
self.y_start_point[0],
self.y_end_point[0],
self.y_start_point[1]
]):
raise self.gcmd.error(
"""AXIS_TWIST_COMPENSATION for Y axis requires
calibrate_start_y, calibrate_end_y and calibrate_x
to be defined
"""
)
start_point = self.y_start_point
end_point = self.y_end_point
y_axis_range = end_point[1] - start_point[1]
interval_dist = y_axis_range / (sample_count - 1)
for i in range(sample_count):
x = start_point[0]
y = start_point[1] + i * interval_dist
nozzle_points.append((x, y))
else:
raise self.gcmd.error(
"AXIS_TWIST_COMPENSATION_CALIBRATE: "
"Invalid axis.")
probe_points = self._calculate_probe_points(
nozzle_points, self.probe_x_offset, self.probe_y_offset)
# verify no other manual probe is in progress
manual_probe.verify_no_manual_probe(self.printer)
# begin calibration
self.current_point_index = 0
self.results = []
self.current_axis = axis
self._calibration(probe_points, nozzle_points, interval_dist)
def _calculate_corrections(self, coordinates):
# Extracting x, y, and z values from coordinates
x_coords = [coord[0] for coord in coordinates]
y_coords = [coord[1] for coord in coordinates]
z_coords = [coord[2] for coord in coordinates]
# Calculate the desired point (average of all corner points in z)
# For a general case, we should extract the unique
# combinations of corner points
z_corners = [z_coords[i] for i, coord in enumerate(coordinates)
if (coord[0] in [x_coords[0], x_coords[-1]])
and (coord[1] in [y_coords[0], y_coords[-1]])]
z_desired = sum(z_corners) / len(z_corners)
# Calculate average deformation per axis
unique_x_coords = sorted(set(x_coords))
unique_y_coords = sorted(set(y_coords))
avg_z_x = []
for x in unique_x_coords:
indices = [i for i, coord in enumerate(coordinates)
if coord[0] == x]
avg_z = sum(z_coords[i] for i in indices) / len(indices)
avg_z_x.append(avg_z)
avg_z_y = []
for y in unique_y_coords:
indices = [i for i, coord in enumerate(coordinates)
if coord[1] == y]
avg_z = sum(z_coords[i] for i in indices) / len(indices)
avg_z_y.append(avg_z)
# Calculate corrections to reach the desired point
x_corrections = [z_desired - avg for avg in avg_z_x]
y_corrections = [z_desired - avg for avg in avg_z_y]
return x_corrections, y_corrections
def _start_autocalibration(self, sample_count):
if not all([
self.x_start_point[0],
self.x_end_point[0],
self.y_start_point[0],
self.y_end_point[0]
]):
raise self.gcmd.error(
"""AXIS_TWIST_COMPENSATION_AUTOCALIBRATE requires
calibrate_start_x, calibrate_end_x, calibrate_start_y
and calibrate_end_y to be defined
"""
)
# check for valid sample_count
if sample_count is None or sample_count < 2:
raise self.gcmd.error(
"SAMPLE_COUNT to probe must be at least 2")
# verify no other manual probe is in progress
manual_probe.verify_no_manual_probe(self.printer)
# clear the current config
self.compensation.clear_compensations()
# calculate some values
x_range = self.end_point[0] - self.start_point[0]
interval_dist = x_range / (sample_count - 1)
nozzle_points = self._calculate_nozzle_points(sample_count,
interval_dist)
probe_points = self._calculate_probe_points(
nozzle_points, self.probe_x_offset, self.probe_y_offset)
min_x = self.x_start_point[0]
max_x = self.x_end_point[0]
min_y = self.y_start_point[1]
max_y = self.y_end_point[1]
# verify no other manual probe is in progress
ManualProbe.verify_no_manual_probe(self.printer)
# calculate x positions
interval_x = (max_x - min_x) / (sample_count - 1)
xps = [min_x + interval_x * i for i in range(sample_count)]
# begin calibration
self.current_point_index = 0
self.results = []
self._calibration(probe_points, nozzle_points, interval_dist)
# Calculate points array
interval_y = (max_y - min_y) / (sample_count - 1)
flip = False
def _calculate_nozzle_points(self, sample_count, interval_dist):
# calculate the points to put the probe at, returned as a list of tuples
nozzle_points = []
points = []
for i in range(sample_count):
x = self.start_point[0] + i * interval_dist
y = self.start_point[1]
nozzle_points.append((x, y))
return nozzle_points
for j in range(sample_count):
if(not flip):
idx = j
else:
idx = sample_count -1 - j
points.append([xps[i], min_y + interval_y * idx ])
flip = not flip
# calculate the points to put the nozzle at, and probe
probe_points = []
for i in range(len(points)):
x = points[i][0] - self.probe_x_offset
y = points[i][1] - self.probe_y_offset
probe_points.append([x, y, self._auto_calibration((x,y))[2]])
# calculate corrections
x_corr, y_corr = self._calculate_corrections(probe_points)
x_corr_str = ', '.join(["{:.6f}".format(x)
for x in x_corr])
y_corr_str = ', '.join(["{:.6f}".format(x)
for x in y_corr])
# finalize
configfile = self.printer.lookup_object('configfile')
configfile.set(self.configname, 'z_compensations', x_corr_str)
configfile.set(self.configname, 'compensation_start_x',
self.x_start_point[0])
configfile.set(self.configname, 'compensation_end_x',
self.x_end_point[0])
configfile.set(self.configname, 'zy_compensations', y_corr_str)
configfile.set(self.configname, 'compensation_start_y',
self.y_start_point[1])
configfile.set(self.configname, 'compensation_end_y',
self.y_end_point[1])
self.gcode.respond_info(
"AXIS_TWIST_COMPENSATION state has been saved "
"for the current session. The SAVE_CONFIG command will "
"update the printer config file and restart the printer.")
# output result
self.gcmd.respond_info(
"AXIS_TWIST_COMPENSATION_AUTOCALIBRATE: Calibration complete: ")
self.gcmd.respond_info("\n".join(map(str, [x_corr, y_corr])), log=False)
def _auto_calibration(self, probe_point):
# horizontal_move_z (to prevent probe trigger or hitting bed)
self._move_helper((None, None, self.horizontal_move_z))
# move to point to probe
self._move_helper((probe_point[0],
probe_point[1], None))
# probe the point
pos = probe.run_single_probe(self.probe, self.gcmd)
# horizontal_move_z (to prevent probe trigger or hitting bed)
self._move_helper((None, None, self.horizontal_move_z))
return pos
def _calculate_probe_points(self, nozzle_points,
probe_x_offset, probe_y_offset):
@@ -186,7 +427,8 @@ class Calibrater:
probe_points[self.current_point_index][1], None))
# probe the point
self.current_measured_z = self.probe.run_probe(self.gcmd)[2]
pos = probe.run_single_probe(self.probe, self.gcmd)
self.current_measured_z = pos[2]
# horizontal_move_z (to prevent probe trigger or hitting bed)
self._move_helper((None, None, self.horizontal_move_z))
@@ -195,7 +437,7 @@ class Calibrater:
self._move_helper((nozzle_points[self.current_point_index]))
# start the manual (nozzle) probe
ManualProbe.ManualProbeHelper(
manual_probe.ManualProbeHelper(
self.printer, self.gcmd,
self._manual_probe_callback_factory(
probe_points, nozzle_points, interval))
@@ -234,14 +476,31 @@ class Calibrater:
configfile = self.printer.lookup_object('configfile')
values_as_str = ', '.join(["{:.6f}".format(x)
for x in self.results])
configfile.set(self.configname, 'z_compensations', values_as_str)
configfile.set(self.configname, 'compensation_start_x',
self.start_point[0])
configfile.set(self.configname, 'compensation_end_x',
self.end_point[0])
self.compensation.z_compensations = self.results
self.compensation.compensation_start_x = self.start_point[0]
self.compensation.compensation_end_x = self.end_point[0]
if(self.current_axis == 'X'):
configfile.set(self.configname, 'z_compensations', values_as_str)
configfile.set(self.configname, 'compensation_start_x',
self.x_start_point[0])
configfile.set(self.configname, 'compensation_end_x',
self.x_end_point[0])
self.compensation.z_compensations = self.results
self.compensation.compensation_start_x = self.x_start_point[0]
self.compensation.compensation_end_x = self.x_end_point[0]
elif(self.current_axis == 'Y'):
configfile.set(self.configname, 'zy_compensations', values_as_str)
configfile.set(self.configname, 'compensation_start_y',
self.y_start_point[1])
configfile.set(self.configname, 'compensation_end_y',
self.y_end_point[1])
self.compensation.zy_compensations = self.results
self.compensation.compensation_start_y = self.y_start_point[1]
self.compensation.compensation_end_y = self.y_end_point[1]
self.gcode.respond_info(
"AXIS_TWIST_COMPENSATION state has been saved "
"for the current session. The SAVE_CONFIG command will "

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# BLTouch support
#
# Copyright (C) 2018-2021 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2018-2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
@@ -23,13 +23,9 @@ Commands = {
}
# BLTouch "endstop" wrapper
class BLTouchEndstopWrapper:
class BLTouchProbe:
def __init__(self, config):
self.printer = config.get_printer()
self.printer.register_event_handler("klippy:connect",
self.handle_connect)
self.printer.register_event_handler('klippy:mcu_identify',
self.handle_mcu_identify)
self.position_endstop = config.getfloat('z_offset', minval=0.)
self.stow_on_each_sample = config.getboolean('stow_on_each_sample',
True)
@@ -44,12 +40,9 @@ class BLTouchEndstopWrapper:
self.next_cmd_time = self.action_end_time = 0.
self.finish_home_complete = self.wait_trigger_complete = None
# Create an "endstop" object to handle the sensor pin
pin = config.get('sensor_pin')
pin_params = ppins.lookup_pin(pin, can_invert=True, can_pullup=True)
mcu = pin_params['chip']
self.mcu_endstop = mcu.setup_pin('endstop', pin_params)
self.mcu_endstop = ppins.setup_pin('endstop', config.get('sensor_pin'))
# output mode
omodes = {'5V': '5V', 'OD': 'OD', None: None}
omodes = ['5V', 'OD', None]
self.output_mode = config.getchoice('set_output_mode', omodes, None)
# Setup for sensor test
self.next_test_time = 0.
@@ -65,19 +58,30 @@ class BLTouchEndstopWrapper:
self.get_steppers = self.mcu_endstop.get_steppers
self.home_wait = self.mcu_endstop.home_wait
self.query_endstop = self.mcu_endstop.query_endstop
# multi probes state
self.multi = 'OFF'
# Common probe implementation helpers
self.cmd_helper = probe.ProbeCommandHelper(
config, self, self.mcu_endstop.query_endstop)
self.probe_offsets = probe.ProbeOffsetsHelper(config)
self.probe_session = probe.ProbeSessionHelper(config, self)
# Register BLTOUCH_DEBUG command
self.gcode = self.printer.lookup_object('gcode')
self.gcode.register_command("BLTOUCH_DEBUG", self.cmd_BLTOUCH_DEBUG,
desc=self.cmd_BLTOUCH_DEBUG_help)
self.gcode.register_command("BLTOUCH_STORE", self.cmd_BLTOUCH_STORE,
desc=self.cmd_BLTOUCH_STORE_help)
# multi probes state
self.multi = 'OFF'
def handle_mcu_identify(self):
kin = self.printer.lookup_object('toolhead').get_kinematics()
for stepper in kin.get_steppers():
if stepper.is_active_axis('z'):
self.add_stepper(stepper)
# Register events
self.printer.register_event_handler("klippy:connect",
self.handle_connect)
def get_probe_params(self, gcmd=None):
return self.probe_session.get_probe_params(gcmd)
def get_offsets(self):
return self.probe_offsets.get_offsets()
def get_status(self, eventtime):
return self.cmd_helper.get_status(eventtime)
def start_probe_session(self, gcmd):
return self.probe_session.start_probe_session(gcmd)
def handle_connect(self):
self.sync_mcu_print_time()
self.next_cmd_time += 0.200
@@ -116,7 +120,11 @@ class BLTouchEndstopWrapper:
self.mcu_endstop.home_start(self.action_end_time, ENDSTOP_SAMPLE_TIME,
ENDSTOP_SAMPLE_COUNT, ENDSTOP_REST_TIME,
triggered=triggered)
trigger_time = self.mcu_endstop.home_wait(self.action_end_time + 0.100)
try:
trigger_time = self.mcu_endstop.home_wait(
self.action_end_time + 0.100)
except self.printer.command_error as e:
return False
return trigger_time > 0.
def raise_probe(self):
self.sync_mcu_print_time()
@@ -183,6 +191,9 @@ class BLTouchEndstopWrapper:
self.verify_raise_probe()
self.sync_print_time()
self.multi = 'OFF'
def probing_move(self, pos, speed):
phoming = self.printer.lookup_object('homing')
return phoming.probing_move(self, pos, speed)
def probe_prepare(self, hmove):
if self.multi == 'OFF' or self.multi == 'FIRST':
self.lower_probe()
@@ -271,6 +282,6 @@ class BLTouchEndstopWrapper:
self.sync_print_time()
def load_config(config):
blt = BLTouchEndstopWrapper(config)
config.get_printer().add_object('probe', probe.PrinterProbe(config, blt))
blt = BLTouchProbe(config)
config.get_printer().add_object('probe', blt)
return blt

View File

@@ -8,6 +8,7 @@ from . import bus
REPORT_TIME = .8
BME280_CHIP_ADDR = 0x76
BME280_REGS = {
'RESET': 0xE0, 'CTRL_HUM': 0xF2,
'STATUS': 0xF3, 'CTRL_MEAS': 0xF4, 'CONFIG': 0xF5,
@@ -16,6 +17,29 @@ BME280_REGS = {
'HUM_MSB': 0xFD, 'HUM_LSB': 0xFE, 'CAL_1': 0x88, 'CAL_2': 0xE1
}
BMP388_REGS = {
"CMD": 0x7E,
"STATUS": 0x03,
"PWR_CTRL": 0x1B,
"OSR": 0x1C,
"ORD": 0x1D,
"INT_CTRL": 0x19,
"CAL_1": 0x31,
"TEMP_MSB": 0x09,
"TEMP_LSB": 0x08,
"TEMP_XLSB": 0x07,
"PRESS_MSB": 0x06,
"PRESS_LSB": 0x05,
"PRESS_XLSB": 0x04,
}
BMP388_REG_VAL_PRESS_EN = 0x01
BMP388_REG_VAL_TEMP_EN = 0x02
BMP388_REG_VAL_PRESS_OS_NO = 0b000
BMP388_REG_VAL_TEMP_OS_NO = 0b000000
BMP388_REG_VAL_ODR_50_HZ = 0x02
BMP388_REG_VAL_DRDY_EN = 0b100000
BMP388_REG_VAL_NORMAL_MODE = 0x30
BME680_REGS = {
'RESET': 0xE0, 'CTRL_HUM': 0x72, 'CTRL_GAS_1': 0x71, 'CTRL_GAS_0': 0x70,
'GAS_WAIT_0': 0x64, 'RES_HEAT_0': 0x5A, 'IDAC_HEAT_0': 0x50,
@@ -46,9 +70,20 @@ BME680_GAS_CONSTANTS = {
15: (1., 244.140625)
}
BMP180_REGS = {
'RESET': 0xE0,
'CAL_1': 0xAA,
'CTRL_MEAS': 0xF4,
'REG_MSB': 0xF6,
'REG_LSB': 0xF7,
'CRV_TEMP': 0x2E,
'CRV_PRES': 0x34
}
STATUS_MEASURING = 1 << 3
STATUS_IM_UPDATE = 1
MODE = 1
MODE_PERIODIC = 3
RUN_GAS = 1 << 4
NB_CONV_0 = 0
EAS_NEW_DATA = 1 << 7
@@ -57,9 +92,11 @@ MEASURE_DONE = 1 << 5
RESET_CHIP_VALUE = 0xB6
BME_CHIPS = {
0x58: 'BMP280', 0x60: 'BME280', 0x61: 'BME680'
0x58: 'BMP280', 0x60: 'BME280', 0x61: 'BME680', 0x55: 'BMP180',
0x50: 'BMP388'
}
BME_CHIP_ID_REG = 0xD0
BMP3_CHIP_ID_REG = 0x00
def get_twos_complement(val, bit_size):
@@ -81,6 +118,14 @@ def get_signed_byte(bits):
return get_twos_complement(bits, 8)
def get_unsigned_short_msb(bits):
return bits[0] << 8 | bits[1]
def get_signed_short_msb(bits):
val = get_unsigned_short_msb(bits)
return get_twos_complement(val, 16)
class BME280:
def __init__(self, config):
self.printer = config.get_printer()
@@ -99,6 +144,7 @@ class BME280:
pow(2, self.os_temp - 1), pow(2, self.os_hum - 1),
pow(2, self.os_pres - 1)))
logging.info("BMxx80: IIR: %dx" % (pow(2, self.iir_filter) - 1))
self.iir_filter = self.iir_filter & 0x07
self.temp = self.pressure = self.humidity = self.gas = self.t_fine = 0.
self.min_temp = self.max_temp = self.range_switching_error = 0.
@@ -111,6 +157,7 @@ class BME280:
return
self.printer.register_event_handler("klippy:connect",
self.handle_connect)
self.last_gas_time = 0
def handle_connect(self):
self._init_bmxx80()
@@ -144,6 +191,29 @@ class BME280:
dig['P9'] = get_signed_short(calib_data_1[22:24])
return dig
def read_calibration_data_bmp388(calib_data_1):
dig = {}
dig["T1"] = get_unsigned_short(calib_data_1[0:2]) / 0.00390625
dig["T2"] = get_unsigned_short(calib_data_1[2:4]) / 1073741824.0
dig["T3"] = get_signed_byte(calib_data_1[4]) / 281474976710656.0
dig["P1"] = get_signed_short(calib_data_1[5:7]) - 16384
dig["P1"] /= 1048576.0
dig["P2"] = get_signed_short(calib_data_1[7:9]) - 16384
dig["P2"] /= 536870912.0
dig["P3"] = get_signed_byte(calib_data_1[9]) / 4294967296.0
dig["P4"] = get_signed_byte(calib_data_1[10]) / 137438953472.0
dig["P5"] = get_unsigned_short(calib_data_1[11:13]) / 0.125
dig["P6"] = get_unsigned_short(calib_data_1[13:15]) / 64.0
dig["P7"] = get_signed_byte(calib_data_1[15]) / 256.0
dig["P8"] = get_signed_byte(calib_data_1[16]) / 32768.0
dig["P9"] = get_signed_short(calib_data_1[17:19])
dig["P9"] /= 281474976710656.0
dig["P10"] = get_signed_byte(calib_data_1[19]) / 281474976710656.0
dig["P11"] = get_signed_byte(calib_data_1[20])
dig["P11"] /= 36893488147419103232.0
return dig
def read_calibration_data_bme280(calib_data_1, calib_data_2):
dig = read_calibration_data_bmp280(calib_data_1)
dig['H1'] = calib_data_1[25] & 0xFF
@@ -188,7 +258,24 @@ class BME280:
dig['G3'] = get_signed_byte(calib_data_2[13])
return dig
chip_id = self.read_id()
def read_calibration_data_bmp180(calib_data_1):
dig = {}
dig['AC1'] = get_signed_short_msb(calib_data_1[0:2])
dig['AC2'] = get_signed_short_msb(calib_data_1[2:4])
dig['AC3'] = get_signed_short_msb(calib_data_1[4:6])
dig['AC4'] = get_unsigned_short_msb(calib_data_1[6:8])
dig['AC5'] = get_unsigned_short_msb(calib_data_1[8:10])
dig['AC6'] = get_unsigned_short_msb(calib_data_1[10:12])
dig['B1'] = get_signed_short_msb(calib_data_1[12:14])
dig['B2'] = get_signed_short_msb(calib_data_1[14:16])
dig['MB'] = get_signed_short_msb(calib_data_1[16:18])
dig['MC'] = get_signed_short_msb(calib_data_1[18:20])
dig['MD'] = get_signed_short_msb(calib_data_1[20:22])
return dig
chip_id = self.read_id() or self.read_bmp3_id()
if chip_id not in BME_CHIPS.keys():
logging.info("bme280: Unknown Chip ID received %#x" % chip_id)
else:
@@ -197,54 +284,133 @@ class BME280:
self.chip_type, self.i2c.i2c_address))
# Reset chip
self.write_register('RESET', [RESET_CHIP_VALUE])
self.write_register('RESET', [RESET_CHIP_VALUE], wait=True)
self.reactor.pause(self.reactor.monotonic() + .5)
# Make sure non-volatile memory has been copied to registers
status = self.read_register('STATUS', 1)[0]
while status & STATUS_IM_UPDATE:
self.reactor.pause(self.reactor.monotonic() + .01)
if self.chip_type != 'BMP180':
# BMP180 has no status register available
status = self.read_register('STATUS', 1)[0]
while status & STATUS_IM_UPDATE:
self.reactor.pause(self.reactor.monotonic() + .01)
status = self.read_register('STATUS', 1)[0]
if self.chip_type == 'BME680':
self.max_sample_time = 0.5
self.max_sample_time = \
(1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + .575)
+ ((2.3 * self.os_hum) + .575)) / 1000
self.sample_timer = self.reactor.register_timer(self._sample_bme680)
self.chip_registers = BME680_REGS
else:
elif self.chip_type == 'BMP180':
self.sample_timer = self.reactor.register_timer(self._sample_bmp180)
self.chip_registers = BMP180_REGS
elif self.chip_type == 'BMP388':
self.chip_registers = BMP388_REGS
self.write_register(
"PWR_CTRL",
[
BMP388_REG_VAL_PRESS_EN
| BMP388_REG_VAL_TEMP_EN
| BMP388_REG_VAL_NORMAL_MODE
],
)
self.write_register(
"OSR", [BMP388_REG_VAL_PRESS_OS_NO | BMP388_REG_VAL_TEMP_OS_NO]
)
self.write_register("ORD", [BMP388_REG_VAL_ODR_50_HZ])
self.write_register("INT_CTRL", [BMP388_REG_VAL_DRDY_EN])
self.sample_timer = self.reactor.register_timer(self._sample_bmp388)
elif self.chip_type == 'BME280':
self.max_sample_time = \
(1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + .575)
+ ((2.3 * self.os_hum) + .575)) / 1000
self.sample_timer = self.reactor.register_timer(self._sample_bme280)
self.chip_registers = BME280_REGS
if self.chip_type in ('BME680', 'BME280'):
self.write_register('CONFIG', (self.iir_filter & 0x07) << 2)
else:
self.max_sample_time = \
(1.25 + (2.3 * self.os_temp)
+ ((2.3 * self.os_pres) + .575)) / 1000
self.sample_timer = self.reactor.register_timer(self._sample_bme280)
self.chip_registers = BME280_REGS
# Read out and calculate the trimming parameters
cal_1 = self.read_register('CAL_1', 26)
cal_2 = self.read_register('CAL_2', 16)
if self.chip_type == 'BMP180':
cal_1 = self.read_register('CAL_1', 22)
elif self.chip_type == 'BMP388':
cal_1 = self.read_register('CAL_1', 21)
else:
cal_1 = self.read_register('CAL_1', 26)
cal_2 = self.read_register('CAL_2', 16)
if self.chip_type == 'BME280':
self.dig = read_calibration_data_bme280(cal_1, cal_2)
elif self.chip_type == 'BMP280':
self.dig = read_calibration_data_bmp280(cal_1)
elif self.chip_type == 'BME680':
self.dig = read_calibration_data_bme680(cal_1, cal_2)
elif self.chip_type == 'BMP180':
self.dig = read_calibration_data_bmp180(cal_1)
elif self.chip_type == 'BMP388':
self.dig = read_calibration_data_bmp388(cal_1)
if self.chip_type in ('BME280', 'BMP280'):
max_standby_time = REPORT_TIME - self.max_sample_time
# 0.5 ms
t_sb = 0
if self.chip_type == 'BME280':
if max_standby_time > 1:
t_sb = 5
elif max_standby_time > 0.5:
t_sb = 4
elif max_standby_time > 0.25:
t_sb = 3
elif max_standby_time > 0.125:
t_sb = 2
elif max_standby_time > 0.0625:
t_sb = 1
elif max_standby_time > 0.020:
t_sb = 7
elif max_standby_time > 0.010:
t_sb = 6
else:
if max_standby_time > 4:
t_sb = 7
elif max_standby_time > 2:
t_sb = 6
elif max_standby_time > 1:
t_sb = 5
elif max_standby_time > 0.5:
t_sb = 4
elif max_standby_time > 0.25:
t_sb = 3
elif max_standby_time > 0.125:
t_sb = 2
elif max_standby_time > 0.0625:
t_sb = 1
cfg = t_sb << 5 | self.iir_filter << 2
self.write_register('CONFIG', cfg)
if self.chip_type == 'BME280':
self.write_register('CTRL_HUM', self.os_hum)
# Enter normal (periodic) mode
meas = self.os_temp << 5 | self.os_pres << 2 | MODE_PERIODIC
self.write_register('CTRL_MEAS', meas, wait=True)
if self.chip_type == 'BME680':
self.write_register('CONFIG', self.iir_filter << 2)
# Should be set once and reused on every mode register write
self.write_register('CTRL_HUM', self.os_hum & 0x07)
gas_wait_0 = self._calc_gas_heater_duration(self.gas_heat_duration)
self.write_register('GAS_WAIT_0', [gas_wait_0])
res_heat_0 = self._calc_gas_heater_resistance(self.gas_heat_temp)
self.write_register('RES_HEAT_0', [res_heat_0])
# Set initial heater current to reach Gas heater target on start
self.write_register('IDAC_HEAT_0', 96)
def _sample_bme280(self, eventtime):
# Enter forced mode
if self.chip_type == 'BME280':
self.write_register('CTRL_HUM', self.os_hum)
meas = self.os_temp << 5 | self.os_pres << 2 | MODE
self.write_register('CTRL_MEAS', meas)
# In normal mode data shadowing is performed
# So reading can be done while measurements are in process
try:
# wait until results are ready
status = self.read_register('STATUS', 1)[0]
while status & STATUS_MEASURING:
self.reactor.pause(
self.reactor.monotonic() + self.max_sample_time)
status = self.read_register('STATUS', 1)[0]
if self.chip_type == 'BME280':
data = self.read_register('PRESSURE_MSB', 8)
elif self.chip_type == 'BMP280':
@@ -271,37 +437,114 @@ class BME280:
self._callback(self.mcu.estimated_print_time(measured_time), self.temp)
return measured_time + REPORT_TIME
def _sample_bmp388(self, eventtime):
status = self.read_register("STATUS", 1)
if status[0] & 0b100000:
self.temp = self._sample_bmp388_temp()
if self.temp < self.min_temp or self.temp > self.max_temp:
self.printer.invoke_shutdown(
"BME280 temperature %0.1f outside range of %0.1f:%.01f"
% (self.temp, self.min_temp, self.max_temp)
)
if status[0] & 0b010000:
self.pressure = self._sample_bmp388_press() / 100.0
measured_time = self.reactor.monotonic()
self._callback(self.mcu.estimated_print_time(measured_time), self.temp)
return measured_time + REPORT_TIME
def _sample_bmp388_temp(self):
xlsb = self.read_register("TEMP_XLSB", 1)
lsb = self.read_register("TEMP_LSB", 1)
msb = self.read_register("TEMP_MSB", 1)
adc_T = (msb[0] << 16) + (lsb[0] << 8) + (xlsb[0])
partial_data1 = adc_T - self.dig["T1"]
partial_data2 = self.dig["T2"] * partial_data1
self.t_fine = partial_data2
self.t_fine += (partial_data1 * partial_data1) * self.dig["T3"]
if self.t_fine < -40.0:
self.t_fine = -40.0
if self.t_fine > 85.0:
self.t_fine = 85.0
return self.t_fine
def _sample_bmp388_press(self):
xlsb = self.read_register("PRESS_XLSB", 1)
lsb = self.read_register("PRESS_LSB", 1)
msb = self.read_register("PRESS_MSB", 1)
adc_P = (msb[0] << 16) + (lsb[0] << 8) + (xlsb[0])
partial_data1 = self.dig["P6"] * self.t_fine
partial_data2 = self.dig["P7"] * (self.t_fine * self.t_fine)
partial_data3 = self.dig["P8"]
partial_data3 *= self.t_fine * self.t_fine * self.t_fine
partial_out1 = self.dig["P5"]
partial_out1 += partial_data1 + partial_data2 + partial_data3
partial_data1 = self.dig["P2"] * self.t_fine
partial_data2 = self.dig["P3"] * (self.t_fine * self.t_fine)
partial_data3 = self.dig["P4"]
partial_data3 *= (self.t_fine * self.t_fine * self.t_fine)
partial_out2 = adc_P * (
self.dig["P1"] + partial_data1 + partial_data2 + partial_data3
)
partial_data1 = adc_P * adc_P
partial_data2 = self.dig["P9"] + (self.dig["P10"] * self.t_fine)
partial_data3 = partial_data1 * partial_data2
partial_data4 = partial_data3 + adc_P * adc_P * adc_P * self.dig["P11"]
comp_press = partial_out1 + partial_out2 + partial_data4
if comp_press < 30000:
comp_press = 30000
if comp_press > 125000:
comp_press = 125000
return comp_press
def _sample_bme680(self, eventtime):
self.write_register('CTRL_HUM', self.os_hum & 0x07)
meas = self.os_temp << 5 | self.os_pres << 2
self.write_register('CTRL_MEAS', [meas])
gas_wait_0 = self._calculate_gas_heater_duration(self.gas_heat_duration)
self.write_register('GAS_WAIT_0', [gas_wait_0])
res_heat_0 = self._calculate_gas_heater_resistance(self.gas_heat_temp)
self.write_register('RES_HEAT_0', [res_heat_0])
gas_config = RUN_GAS | NB_CONV_0
self.write_register('CTRL_GAS_1', [gas_config])
def data_ready(stat):
def data_ready(stat, run_gas):
new_data = (stat & EAS_NEW_DATA)
gas_done = not (stat & GAS_DONE)
meas_done = not (stat & MEASURE_DONE)
if not run_gas:
gas_done = True
return new_data and gas_done and meas_done
run_gas = False
# Check VOC once a while
if self.reactor.monotonic() - self.last_gas_time > 3:
gas_config = RUN_GAS | NB_CONV_0
self.write_register('CTRL_GAS_1', [gas_config])
run_gas = True
# Enter forced mode
meas = meas | MODE
self.write_register('CTRL_MEAS', meas)
meas = self.os_temp << 5 | self.os_pres << 2 | MODE
self.write_register('CTRL_MEAS', meas, wait=True)
max_sample_time = self.max_sample_time
if run_gas:
max_sample_time += self.gas_heat_duration / 1000
self.reactor.pause(self.reactor.monotonic() + max_sample_time)
try:
# wait until results are ready
status = self.read_register('EAS_STATUS_0', 1)[0]
while not data_ready(status):
while not data_ready(status, run_gas):
self.reactor.pause(
self.reactor.monotonic() + self.max_sample_time)
status = self.read_register('EAS_STATUS_0', 1)[0]
data = self.read_register('PRESSURE_MSB', 8)
gas_data = self.read_register('GAS_R_MSB', 2)
gas_data = [0, 0]
if run_gas:
gas_data = self.read_register('GAS_R_MSB', 2)
except Exception:
logging.exception("BME680: Error reading data")
self.temp = self.pressure = self.humidity = self.gas = .0
@@ -325,6 +568,10 @@ class BME280:
gas_raw = (gas_data[0] << 2) | ((gas_data[1] & 0xC0) >> 6)
gas_range = (gas_data[1] & 0x0F)
self.gas = self._compensate_gas(gas_raw, gas_range)
# Disable gas measurement on success
gas_config = NB_CONV_0
self.write_register('CTRL_GAS_1', [gas_config])
self.last_gas_time = self.reactor.monotonic()
if self.temp < self.min_temp or self.temp > self.max_temp:
self.printer.invoke_shutdown(
@@ -334,6 +581,43 @@ class BME280:
self._callback(self.mcu.estimated_print_time(measured_time), self.temp)
return measured_time + REPORT_TIME
def _sample_bmp180(self, eventtime):
meas = self.chip_registers['CRV_TEMP']
self.write_register('CTRL_MEAS', meas)
try:
self.reactor.pause(self.reactor.monotonic() + .01)
data = self.read_register('REG_MSB', 2)
temp_raw = (data[0] << 8) | data[1]
except Exception:
logging.exception("BMP180: Error reading temperature")
self.temp = self.pressure = .0
return self.reactor.NEVER
meas = self.chip_registers['CRV_PRES'] | (self.os_pres << 6)
self.write_register('CTRL_MEAS', meas)
try:
self.reactor.pause(self.reactor.monotonic() + .01)
data = self.read_register('REG_MSB', 3)
pressure_raw = \
((data[0] << 16)|(data[1] << 8)|data[2]) >> (8 - self.os_pres)
except Exception:
logging.exception("BMP180: Error reading pressure")
self.temp = self.pressure = .0
return self.reactor.NEVER
self.temp = self._compensate_temp_bmp180(temp_raw)
self.pressure = self._compensate_pressure_bmp180(pressure_raw) / 100.
if self.temp < self.min_temp or self.temp > self.max_temp:
self.printer.invoke_shutdown(
"BMP180 temperature %0.1f outside range of %0.1f:%.01f"
% (self.temp, self.min_temp, self.max_temp))
measured_time = self.reactor.monotonic()
self._callback(self.mcu.estimated_print_time(measured_time), self.temp)
return measured_time + REPORT_TIME
def _compensate_temp(self, raw_temp):
dig = self.dig
var1 = ((raw_temp / 16384. - (dig['T1'] / 1024.)) * dig['T2'])
@@ -416,7 +700,7 @@ class BME280:
gas_raw - 512. + var1)
return gas
def _calculate_gas_heater_resistance(self, target_temp):
def _calc_gas_heater_resistance(self, target_temp):
amb_temp = self.temp
heater_data = self.read_register('RES_HEAT_VAL', 3)
res_heat_val = get_signed_byte(heater_data[0])
@@ -431,7 +715,7 @@ class BME280:
* (1. / (1. + (res_heat_val * 0.002)))) - 25))
return int(res_heat)
def _calculate_gas_heater_duration(self, duration_ms):
def _calc_gas_heater_duration(self, duration_ms):
if duration_ms >= 4032:
duration_reg = 0xff
else:
@@ -443,24 +727,64 @@ class BME280:
return duration_reg
def _compensate_temp_bmp180(self, raw_temp):
dig = self.dig
x1 = (raw_temp - dig['AC6']) * dig['AC5'] / 32768.
x2 = dig['MC'] * 2048 / (x1 + dig['MD'])
b5 = x1 + x2
self.t_fine = b5
return (b5 + 8)/16./10.
def _compensate_pressure_bmp180(self, raw_pressure):
dig = self.dig
b5 = self.t_fine
b6 = b5 - 4000
x1 = (dig['B2'] * (b6 * b6 / 4096)) / 2048
x2 = dig['AC2'] * b6 / 2048
x3 = x1 + x2
b3 = ((int(dig['AC1'] * 4 + x3) << self.os_pres) + 2) / 4
x1 = dig['AC3'] * b6 / 8192
x2 = (dig['B1'] * (b6 * b6 / 4096)) / 65536
x3 = ((x1 + x2) + 2) / 4
b4 = dig['AC4'] * (x3 + 32768) / 32768
b7 = (raw_pressure - b3) * (50000 >> self.os_pres)
if (b7 < 0x80000000):
p = (b7 * 2) / b4
else:
p = (b7 / b4) * 2
x1 = (p / 256) * (p / 256)
x1 = (x1 * 3038) / 65536
x2 = (-7357 * p) / 65536
p = p + (x1 + x2 + 3791) / 16.
return p
def read_id(self):
# read chip id register
regs = [BME_CHIP_ID_REG]
params = self.i2c.i2c_read(regs, 1)
return bytearray(params['response'])[0]
def read_bmp3_id(self):
# read chip id register
regs = [BMP3_CHIP_ID_REG]
params = self.i2c.i2c_read(regs, 1)
return bytearray(params['response'])[0]
def read_register(self, reg_name, read_len):
# read a single register
regs = [self.chip_registers[reg_name]]
params = self.i2c.i2c_read(regs, read_len)
return bytearray(params['response'])
def write_register(self, reg_name, data):
def write_register(self, reg_name, data, wait = False):
if type(data) is not list:
data = [data]
reg = self.chip_registers[reg_name]
data.insert(0, reg)
self.i2c.i2c_write(data)
if not wait:
self.i2c.i2c_write(data)
else:
self.i2c.i2c_write_wait_ack(data)
def get_status(self, eventtime):
data = {

View File

@@ -0,0 +1,297 @@
# Tools for reading bulk sensor data from the mcu
#
# Copyright (C) 2020-2023 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging, threading, struct
# This "bulk sensor" module facilitates the processing of sensor chip
# measurements that do not require the host to respond with low
# latency. This module helps collect these measurements into batches
# that are then processed periodically by the host code (as specified
# by BatchBulkHelper.batch_interval). It supports the collection of
# thousands of sensor measurements per second.
#
# Processing measurements in batches reduces load on the mcu, reduces
# bandwidth to/from the mcu, and reduces load on the host. It also
# makes it easier to export the raw measurements via the webhooks
# system (aka API Server).
BATCH_INTERVAL = 0.500
# Helper to process accumulated messages in periodic batches
class BatchBulkHelper:
def __init__(self, printer, batch_cb, start_cb=None, stop_cb=None,
batch_interval=BATCH_INTERVAL):
self.printer = printer
self.batch_cb = batch_cb
if start_cb is None:
start_cb = (lambda: None)
self.start_cb = start_cb
if stop_cb is None:
stop_cb = (lambda: None)
self.stop_cb = stop_cb
self.is_started = False
self.batch_interval = batch_interval
self.batch_timer = None
self.client_cbs = []
self.webhooks_start_resp = {}
# Periodic batch processing
def _start(self):
if self.is_started:
return
self.is_started = True
try:
self.start_cb()
except self.printer.command_error as e:
logging.exception("BatchBulkHelper start callback error")
self.is_started = False
del self.client_cbs[:]
raise
reactor = self.printer.get_reactor()
systime = reactor.monotonic()
waketime = systime + self.batch_interval
self.batch_timer = reactor.register_timer(self._proc_batch, waketime)
def _stop(self):
del self.client_cbs[:]
self.printer.get_reactor().unregister_timer(self.batch_timer)
self.batch_timer = None
if not self.is_started:
return
try:
self.stop_cb()
except self.printer.command_error as e:
logging.exception("BatchBulkHelper stop callback error")
del self.client_cbs[:]
self.is_started = False
if self.client_cbs:
# New client started while in process of stopping
self._start()
def _proc_batch(self, eventtime):
try:
msg = self.batch_cb(eventtime)
except self.printer.command_error as e:
logging.exception("BatchBulkHelper batch callback error")
self._stop()
return self.printer.get_reactor().NEVER
if not msg:
return eventtime + self.batch_interval
for client_cb in list(self.client_cbs):
res = client_cb(msg)
if not res:
# This client no longer needs updates - unregister it
self.client_cbs.remove(client_cb)
if not self.client_cbs:
self._stop()
return self.printer.get_reactor().NEVER
return eventtime + self.batch_interval
# Client registration
def add_client(self, client_cb):
self.client_cbs.append(client_cb)
self._start()
# Webhooks registration
def _add_api_client(self, web_request):
whbatch = BatchWebhooksClient(web_request)
self.add_client(whbatch.handle_batch)
web_request.send(self.webhooks_start_resp)
def add_mux_endpoint(self, path, key, value, webhooks_start_resp):
self.webhooks_start_resp = webhooks_start_resp
wh = self.printer.lookup_object('webhooks')
wh.register_mux_endpoint(path, key, value, self._add_api_client)
# A webhooks wrapper for use by BatchBulkHelper
class BatchWebhooksClient:
def __init__(self, web_request):
self.cconn = web_request.get_client_connection()
self.template = web_request.get_dict('response_template', {})
def handle_batch(self, msg):
if self.cconn.is_closed():
return False
tmp = dict(self.template)
tmp['params'] = msg
self.cconn.send(tmp)
return True
# Helper class to store incoming messages in a queue
class BulkDataQueue:
def __init__(self, mcu, msg_name="sensor_bulk_data", oid=None):
# Measurement storage (accessed from background thread)
self.lock = threading.Lock()
self.raw_samples = []
# Register callback with mcu
mcu.register_response(self._handle_data, msg_name, oid)
def _handle_data(self, params):
with self.lock:
self.raw_samples.append(params)
def pull_queue(self):
with self.lock:
raw_samples = self.raw_samples
self.raw_samples = []
return raw_samples
def clear_queue(self):
self.pull_queue()
######################################################################
# Clock synchronization
######################################################################
# It is common for sensors to produce measurements at a fixed
# frequency. If the mcu can reliably obtain all of these
# measurements, then the code here can calculate a precision timestamp
# for them. That is, it can determine the actual sensor measurement
# frequency, the time of the first measurement, and thus a precise
# time for all measurements.
#
# This system works by having the mcu periodically report a precision
# timestamp along with the total number of measurements the sensor has
# taken as of that time. In brief, knowing the total number of
# measurements taken over an extended period provides an accurate
# estimate of measurement frequency, which can then also be utilized
# to determine the time of the first measurement.
# Helper class for chip clock synchronization via linear regression
class ClockSyncRegression:
def __init__(self, mcu, chip_clock_smooth, decay = 1. / 20.):
self.mcu = mcu
self.chip_clock_smooth = chip_clock_smooth
self.decay = decay
self.last_chip_clock = self.last_exp_mcu_clock = 0.
self.mcu_clock_avg = self.mcu_clock_variance = 0.
self.chip_clock_avg = self.chip_clock_covariance = 0.
def reset(self, mcu_clock, chip_clock):
self.mcu_clock_avg = self.last_mcu_clock = mcu_clock
self.chip_clock_avg = chip_clock
self.mcu_clock_variance = self.chip_clock_covariance = 0.
self.last_chip_clock = self.last_exp_mcu_clock = 0.
def update(self, mcu_clock, chip_clock):
# Update linear regression
decay = self.decay
diff_mcu_clock = mcu_clock - self.mcu_clock_avg
self.mcu_clock_avg += decay * diff_mcu_clock
self.mcu_clock_variance = (1. - decay) * (
self.mcu_clock_variance + diff_mcu_clock**2 * decay)
diff_chip_clock = chip_clock - self.chip_clock_avg
self.chip_clock_avg += decay * diff_chip_clock
self.chip_clock_covariance = (1. - decay) * (
self.chip_clock_covariance + diff_mcu_clock*diff_chip_clock*decay)
def set_last_chip_clock(self, chip_clock):
base_mcu, base_chip, inv_cfreq = self.get_clock_translation()
self.last_chip_clock = chip_clock
self.last_exp_mcu_clock = base_mcu + (chip_clock-base_chip) * inv_cfreq
def get_clock_translation(self):
inv_chip_freq = self.mcu_clock_variance / self.chip_clock_covariance
if not self.last_chip_clock:
return self.mcu_clock_avg, self.chip_clock_avg, inv_chip_freq
# Find mcu clock associated with future chip_clock
s_chip_clock = self.last_chip_clock + self.chip_clock_smooth
scdiff = s_chip_clock - self.chip_clock_avg
s_mcu_clock = self.mcu_clock_avg + scdiff * inv_chip_freq
# Calculate frequency to converge at future point
mdiff = s_mcu_clock - self.last_exp_mcu_clock
s_inv_chip_freq = mdiff / self.chip_clock_smooth
return self.last_exp_mcu_clock, self.last_chip_clock, s_inv_chip_freq
def get_time_translation(self):
base_mcu, base_chip, inv_cfreq = self.get_clock_translation()
clock_to_print_time = self.mcu.clock_to_print_time
base_time = clock_to_print_time(base_mcu)
inv_freq = clock_to_print_time(base_mcu + inv_cfreq) - base_time
return base_time, base_chip, inv_freq
MAX_BULK_MSG_SIZE = 51
# Read sensor_bulk_data and calculate timestamps for devices that take
# samples at a fixed frequency (and produce fixed data size samples).
class FixedFreqReader:
def __init__(self, mcu, chip_clock_smooth, unpack_fmt):
self.mcu = mcu
self.clock_sync = ClockSyncRegression(mcu, chip_clock_smooth)
unpack = struct.Struct(unpack_fmt)
self.unpack_from = unpack.unpack_from
self.bytes_per_sample = unpack.size
self.samples_per_block = MAX_BULK_MSG_SIZE // self.bytes_per_sample
self.last_sequence = self.max_query_duration = 0
self.last_overflows = 0
self.bulk_queue = self.oid = self.query_status_cmd = None
def setup_query_command(self, msgformat, oid, cq):
# Lookup sensor query command (that responds with sensor_bulk_status)
self.oid = oid
self.query_status_cmd = self.mcu.lookup_query_command(
msgformat, "sensor_bulk_status oid=%c clock=%u query_ticks=%u"
" next_sequence=%hu buffered=%u possible_overflows=%hu",
oid=oid, cq=cq)
# Read sensor_bulk_data messages and store in a queue
self.bulk_queue = BulkDataQueue(self.mcu, oid=oid)
def get_last_overflows(self):
return self.last_overflows
def _clear_duration_filter(self):
self.max_query_duration = 1 << 31
def note_start(self):
self.last_sequence = 0
self.last_overflows = 0
# Clear local queue (clear any stale samples from previous session)
self.bulk_queue.clear_queue()
# Set initial clock
self._clear_duration_filter()
self._update_clock(is_reset=True)
self._clear_duration_filter()
def note_end(self):
# Clear local queue (free no longer needed memory)
self.bulk_queue.clear_queue()
def _update_clock(self, is_reset=False):
params = self.query_status_cmd.send([self.oid])
mcu_clock = self.mcu.clock32_to_clock64(params['clock'])
seq_diff = (params['next_sequence'] - self.last_sequence) & 0xffff
self.last_sequence += seq_diff
buffered = params['buffered']
po_diff = (params['possible_overflows'] - self.last_overflows) & 0xffff
self.last_overflows += po_diff
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 = (self.last_sequence * self.samples_per_block
+ buffered // self.bytes_per_sample)
# The "chip clock" is the message counter plus .5 for average
# inaccuracy of query responses and plus .5 for assumed offset
# of hardware processing time.
chip_clock = msg_count + 1
avg_mcu_clock = mcu_clock + duration // 2
if is_reset:
self.clock_sync.reset(avg_mcu_clock, chip_clock)
else:
self.clock_sync.update(avg_mcu_clock, chip_clock)
# Convert sensor_bulk_data responses into list of samples
def pull_samples(self):
# Query MCU for sample timing and update clock synchronization
self._update_clock()
# Pull sensor_bulk_data messages from local queue
raw_samples = self.bulk_queue.pull_queue()
if not raw_samples:
return []
# Load variables to optimize inner loop below
last_sequence = self.last_sequence
time_base, chip_base, inv_freq = self.clock_sync.get_time_translation()
unpack_from = self.unpack_from
bytes_per_sample = self.bytes_per_sample
samples_per_block = self.samples_per_block
# Process every message in raw_samples
count = seq = 0
samples = [None] * (len(raw_samples) * samples_per_block)
for params in raw_samples:
seq_diff = (params['sequence'] - last_sequence) & 0xffff
seq_diff -= (seq_diff & 0x8000) << 1
seq = last_sequence + seq_diff
msg_cdiff = seq * samples_per_block - chip_base
data = params['data']
for i in range(len(data) // bytes_per_sample):
ptime = time_base + (msg_cdiff + i) * inv_freq
udata = unpack_from(data, i * bytes_per_sample)
samples[count] = (ptime,) + udata
count += 1
self.clock_sync.set_last_chip_clock(seq * samples_per_block + i)
del samples[count:]
return samples

View File

@@ -160,7 +160,7 @@ class MCU_I2C:
% (self.oid, speed, addr))
self.cmd_queue = self.mcu.alloc_command_queue()
self.mcu.register_config_callback(self.build_config)
self.i2c_write_cmd = self.i2c_read_cmd = self.i2c_modify_bits_cmd = None
self.i2c_write_cmd = self.i2c_read_cmd = None
def get_oid(self):
return self.oid
def get_mcu(self):
@@ -180,9 +180,6 @@ class MCU_I2C:
"i2c_read oid=%c reg=%*s read_len=%u",
"i2c_read_response oid=%c response=%*s", oid=self.oid,
cq=self.cmd_queue)
self.i2c_modify_bits_cmd = self.mcu.lookup_command(
"i2c_modify_bits oid=%c reg=%*s clear_set_bits=%*s",
cq=self.cmd_queue)
def i2c_write(self, data, minclock=0, reqclock=0):
if self.i2c_write_cmd is None:
# Send setup message via mcu initialization
@@ -192,21 +189,11 @@ class MCU_I2C:
return
self.i2c_write_cmd.send([self.oid, data],
minclock=minclock, reqclock=reqclock)
def i2c_write_wait_ack(self, data, minclock=0, reqclock=0):
self.i2c_write_cmd.send_wait_ack([self.oid, data],
minclock=minclock, reqclock=reqclock)
def i2c_read(self, write, read_len):
return self.i2c_read_cmd.send([self.oid, write, read_len])
def i2c_modify_bits(self, reg, clear_bits, set_bits,
minclock=0, reqclock=0):
clearset = clear_bits + set_bits
if self.i2c_modify_bits_cmd is None:
# Send setup message via mcu initialization
reg_msg = "".join(["%02x" % (x,) for x in reg])
clearset_msg = "".join(["%02x" % (x,) for x in clearset])
self.mcu.add_config_cmd(
"i2c_modify_bits oid=%d reg=%s clear_set_bits=%s" % (
self.oid, reg_msg, clearset_msg), is_init=True)
return
self.i2c_modify_bits_cmd.send([self.oid, reg, clearset],
minclock=minclock, reqclock=reqclock)
def MCU_I2C_from_config(config, default_addr=None, default_speed=100000):
# Load bus parameters

View File

@@ -1,6 +1,6 @@
# Support for button detection and callbacks
#
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2018-2023 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
@@ -57,10 +57,9 @@ class MCU_buttons:
def handle_buttons_state(self, params):
# Expand the message ack_count from 8-bit
ack_count = self.ack_count
ack_diff = (ack_count - params['ack_count']) & 0xff
if ack_diff & 0x80:
ack_diff -= 0x100
msg_ack_count = ack_count - ack_diff
ack_diff = (params['ack_count'] - ack_count) & 0xff
ack_diff -= (ack_diff & 0x80) << 1
msg_ack_count = ack_count + ack_diff
# Determine new buttons
buttons = bytearray(params['state'])
new_count = msg_ack_count + len(buttons) - self.ack_count
@@ -70,17 +69,17 @@ class MCU_buttons:
# Send ack to MCU
self.ack_cmd.send([self.oid, new_count])
self.ack_count += new_count
# Call self.handle_button() with this event in main thread
for nb in new_buttons:
self.reactor.register_async_callback(
(lambda e, s=self, b=nb: s.handle_button(e, b)))
def handle_button(self, eventtime, button):
button ^= self.invert
changed = button ^ self.last_button
for mask, shift, callback in self.callbacks:
if changed & mask:
callback(eventtime, (button & mask) >> shift)
self.last_button = button
# Invoke callbacks with this event in main thread
btime = params['#receive_time']
for button in new_buttons:
button ^= self.invert
changed = button ^ self.last_button
self.last_button = button
for mask, shift, callback in self.callbacks:
if changed & mask:
state = (button & mask) >> shift
self.reactor.register_async_callback(
(lambda et, c=callback, bt=btime, s=state: c(bt, s)))
######################################################################
@@ -105,7 +104,7 @@ class MCU_ADC_buttons:
self.max_value = 0.
ppins = printer.lookup_object('pins')
self.mcu_adc = ppins.setup_pin('adc', self.pin)
self.mcu_adc.setup_minmax(ADC_SAMPLE_TIME, ADC_SAMPLE_COUNT)
self.mcu_adc.setup_adc_sample(ADC_SAMPLE_TIME, ADC_SAMPLE_COUNT)
self.mcu_adc.setup_adc_callback(ADC_REPORT_TIME, self.adc_callback)
query_adc = printer.lookup_object('query_adc')
query_adc.register_adc('adc_button:' + pin.strip(), self.mcu_adc)

View File

@@ -62,9 +62,7 @@ class ControllerFan:
self.last_on += 1
if speed != self.last_speed:
self.last_speed = speed
curtime = self.printer.get_reactor().monotonic()
print_time = self.fan.get_mcu().estimated_print_time(curtime)
self.fan.set_speed(print_time + PIN_MIN_TIME, speed)
self.fan.set_speed(speed)
return eventtime + 1.
def load_config_prefix(config):

View File

@@ -0,0 +1,209 @@
# Support for YHCB2004 (20x4 text) LCD displays based on AiP31068 controller
#
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2018 Eric Callahan <arksine.code@gmail.com>
# Copyright (C) 2021 Marc-Andre Denis <marcadenis@msn.com>
# Copyright (C) 2024 Alexander Bazarov <oss@bazarov.dev>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
# This file is a modified version of hd44780_spi.py, introducing slightly
# different protocol as implemented in Marlin FW (based on
# https://github.com/red-scorp/LiquidCrystal_AIP31068 ).
# In addition, a hack is used to send 8 commands, each 9 bits, at once,
# allowing the transmission of a full 9 bytes.
# This helps avoid modifying the SW_SPI driver to handle non-8-bit data.
from .. import bus
LINE_LENGTH_DEFAULT=20
LINE_LENGTH_OPTIONS={16:16, 20:20}
TextGlyphs = { 'right_arrow': b'\x7e' }
# Each command is 9 bits long:
# 1 bit for RS (Register Select) - 0 for command, 1 for data
# 8 bits for the command/data
# Command is a bitwise OR of CMND(=opcode) and flg_CMND(=parameters) multiplied
# by 1 or 0 as En/Dis flag.
# cmd = CMND | flg_CMND.param0*0 | flg_CMND.param1*1
# or just by OR with enabled flags:
# cmd = CMND | flg_CMND.param1
class CMND:
CLR = 1 # Clear display
HOME = 2 # Return home
ENTERY_MODE = 2**2 # Entry mode set
DISPLAY = 2**3 # Display on/off control
SHIFT = 2**4 # Cursor or display shift
FUNCTION = 2**5 # Function set
CGRAM = 2**6 # Character Generator RAM
DDRAM = 2**7 # Display Data RAM
WRITE_RAM = 2**8 # Write to RAM
# Define flags for all commands:
class flg_ENTERY_MODE:
INC = 2**1 # Increment
SHIFT = 2**0 # Shift display
class flg_DISPLAY:
ON = 2**2 # Display ON
CURSOR = 2**1 # Cursor ON
BLINK = 2**0 # Blink ON
class flg_SHIFT:
WHOLE_DISPLAY = 2**3 # Shift whole display
RIGHT = 2**2 # Shift right
class flg_FUNCTION:
TWO_LINES = 2**3 # 2-line display mode
FIVE_BY_ELEVEN = 2**2 # 5x11 dot character font
class flg_CGRAM:
MASK = 0b00111111 # CGRAM address mask
class flg_DDRAM:
MASK = 0b01111111 # DDRAM address mask
class flg_WRITE_RAM:
MASK = 0b11111111 # Write RAM mask
DISPLAY_INIT_CMNDS= [
# CMND.CLR - no need as framebuffer will rewrite all
CMND.HOME, # move cursor to home (0x00)
CMND.ENTERY_MODE | flg_ENTERY_MODE.INC, # increment cursor and no shift
CMND.DISPLAY | flg_DISPLAY.ON, # keep cursor and blinking off
CMND.SHIFT | flg_SHIFT.RIGHT, # shift right cursor only
CMND.FUNCTION | flg_FUNCTION.TWO_LINES, # 2-line display mode, 5x8 dots
]
class aip31068_spi:
def __init__(self, config):
self.printer = config.get_printer()
# spi config
self.spi = bus.MCU_SPI_from_config(
config, 0x00, pin_option="latch_pin") # (config, mode, cs_name)
self.mcu = self.spi.get_mcu()
self.icons = {}
self.line_length = config.getchoice('line_length', LINE_LENGTH_OPTIONS,
LINE_LENGTH_DEFAULT)
# each controller's line is 2 lines on the display and hence twice
# line length
self.text_framebuffers = [bytearray(b' '*2*self.line_length),
bytearray(b' '*2*self.line_length)]
self.glyph_framebuffer = bytearray(64)
# all_framebuffers - list of tuples per buffer type.
# Each tuple contains:
# 1. the updated framebuffer
# 2. a copy of the old framebuffer == data on the display
# 3. the command to send to write to this buffer
# Then flush() will compare new data with data on the display
# and send only the differences to the display controller
# and update the old framebuffer with the new data
# (immutable tuple is allowed to store mutable bytearray)
self.all_framebuffers = [
# Text framebuffers
(self.text_framebuffers[0], bytearray(b'~'*2*self.line_length),
CMND.DDRAM | (flg_DDRAM.MASK & 0x00) ),
(self.text_framebuffers[1], bytearray(b'~'*2*self.line_length),
CMND.DDRAM | (flg_DDRAM.MASK & 0x40) ),
# Glyph framebuffer
(self.glyph_framebuffer, bytearray(b'~'*64),
CMND.CGRAM | (flg_CGRAM.MASK & 0x00) ) ]
@staticmethod
def encode(data, width = 9):
encoded_bytes = []
accumulator = 0 # To accumulate bits
acc_bits = 0 # Count of bits in the accumulator
for num in data:
# check that num will fit in width bits
if num >= (1 << width):
raise ValueError("Number {} does not fit in {} bits".
format(num, width))
# Shift the current number into the accumulator from the right
accumulator = (accumulator << width) | num
acc_bits += width # Update the count of bits in the accumulator
# While we have at least 8 bits, form a byte and append it
while acc_bits >= 8:
acc_bits -= 8 # Decrease bit count by 8
# Extract the 8 most significant bits to form a byte
byte = (accumulator >> acc_bits) & 0xFF
# Remove msb 8 bits from the accumulator
accumulator &= (1 << acc_bits) - 1
encoded_bytes.append(byte)
# Handle any remaining bits by padding them on the right to byte
if acc_bits > 0:
last_byte = accumulator << (8 - acc_bits)
encoded_bytes.append(last_byte)
return encoded_bytes
def send(self, data, minclock=0):
# different commands have different processing time
# to avoid timing violation pad with some fast command, e.g. ENTRY_MODE
# that has execution time of 39us (for comparison CLR is 1.53ms)
pad = CMND.ENTERY_MODE | flg_ENTERY_MODE.INC
for i in range(0, len(data), 8):
# Take a slice of 8 numbers
group = data[i:i+8]
# Pad the group if it has fewer than 8 elements
if len(group) < 8:
group.extend([pad] * (8 - len(group)))
self.spi.spi_send(self.encode(group), minclock)
def flush(self):
# Find all differences in the framebuffers and send them to the chip
for new_data, old_data, fb_cmnd in self.all_framebuffers:
if new_data == old_data:
continue
# Find the position of all changed bytes in this framebuffer
diffs = [[i, 1] for i, (n, o) in enumerate(zip(new_data, old_data))
if n != o]
# Batch together changes that are close to each other
for i in range(len(diffs)-2, -1, -1):
pos, count = diffs[i]
nextpos, nextcount = diffs[i+1]
if pos + 4 >= nextpos and nextcount < 16:
diffs[i][1] = nextcount + (nextpos - pos)
del diffs[i+1]
# Transmit changes
for pos, count in diffs:
chip_pos = pos
self.send([fb_cmnd + chip_pos])
self.send([CMND.WRITE_RAM | byte for byte in
new_data[pos:pos+count]])
old_data[:] = new_data
def init(self):
curtime = self.printer.get_reactor().monotonic()
print_time = self.mcu.estimated_print_time(curtime)
for i, cmds in enumerate(DISPLAY_INIT_CMNDS):
minclock = self.mcu.print_time_to_clock(print_time + i * .100)
self.send([cmds], minclock=minclock)
self.flush()
def write_text(self, x, y, data):
if x + len(data) > self.line_length:
data = data[:self.line_length - min(x, self.line_length)]
pos = x + ((y & 0x02) >> 1) * self.line_length
self.text_framebuffers[y & 1][pos:pos+len(data)] = data
def set_glyphs(self, glyphs):
for glyph_name, glyph_data in glyphs.items():
data = glyph_data.get('icon5x8')
if data is not None:
self.icons[glyph_name] = data
def write_glyph(self, x, y, glyph_name):
data = self.icons.get(glyph_name)
if data is not None:
slot, bits = data
self.write_text(x, y, [slot])
self.glyph_framebuffer[slot * 8:(slot + 1) * 8] = bits
return 1
char = TextGlyphs.get(glyph_name)
if char is not None:
# Draw character
self.write_text(x, y, char)
return 1
return 0
def write_graphics(self, x, y, data):
pass # this display supports only hardcoded or 8 user defined glyphs
def clear(self):
spaces = b' ' * 2*self.line_length
self.text_framebuffers[0][:] = spaces
self.text_framebuffers[1][:] = spaces
def get_dimensions(self):
return (self.line_length, 4)

View File

@@ -6,7 +6,7 @@
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging, os, ast
from . import hd44780, hd44780_spi, st7920, uc1701, menu
from . import aip31068_spi, hd44780, hd44780_spi, st7920, uc1701, menu
# Normal time between each screen redraw
REDRAW_TIME = 0.500
@@ -17,7 +17,8 @@ LCD_chips = {
'st7920': st7920.ST7920, 'emulated_st7920': st7920.EmulatedST7920,
'hd44780': hd44780.HD44780, 'uc1701': uc1701.UC1701,
'ssd1306': uc1701.SSD1306, 'sh1106': uc1701.SH1106,
'hd44780_spi': hd44780_spi.hd44780_spi
'hd44780_spi': hd44780_spi.hd44780_spi,
'aip31068_spi':aip31068_spi.aip31068_spi
}
# Storage of [display_template my_template] config sections

View File

@@ -8,7 +8,7 @@ import logging
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
LINE_LENGTH_DEFAULT=20
LINE_LENGTH_OPTIONS={16:16, 20:20}
LINE_LENGTH_OPTIONS=[16, 20]
TextGlyphs = { 'right_arrow': b'\x7e' }

View File

@@ -9,7 +9,7 @@ import logging
from .. import bus
LINE_LENGTH_DEFAULT=20
LINE_LENGTH_OPTIONS={16:16, 20:20}
LINE_LENGTH_OPTIONS=[16, 20]
TextGlyphs = { 'right_arrow': b'\x7e' }

View File

@@ -18,7 +18,7 @@ class MenuKeys:
# Register rotary encoder
encoder_pins = config.get('encoder_pins', None)
encoder_steps_per_detent = config.getchoice('encoder_steps_per_detent',
{2: 2, 4: 4}, 4)
[2, 4], 4)
if encoder_pins is not None:
try:
pin1, pin2 = encoder_pins.split(',')

View File

@@ -1,16 +1,15 @@
# Support for "dotstar" leds
#
# Copyright (C) 2019-2022 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2019-2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import bus
from . import bus, led
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
class PrinterDotstar:
def __init__(self, config):
self.printer = printer = config.get_printer()
name = config.get_name().split()[1]
# Configure a software spi bus
ppins = printer.lookup_object('pins')
data_pin_params = ppins.lookup_pin(config.get('data_pin'))
@@ -23,9 +22,8 @@ class PrinterDotstar:
self.spi = bus.MCU_SPI(mcu, None, None, 0, 500000, sw_spi_pins)
# Initialize color data
self.chain_count = config.getint('chain_count', 1, minval=1)
pled = printer.load_object(config, "led")
self.led_helper = pled.setup_helper(config, self.update_leds,
self.chain_count)
self.led_helper = led.LEDHelper(config, self.update_leds,
self.chain_count)
self.prev_data = None
# Register commands
printer.register_event_handler("klippy:connect", self.handle_connect)

View File

@@ -191,7 +191,6 @@ class EndstopPhases:
def generate_stats(self, stepper_name, phase_calc):
phase_history = phase_calc.phase_history
wph = phase_history + phase_history
count = sum(phase_history)
phases = len(phase_history)
half_phases = phases // 2
res = []

133
klippy/extras/error_mcu.py Normal file
View File

@@ -0,0 +1,133 @@
# More verbose information on micro-controller errors
#
# Copyright (C) 2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
message_shutdown = """
Once the underlying issue is corrected, use the
"FIRMWARE_RESTART" command to reset the firmware, reload the
config, and restart the host software.
Printer is shutdown
"""
message_protocol_error1 = """
This is frequently caused by running an older version of the
firmware on the MCU(s). Fix by recompiling and flashing the
firmware.
"""
message_protocol_error2 = """
Once the underlying issue is corrected, use the "RESTART"
command to reload the config and restart the host software.
"""
message_mcu_connect_error = """
Once the underlying issue is corrected, use the
"FIRMWARE_RESTART" command to reset the firmware, reload the
config, and restart the host software.
Error configuring printer
"""
Common_MCU_errors = {
("Timer too close",): """
This often indicates the host computer is overloaded. Check
for other processes consuming excessive CPU time, high swap
usage, disk errors, overheating, unstable voltage, or
similar system problems on the host computer.""",
("Missed scheduling of next ",): """
This is generally indicative of an intermittent
communication failure between micro-controller and host.""",
("ADC out of range",): """
This generally occurs when a heater temperature exceeds
its configured min_temp or max_temp.""",
("Rescheduled timer in the past", "Stepper too far in past"): """
This generally occurs when the micro-controller has been
requested to step at a rate higher than it is capable of
obtaining.""",
("Command request",): """
This generally occurs in response to an M112 G-Code command
or in response to an internal error in the host software.""",
}
def error_hint(msg):
for prefixes, help_msg in Common_MCU_errors.items():
for prefix in prefixes:
if msg.startswith(prefix):
return help_msg
return ""
class PrinterMCUError:
def __init__(self, config):
self.printer = config.get_printer()
self.clarify_callbacks = {}
self.printer.register_event_handler("klippy:notify_mcu_shutdown",
self._handle_notify_mcu_shutdown)
self.printer.register_event_handler("klippy:notify_mcu_error",
self._handle_notify_mcu_error)
def add_clarify(self, msg, callback):
self.clarify_callbacks.setdefault(msg, []).append(callback)
def _check_mcu_shutdown(self, msg, details):
mcu_name = details['mcu']
mcu_msg = details['reason']
event_type = details['event_type']
prefix = "MCU '%s' shutdown: " % (mcu_name,)
if event_type == 'is_shutdown':
prefix = "Previous MCU '%s' shutdown: " % (mcu_name,)
# Lookup generic hint
hint = error_hint(mcu_msg)
# Add per instance help
clarify = [cb(msg, details)
for cb in self.clarify_callbacks.get(mcu_msg, [])]
clarify = [cm for cm in clarify if cm is not None]
clarify_msg = ""
if clarify:
clarify_msg = "\n".join(["", ""] + clarify + [""])
# Update error message
newmsg = "%s%s%s%s%s" % (prefix, mcu_msg, clarify_msg,
hint, message_shutdown)
self.printer.update_error_msg(msg, newmsg)
def _handle_notify_mcu_shutdown(self, msg, details):
if msg == "MCU shutdown":
self._check_mcu_shutdown(msg, details)
else:
self.printer.update_error_msg(msg, "%s%s" % (msg, message_shutdown))
def _check_protocol_error(self, msg, details):
host_version = self.printer.start_args['software_version']
msg_update = []
msg_updated = []
for mcu_name, mcu in self.printer.lookup_objects('mcu'):
try:
mcu_version = mcu.get_status()['mcu_version']
except:
logging.exception("Unable to retrieve mcu_version from mcu")
continue
if mcu_version != host_version:
msg_update.append("%s: Current version %s"
% (mcu_name.split()[-1], mcu_version))
else:
msg_updated.append("%s: Current version %s"
% (mcu_name.split()[-1], mcu_version))
if not msg_update:
msg_update.append("<none>")
if not msg_updated:
msg_updated.append("<none>")
newmsg = ["MCU Protocol error",
message_protocol_error1,
"Your Klipper version is: %s" % (host_version,),
"MCU(s) which should be updated:"]
newmsg += msg_update + ["Up-to-date MCU(s):"] + msg_updated
newmsg += [message_protocol_error2, details['error']]
self.printer.update_error_msg(msg, "\n".join(newmsg))
def _check_mcu_connect_error(self, msg, details):
newmsg = "%s%s" % (details['error'], message_mcu_connect_error)
self.printer.update_error_msg(msg, newmsg)
def _handle_notify_mcu_error(self, msg, details):
if msg == "Protocol error":
self._check_protocol_error(msg, details)
elif msg == "MCU error during connect":
self._check_mcu_connect_error(msg, details)
def load_config(config):
return PrinterMCUError(config)

View File

@@ -234,7 +234,7 @@ class ExcludeObject:
elif current:
if not self.current_object:
gcmd.respond_error('There is no current object to cancel')
raise self.gcode.error('There is no current object to cancel')
else:
self._exclude_object(self.current_object)

View File

@@ -1,17 +1,14 @@
# Printer cooling fan
#
# Copyright (C) 2016-2020 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2016-2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import pulse_counter
FAN_MIN_TIME = 0.100
from . import pulse_counter, output_pin
class Fan:
def __init__(self, config, default_shutdown_speed=0.):
self.printer = config.get_printer()
self.last_fan_value = 0.
self.last_fan_time = 0.
self.last_fan_value = self.last_req_value = 0.
# Read config
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
self.kick_start_time = config.getfloat('kick_start_time', 0.1,
@@ -36,6 +33,10 @@ class Fan:
self.enable_pin = ppins.setup_pin('digital_out', enable_pin)
self.enable_pin.setup_max_duration(0.)
# Create gcode request queue
self.gcrq = output_pin.GCodeRequestQueue(config, self.mcu_fan.get_mcu(),
self._apply_speed)
# Setup tachometer
self.tachometer = FanTachometer(config)
@@ -45,37 +46,37 @@ class Fan:
def get_mcu(self):
return self.mcu_fan.get_mcu()
def set_speed(self, print_time, value):
def _apply_speed(self, print_time, value):
if value < self.off_below:
value = 0.
value = max(0., min(self.max_power, value * self.max_power))
if value == self.last_fan_value:
return
print_time = max(self.last_fan_time + FAN_MIN_TIME, print_time)
return "discard", 0.
if self.enable_pin:
if value > 0 and self.last_fan_value == 0:
self.enable_pin.set_digital(print_time, 1)
elif value == 0 and self.last_fan_value > 0:
self.enable_pin.set_digital(print_time, 0)
if (value and value < self.max_power and self.kick_start_time
if (value and self.kick_start_time
and (not self.last_fan_value or value - self.last_fan_value > .5)):
# Run fan at full speed for specified kick_start_time
self.last_req_value = value
self.last_fan_value = self.max_power
self.mcu_fan.set_pwm(print_time, self.max_power)
print_time += self.kick_start_time
return "delay", self.kick_start_time
self.last_fan_value = self.last_req_value = value
self.mcu_fan.set_pwm(print_time, value)
self.last_fan_time = print_time
self.last_fan_value = value
def set_speed(self, value, print_time=None):
self.gcrq.send_async_request(value, print_time)
def set_speed_from_command(self, value):
toolhead = self.printer.lookup_object('toolhead')
toolhead.register_lookahead_callback((lambda pt:
self.set_speed(pt, value)))
self.gcrq.queue_gcode_request(value)
def _handle_request_restart(self, print_time):
self.set_speed(print_time, 0.)
self.set_speed(0., print_time)
def get_status(self, eventtime):
tachometer_status = self.tachometer.get_status(eventtime)
return {
'speed': self.last_fan_value,
'speed': self.last_req_value,
'rpm': tachometer_status['rpm'],
}

View File

@@ -1,9 +1,10 @@
# Support fans that are controlled by gcode
#
# Copyright (C) 2016-2020 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2016-2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import fan
import logging
from . import fan, output_pin
class PrinterFanGeneric:
cmd_SET_FAN_SPEED_help = "Sets the speed of a fan"
@@ -12,6 +13,9 @@ class PrinterFanGeneric:
self.fan = fan.Fan(config, default_shutdown_speed=0.)
self.fan_name = config.get_name().split()[-1]
# Template handling
self.template_eval = output_pin.lookup_template_eval(config)
gcode = self.printer.lookup_object("gcode")
gcode.register_mux_command("SET_FAN_SPEED", "FAN",
self.fan_name,
@@ -20,8 +24,21 @@ class PrinterFanGeneric:
def get_status(self, eventtime):
return self.fan.get_status(eventtime)
def _template_update(self, text):
try:
value = float(text)
except ValueError as e:
logging.exception("fan_generic template render error")
self.fan.set_speed(value)
def cmd_SET_FAN_SPEED(self, gcmd):
speed = gcmd.get_float('SPEED', 0.)
speed = gcmd.get_float('SPEED', None, 0.)
template = gcmd.get('TEMPLATE', None)
if (speed is None) == (template is None):
raise gcmd.error("SET_FAN_SPEED must specify SPEED or TEMPLATE")
# Check for template setting
if template is not None:
self.template_eval.set_template(gcmd, self._template_update)
return
self.fan.set_speed_from_command(speed)
def load_config_prefix(config):

View File

@@ -86,11 +86,13 @@ class ForceMove:
0., 0., 0., axis_r, 0., 0., 0., cruise_v, accel)
print_time = print_time + accel_t + cruise_t + accel_t
stepper.generate_steps(print_time)
self.trapq_finalize_moves(self.trapq, print_time + 99999.9)
self.trapq_finalize_moves(self.trapq, print_time + 99999.9,
print_time + 99999.9)
stepper.set_trapq(prev_trapq)
stepper.set_stepper_kinematics(prev_sk)
toolhead.note_kinematic_activity(print_time)
toolhead.note_mcu_movequeue_activity(print_time)
toolhead.dwell(accel_t + cruise_t + accel_t)
toolhead.flush_step_generation()
def _lookup_stepper(self, gcmd):
name = gcmd.get('STEPPER')
if name not in self.steppers:
@@ -129,8 +131,12 @@ class ForceMove:
x = gcmd.get_float('X', curpos[0])
y = gcmd.get_float('Y', curpos[1])
z = gcmd.get_float('Z', curpos[2])
logging.info("SET_KINEMATIC_POSITION pos=%.3f,%.3f,%.3f", x, y, z)
toolhead.set_position([x, y, z, curpos[3]], homing_axes=(0, 1, 2))
clear = gcmd.get('CLEAR', '').lower()
clear_axes = "".join([a for a in "xyz" if a in clear])
logging.info("SET_KINEMATIC_POSITION pos=%.3f,%.3f,%.3f clear=%s",
x, y, z, clear_axes)
toolhead.set_position([x, y, z, curpos[3]], homing_axes="xyz")
toolhead.get_kinematics().clear_homing_state(clear_axes)
def load_config(config):
return ForceMove(config)

Some files were not shown because too many files have changed in this diff Show More