Compare commits
448 Commits
work-heate
...
umo2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aadda6fb3 | ||
|
|
159b71e51e | ||
|
|
718be7c6a3 | ||
|
|
eb7bdf18ad | ||
|
|
fe44dd8baa | ||
|
|
ae010215e7 | ||
|
|
eec81683eb | ||
|
|
1965298ab0 | ||
|
|
9a1ac45d19 | ||
|
|
b817848567 | ||
|
|
3a11645afe | ||
|
|
7ed7791723 | ||
|
|
3b68769ea5 | ||
|
|
2ddfa32dd8 | ||
|
|
371647109f | ||
|
|
91b5e8e942 | ||
|
|
d34d3b05b8 | ||
|
|
78462cff4c | ||
|
|
edbfc6f856 | ||
|
|
d6d8587289 | ||
|
|
2919f37343 | ||
|
|
dd4cc8eb4c | ||
|
|
c520bf981d | ||
|
|
c454e88d9a | ||
|
|
b5e573957c | ||
|
|
6d59279438 | ||
|
|
1d569a6631 | ||
|
|
7b25d1c06f | ||
|
|
864c78f24a | ||
|
|
c09ca4cf5a | ||
|
|
6f685e9e01 | ||
|
|
128226fe8a | ||
|
|
5cbe7d83e8 | ||
|
|
9399e738bc | ||
|
|
126275d1f4 | ||
|
|
f8da8099d5 | ||
|
|
bcd4510958 | ||
|
|
3ef760c18f | ||
|
|
cfc58d3ce7 | ||
|
|
5eb07966b5 | ||
|
|
e1ba7c17ce | ||
|
|
0df40b43e8 | ||
|
|
17ce45d212 | ||
|
|
39d01158ba | ||
|
|
73c6674306 | ||
|
|
c78dd6a00a | ||
|
|
d5c031bc13 | ||
|
|
2cbb895978 | ||
|
|
e1176e4dfb | ||
|
|
6773ab074b | ||
|
|
4a567c8d10 | ||
|
|
60879fd298 | ||
|
|
ef4c76fe94 | ||
|
|
116b304541 | ||
|
|
3219712c17 | ||
|
|
b761b8c654 | ||
|
|
a209d4db5b | ||
|
|
354b1e666b | ||
|
|
4691243179 | ||
|
|
4e4a5c6336 | ||
|
|
9323a5dfe2 | ||
|
|
b724b3a348 | ||
|
|
317f8c94c8 | ||
|
|
9c0d0f6a72 | ||
|
|
5923a2e3a1 | ||
|
|
8d67e1a4e9 | ||
|
|
33bd67f9b7 | ||
|
|
993cec0891 | ||
|
|
697c6e8d28 | ||
|
|
2585accfeb | ||
|
|
37ddab223f | ||
|
|
119d007058 | ||
|
|
1931b11001 | ||
|
|
c01e6eee1d | ||
|
|
42fbf8256f | ||
|
|
9346ad1914 | ||
|
|
0e52f03b5b | ||
|
|
f54b7b9376 | ||
|
|
5666b88c69 | ||
|
|
889be5b275 | ||
|
|
607d0b4237 | ||
|
|
d120a313b7 | ||
|
|
4d4b9684a5 | ||
|
|
14cbb8dd2d | ||
|
|
aa3388cc59 | ||
|
|
d6902240dd | ||
|
|
105ce35e1b | ||
|
|
c0ca4c5cc7 | ||
|
|
cfa48fe39f | ||
|
|
2dd73d0431 | ||
|
|
d25602e88d | ||
|
|
1f3b4cc749 | ||
|
|
8e58f8fb39 | ||
|
|
f4130aa948 | ||
|
|
de182b1d14 | ||
|
|
f5956b5395 | ||
|
|
8d7e487149 | ||
|
|
eb43b20e3b | ||
|
|
388fe1b23f | ||
|
|
f6d878a898 | ||
|
|
b3e894f241 | ||
|
|
3dbac01e1d | ||
|
|
69507a0354 | ||
|
|
42c9031c81 | ||
|
|
cb0c38f7d8 | ||
|
|
0181023954 | ||
|
|
07b3726d31 | ||
|
|
28a4baf95c | ||
|
|
14685bf77f | ||
|
|
b1011e3fb1 | ||
|
|
17b8ce4c6b | ||
|
|
9090377bbc | ||
|
|
2d4589949c | ||
|
|
8c01be8c75 | ||
|
|
3a015cd00d | ||
|
|
841a9ca2f7 | ||
|
|
fe9eff8ce3 | ||
|
|
82f540bb73 | ||
|
|
ed36041b67 | ||
|
|
1af219fad6 | ||
|
|
6c1d5d912a | ||
|
|
ee0bc3d697 | ||
|
|
64e01f03a2 | ||
|
|
7201f41664 | ||
|
|
d40fd2190d | ||
|
|
4c21e1d00f | ||
|
|
2082300309 | ||
|
|
53acdfd0a5 | ||
|
|
a537ae0ceb | ||
|
|
307c03e480 | ||
|
|
64d6f110a9 | ||
|
|
447908ce0c | ||
|
|
bb281834b0 | ||
|
|
9dbfc76d9d | ||
|
|
ae536b4786 | ||
|
|
8928c394cf | ||
|
|
01422da951 | ||
|
|
f06eeb5c7a | ||
|
|
ca83c13f37 | ||
|
|
8627c94d6a | ||
|
|
6f87a4e685 | ||
|
|
b5aea5b774 | ||
|
|
fd55dd9e9d | ||
|
|
885f63cff0 | ||
|
|
efabe63357 | ||
|
|
1dc9aa8e19 | ||
|
|
9aba1a8536 | ||
|
|
81277154d2 | ||
|
|
d444289111 | ||
|
|
89ffbbed4c | ||
|
|
cc6736c3e3 | ||
|
|
1cc6398074 | ||
|
|
1e045e8ee0 | ||
|
|
f7e33df99d | ||
|
|
4504c0333f | ||
|
|
554ae78d8c | ||
|
|
ee79d0e307 | ||
|
|
7b697105b3 | ||
|
|
3cf8899a5a | ||
|
|
b7c243db19 | ||
|
|
5b2f8104c7 | ||
|
|
cf3bedfbdc | ||
|
|
7f4f696f10 | ||
|
|
9c37a918db | ||
|
|
f2b68fef73 | ||
|
|
c352617c30 | ||
|
|
5d1f773ffb | ||
|
|
da8e0a6e50 | ||
|
|
42faa962fc | ||
|
|
0f94f6c8e3 | ||
|
|
c917bd893d | ||
|
|
d57bc253c5 | ||
|
|
0dce120a20 | ||
|
|
ab61b0a435 | ||
|
|
cc919a5f8d | ||
|
|
8e107b2280 | ||
|
|
f1e0730701 | ||
|
|
2e82fc4790 | ||
|
|
bfda326c24 | ||
|
|
f2b27d17b7 | ||
|
|
5001983d34 | ||
|
|
73e27aee4f | ||
|
|
0d27195fd4 | ||
|
|
1f5783a250 | ||
|
|
37952e8686 | ||
|
|
ab9b9e8584 | ||
|
|
3fb1191cad | ||
|
|
f3a1c914a4 | ||
|
|
b2e36e5d98 | ||
|
|
ff0ffedd17 | ||
|
|
1e87d26707 | ||
|
|
f8de9ae080 | ||
|
|
6a87c5e9f5 | ||
|
|
db7a9cf071 | ||
|
|
765de72f9e | ||
|
|
6202a0f3bc | ||
|
|
413ff19ea8 | ||
|
|
4e7fcc2704 | ||
|
|
871637d3f2 | ||
|
|
0fbcc156c5 | ||
|
|
56d3f4e64c | ||
|
|
cb6828ec34 | ||
|
|
3656006a30 | ||
|
|
7a9b06ad86 | ||
|
|
acd96047de | ||
|
|
516ef1d361 | ||
|
|
b9757c9b69 | ||
|
|
a9b04e8536 | ||
|
|
841adcfff7 | ||
|
|
8ab12c86bf | ||
|
|
abc76ee963 | ||
|
|
b826844b34 | ||
|
|
017371b744 | ||
|
|
4aa2250221 | ||
|
|
49d9ed22bf | ||
|
|
51311948be | ||
|
|
61bbd455cf | ||
|
|
d93645a750 | ||
|
|
8c67adc164 | ||
|
|
04e7eb20fd | ||
|
|
868760f5b1 | ||
|
|
b0fa36e221 | ||
|
|
6356e3d35c | ||
|
|
6e9b5b309c | ||
|
|
d98abfc5db | ||
|
|
c3c64adc32 | ||
|
|
efc2d9b364 | ||
|
|
d96bb6ca82 | ||
|
|
8087200ffe | ||
|
|
61c0c8d2ef | ||
|
|
ce7657e537 | ||
|
|
037377b927 | ||
|
|
5493bdfb48 | ||
|
|
4b9cb36247 | ||
|
|
f3e89e25c5 | ||
|
|
655861cf12 | ||
|
|
050bc33241 | ||
|
|
46ee920b93 | ||
|
|
3a9e9a4bef | ||
|
|
3beb465247 | ||
|
|
2ec69ae361 | ||
|
|
52b07f467e | ||
|
|
81a1a03ed0 | ||
|
|
869440a7ed | ||
|
|
20f26b534d | ||
|
|
91cba8a17f | ||
|
|
be429caba3 | ||
|
|
8176ba22aa | ||
|
|
4b9add2fc3 | ||
|
|
55f60601ca | ||
|
|
876f351127 | ||
|
|
089516a6f2 | ||
|
|
f511e201f9 | ||
|
|
52617455ce | ||
|
|
d679f711eb | ||
|
|
68dbbc8d41 | ||
|
|
59ebdce605 | ||
|
|
310747a636 | ||
|
|
a3b4b39ff1 | ||
|
|
fb91aad583 | ||
|
|
825d4baf90 | ||
|
|
8faed8d9fe | ||
|
|
272e815522 | ||
|
|
06d65ef5ac | ||
|
|
d886c1761b | ||
|
|
47aa28e530 | ||
|
|
fbd5b49215 | ||
|
|
dad2196776 | ||
|
|
b50d740542 | ||
|
|
3e7efe5ef1 | ||
|
|
75a10bfcaf | ||
|
|
730e5951bc | ||
|
|
941fb5a367 | ||
|
|
17d471c07c | ||
|
|
ef1d8bc3bd | ||
|
|
581c954f40 | ||
|
|
98068beca0 | ||
|
|
3c1bf4ccfe | ||
|
|
1836ec431c | ||
|
|
14c105b86e | ||
|
|
2f6d240900 | ||
|
|
edc3d34beb | ||
|
|
53f1bf2af2 | ||
|
|
1fc6d214f4 | ||
|
|
bf5c4daf86 | ||
|
|
ec56167032 | ||
|
|
15339aec64 | ||
|
|
a90110d9ba | ||
|
|
e24ea3652c | ||
|
|
508c28e689 | ||
|
|
fec3e685c9 | ||
|
|
b16cb6575d | ||
|
|
329fbd01d8 | ||
|
|
01b0e98ab2 | ||
|
|
638c085ffa | ||
|
|
8a2de5f23e | ||
|
|
2c90c97ccd | ||
|
|
2db2ef82f2 | ||
|
|
eb0581c264 | ||
|
|
61fb5fe29c | ||
|
|
9fd415d3f5 | ||
|
|
b7366ae3fc | ||
|
|
6cdcf75e6b | ||
|
|
d57fe4395e | ||
|
|
0114d72a6c | ||
|
|
8c1037ef1b | ||
|
|
ed796fcfaa | ||
|
|
6ab253366c | ||
|
|
4aa550837f | ||
|
|
c72d73ec45 | ||
|
|
5fe333934d | ||
|
|
9a06d2b7e8 | ||
|
|
cf3b0475da | ||
|
|
aae29ba48b | ||
|
|
485c8f2ef0 | ||
|
|
7083879700 | ||
|
|
9ca71d8608 | ||
|
|
8a3d2afd79 | ||
|
|
80d185c94c | ||
|
|
cb13ee76ff | ||
|
|
a2a91654a9 | ||
|
|
383b83d788 | ||
|
|
2b9e041a86 | ||
|
|
90c1b82baa | ||
|
|
896343d943 | ||
|
|
1499bfa489 | ||
|
|
b7233d1197 | ||
|
|
16b4b6b302 | ||
|
|
7f89668d6c | ||
|
|
aecb29d2b0 | ||
|
|
9ce631e8d1 | ||
|
|
2165c90011 | ||
|
|
a6df541104 | ||
|
|
62325d4a35 | ||
|
|
03068b48fe | ||
|
|
d45b9c92d8 | ||
|
|
49205f92ff | ||
|
|
5493c60373 | ||
|
|
847331260c | ||
|
|
8291788f40 | ||
|
|
a18c74be05 | ||
|
|
42d8b9b847 | ||
|
|
2cfef4d94d | ||
|
|
f2e69a3703 | ||
|
|
d6494ffed5 | ||
|
|
9bd0d47576 | ||
|
|
a46dba08e2 | ||
|
|
f6718291b7 | ||
|
|
8a203cf2cb | ||
|
|
58541a799e | ||
|
|
848124ac4d | ||
|
|
3cdb1793d4 | ||
|
|
64ba37c02e | ||
|
|
06bb49f135 | ||
|
|
61f81bdb26 | ||
|
|
c28ed06e98 | ||
|
|
405935f918 | ||
|
|
906431bb00 | ||
|
|
4ef21a1e9b | ||
|
|
c5c79c936f | ||
|
|
f00919070e | ||
|
|
2ad0b1afc2 | ||
|
|
c75eb53c0c | ||
|
|
9f328cab95 | ||
|
|
c88ee84bed | ||
|
|
38bf6f2693 | ||
|
|
4f3a7fd227 | ||
|
|
f119e96e8f | ||
|
|
6d1256ddcc | ||
|
|
2af8d3f1d0 | ||
|
|
6631275ab6 | ||
|
|
9d36f31615 | ||
|
|
0f7887fffe | ||
|
|
a34034494e | ||
|
|
eeb2678ec2 | ||
|
|
a91d8a66f3 | ||
|
|
9d4ab862b9 | ||
|
|
85ebafd3f6 | ||
|
|
9adb313ee8 | ||
|
|
faa89be816 | ||
|
|
89d94dd33b | ||
|
|
a796ca5e72 | ||
|
|
94da4d10d7 | ||
|
|
31fe50ffa3 | ||
|
|
b381f509d1 | ||
|
|
ea546c789b | ||
|
|
f0a7797712 | ||
|
|
08102a0bf9 | ||
|
|
1c3b30b815 | ||
|
|
1563a68144 | ||
|
|
48590a35e4 | ||
|
|
335a0e20c2 | ||
|
|
8a1c3cd668 | ||
|
|
2c246c7d33 | ||
|
|
a4aa2a9002 | ||
|
|
08a85ba869 | ||
|
|
39f08aeda1 | ||
|
|
fe89c19ac0 | ||
|
|
0c806d84f7 | ||
|
|
55339998e5 | ||
|
|
0855994e59 | ||
|
|
8e1cdb199a | ||
|
|
34e9ea55df | ||
|
|
52af688245 | ||
|
|
8a530cbcce | ||
|
|
b89d552387 | ||
|
|
96cceed23e | ||
|
|
8f361a15b2 | ||
|
|
f4143af4fa | ||
|
|
1c0adb9af8 | ||
|
|
8a7a39530e | ||
|
|
3358295de8 | ||
|
|
ef75346861 | ||
|
|
064eee6859 | ||
|
|
8b7cc43952 | ||
|
|
9426485bb6 | ||
|
|
b4aca122a1 | ||
|
|
d9236f1c20 | ||
|
|
8a5801a204 | ||
|
|
71433b8224 | ||
|
|
87ac69363a | ||
|
|
5731d964b6 | ||
|
|
f323a4fcc7 | ||
|
|
69e0d866c0 | ||
|
|
0532a41c75 | ||
|
|
900bf2be55 | ||
|
|
cc4ad6670f | ||
|
|
28995a8bce | ||
|
|
7940a6a728 | ||
|
|
6ade82ed7e | ||
|
|
3a57f71f33 | ||
|
|
293858c51f | ||
|
|
14a83103c3 | ||
|
|
08a1c9f127 | ||
|
|
40d6a06f8f | ||
|
|
f71d2c7cfc | ||
|
|
81de9a8615 | ||
|
|
f9d7a71195 | ||
|
|
ff3eed2ad8 | ||
|
|
9e45ec222e | ||
|
|
3e55008323 | ||
|
|
d81eb557d7 | ||
|
|
d5e5a6da2d | ||
|
|
c0edfbc4ea | ||
|
|
3f2ef88eb9 | ||
|
|
503e7e368b | ||
|
|
ca815f52c8 | ||
|
|
025ae2349d |
4
.github/workflows/build-test.yaml
vendored
4
.github/workflows/build-test.yaml
vendored
@@ -4,7 +4,7 @@ on: [push, pull_request]
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
run: ./scripts/ci-build.sh 2>&1
|
run: ./scripts/ci-build.sh 2>&1
|
||||||
|
|
||||||
- name: Upload micro-controller data dictionaries
|
- name: Upload micro-controller data dictionaries
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: data-dict
|
name: data-dict
|
||||||
path: ci_build/dict
|
path: ci_build/dict
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -4,15 +4,14 @@ Welcome to the Klipper project!
|
|||||||
|
|
||||||
https://www.klipper3d.org/
|
https://www.klipper3d.org/
|
||||||
|
|
||||||
Klipper is a 3d-Printer firmware. It combines the power of a general
|
The Klipper firmware controls 3d-Printers. It combines the power of a
|
||||||
purpose computer with one or more micro-controllers. See the
|
general purpose computer with one or more micro-controllers. See the
|
||||||
[features document](https://www.klipper3d.org/Features.html) for more
|
[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
|
Start by [installing Klipper software](https://www.klipper3d.org/Installation.html).
|
||||||
[installing](https://www.klipper3d.org/Installation.html) it.
|
|
||||||
|
|
||||||
Klipper is Free Software. See the [license](COPYING) or read the
|
Klipper software is Free Software. See the [license](COPYING) or read
|
||||||
[documentation](https://www.klipper3d.org/Overview.html). We depend on
|
the [documentation](https://www.klipper3d.org/Overview.html). We
|
||||||
the generous support from our
|
depend on the generous support from our
|
||||||
[sponsors](https://www.klipper3d.org/Sponsors.html).
|
[sponsors](https://www.klipper3d.org/Sponsors.html).
|
||||||
|
|||||||
138
config/example-generic-caretesian.cfg
Normal file
138
config/example-generic-caretesian.cfg
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# This file is an example config file for cartesian style printers.
|
||||||
|
# One may copy and edit this file to configure a new printer with
|
||||||
|
# a generic cartesian kinematics.
|
||||||
|
|
||||||
|
# DO NOT COPY THIS FILE WITHOUT CAREFULLY READING AND UPDATING IT
|
||||||
|
# FIRST. Incorrectly configured parameters may cause damage.
|
||||||
|
|
||||||
|
# See docs/Config_Reference.md for a description of parameters.
|
||||||
|
|
||||||
|
[carriage x]
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 300
|
||||||
|
homing_speed: 50
|
||||||
|
endstop_pin: ^PE5
|
||||||
|
|
||||||
|
[carriage y]
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 200
|
||||||
|
homing_speed: 50
|
||||||
|
endstop_pin: ^PJ1
|
||||||
|
|
||||||
|
[extra_carriage y1]
|
||||||
|
primary_carriage: y
|
||||||
|
endstop_pin: ^PB6
|
||||||
|
|
||||||
|
[carriage z]
|
||||||
|
position_endstop: 0.5
|
||||||
|
position_max: 100
|
||||||
|
endstop_pin: ^PD3
|
||||||
|
|
||||||
|
[dual_carriage u]
|
||||||
|
primary_carriage: x
|
||||||
|
position_endstop: 300
|
||||||
|
position_max: 300
|
||||||
|
homing_speed: 50
|
||||||
|
endstop_pin: ^PE4
|
||||||
|
|
||||||
|
[stepper my_stepper_x]
|
||||||
|
carriages: x+y
|
||||||
|
step_pin: PF0
|
||||||
|
dir_pin: PF1
|
||||||
|
enable_pin: !PD7
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
|
||||||
|
[stepper my_stepper_u]
|
||||||
|
carriages: u-y1
|
||||||
|
step_pin: PH1
|
||||||
|
dir_pin: PH0
|
||||||
|
enable_pin: !PA1
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
|
||||||
|
[stepper my_stepper_y0]
|
||||||
|
carriages: y
|
||||||
|
step_pin: PF6
|
||||||
|
dir_pin: !PF7
|
||||||
|
enable_pin: !PF2
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
|
||||||
|
[stepper my_stepper_y1]
|
||||||
|
carriages: y1
|
||||||
|
step_pin: PE3
|
||||||
|
dir_pin: !PH6
|
||||||
|
enable_pin: !PG5
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
|
||||||
|
[stepper my_stepper_z0]
|
||||||
|
carriages: z
|
||||||
|
step_pin: PL3
|
||||||
|
dir_pin: PL1
|
||||||
|
enable_pin: !PK0
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 8
|
||||||
|
|
||||||
|
[stepper my_stepper_z1]
|
||||||
|
carriages: z
|
||||||
|
step_pin: PG1
|
||||||
|
dir_pin: PG0
|
||||||
|
enable_pin: !PH3
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 8
|
||||||
|
|
||||||
|
[extruder]
|
||||||
|
step_pin: PA4
|
||||||
|
dir_pin: PA6
|
||||||
|
enable_pin: !PA2
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 33.5
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: PB4
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PK5
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 22.2
|
||||||
|
pid_Ki: 1.08
|
||||||
|
pid_Kd: 114
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 250
|
||||||
|
|
||||||
|
[extruder1]
|
||||||
|
step_pin: PC1
|
||||||
|
dir_pin: PC3
|
||||||
|
enable_pin: !PC7
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 33.5
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: PB5
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PK7
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 22.2
|
||||||
|
pid_Ki: 1.08
|
||||||
|
pid_Kd: 114
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 250
|
||||||
|
|
||||||
|
[heater_bed]
|
||||||
|
heater_pin: PH5
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PK6
|
||||||
|
control: watermark
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 110
|
||||||
|
|
||||||
|
[mcu]
|
||||||
|
serial: /dev/ttyACM0
|
||||||
|
|
||||||
|
[printer]
|
||||||
|
kinematics: generic_cartesian
|
||||||
|
max_velocity: 500
|
||||||
|
max_accel: 3000
|
||||||
|
max_z_velocity: 20
|
||||||
|
max_z_accel: 100
|
||||||
@@ -39,7 +39,7 @@ position_max: 270
|
|||||||
# Motor4
|
# Motor4
|
||||||
# The M8P only has 4 heater outputs which leaves an extra stepper
|
# The M8P only has 4 heater outputs which leaves an extra stepper
|
||||||
# This can be used for a second Z stepper, dual_carriage, extruder co-stepper,
|
# This can be used for a second Z stepper, dual_carriage, extruder co-stepper,
|
||||||
# or other accesory such as an MMU
|
# or other accessory such as an MMU
|
||||||
#[stepper_]
|
#[stepper_]
|
||||||
#step_pin: PD3
|
#step_pin: PD3
|
||||||
#dir_pin: PD2
|
#dir_pin: PD2
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ position_max: 270
|
|||||||
# Motor4
|
# Motor4
|
||||||
# The M8P only has 4 heater outputs which leaves an extra stepper
|
# The M8P only has 4 heater outputs which leaves an extra stepper
|
||||||
# This can be used for a second Z stepper, dual_carriage, extruder co-stepper,
|
# This can be used for a second Z stepper, dual_carriage, extruder co-stepper,
|
||||||
# or other accesory such as an MMU
|
# or other accessory such as an MMU
|
||||||
#[stepper_]
|
#[stepper_]
|
||||||
#step_pin: PD3
|
#step_pin: PD3
|
||||||
#dir_pin: PD2
|
#dir_pin: PD2
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ position_max: 200
|
|||||||
# Motor-4
|
# Motor-4
|
||||||
# The Octopus only has 4 heater outputs which leaves an extra stepper
|
# The Octopus only has 4 heater outputs which leaves an extra stepper
|
||||||
# This can be used for a second Z stepper, dual_carriage, extruder co-stepper,
|
# This can be used for a second Z stepper, dual_carriage, extruder co-stepper,
|
||||||
# or other accesory such as an MMU
|
# or other accessory such as an MMU
|
||||||
#[stepper_]
|
#[stepper_]
|
||||||
#step_pin: PB8
|
#step_pin: PB8
|
||||||
#dir_pin: PB9
|
#dir_pin: PB9
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ position_max: 200
|
|||||||
# Driver3
|
# Driver3
|
||||||
# The Octopus only has 4 heater outputs which leaves an extra stepper
|
# The Octopus only has 4 heater outputs which leaves an extra stepper
|
||||||
# This can be used for a second Z stepper, dual_carriage, extruder co-stepper,
|
# This can be used for a second Z stepper, dual_carriage, extruder co-stepper,
|
||||||
# or other accesory such as an MMU
|
# or other accessory such as an MMU
|
||||||
#[stepper_]
|
#[stepper_]
|
||||||
#step_pin: PG4
|
#step_pin: PG4
|
||||||
#dir_pin: PC1
|
#dir_pin: PC1
|
||||||
|
|||||||
@@ -153,3 +153,48 @@ aliases:
|
|||||||
#uart_pin: PD12
|
#uart_pin: PD12
|
||||||
#run_current: 0.600
|
#run_current: 0.600
|
||||||
#diag_pin:
|
#diag_pin:
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# TMC2130 configuration
|
||||||
|
########################################
|
||||||
|
|
||||||
|
#[tmc2130 stepper_x]
|
||||||
|
#cs_pin: PE0
|
||||||
|
#spi_software_miso_pin: PA14
|
||||||
|
#spi_software_mosi_pin: PE14
|
||||||
|
#spi_software_sclk_pin: PE15
|
||||||
|
#run_current: 0.800
|
||||||
|
#diag1_pin: PC1
|
||||||
|
|
||||||
|
#[tmc2130 stepper_y]
|
||||||
|
#cs_pin: PD3
|
||||||
|
#spi_software_miso_pin: PA14
|
||||||
|
#spi_software_mosi_pin: PE14
|
||||||
|
#spi_software_sclk_pin: PE15
|
||||||
|
#run_current: 0.800
|
||||||
|
#diag1_pin: PC3
|
||||||
|
|
||||||
|
#[tmc2130 stepper_z]
|
||||||
|
#cs_pin: PD0
|
||||||
|
#spi_software_miso_pin: PA14
|
||||||
|
#spi_software_mosi_pin: PE14
|
||||||
|
#spi_software_sclk_pin: PE15
|
||||||
|
#run_current: 0.800
|
||||||
|
#diag1_pin: PC0
|
||||||
|
|
||||||
|
#[tmc2130 extruder]
|
||||||
|
#cs_pin: PC6
|
||||||
|
#spi_software_miso_pin: PA14
|
||||||
|
#spi_software_mosi_pin: PE14
|
||||||
|
#spi_software_sclk_pin: PE15
|
||||||
|
#run_current: 0.600
|
||||||
|
#diag1_pin: PC2
|
||||||
|
|
||||||
|
#[tmc2130 extruder1]
|
||||||
|
#cs_pin: PD12
|
||||||
|
#spi_software_miso_pin: PA14
|
||||||
|
#spi_software_mosi_pin: PE14
|
||||||
|
#spi_software_sclk_pin: PE15
|
||||||
|
#run_current: 0.600
|
||||||
|
#stealthchop_threshold: 999999
|
||||||
|
#diag1_pin: PA0
|
||||||
|
|||||||
@@ -122,6 +122,12 @@ max_z_accel: 100
|
|||||||
[static_digital_output usb_pullup_enable]
|
[static_digital_output usb_pullup_enable]
|
||||||
pins: !PA14
|
pins: !PA14
|
||||||
|
|
||||||
|
#[neopixel my_neopixel]
|
||||||
|
#pin: PA8
|
||||||
|
|
||||||
|
[output_pin red_led]
|
||||||
|
pin: PA13
|
||||||
|
|
||||||
[board_pins]
|
[board_pins]
|
||||||
aliases:
|
aliases:
|
||||||
# EXP1 header
|
# EXP1 header
|
||||||
|
|||||||
@@ -95,4 +95,4 @@ max_z_accel: 100
|
|||||||
aliases:
|
aliases:
|
||||||
EXP1_1=PC6,EXP1_3=PB10,EXP1_5=PB14,EXP1_7=PB12,EXP1_9=<GND>,
|
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>,
|
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
|
||||||
|
|||||||
232
config/generic-mellow-fly-e3-v2.cfg
Normal file
232
config/generic-mellow-fly-e3-v2.cfg
Normal 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
|
||||||
@@ -89,32 +89,32 @@ max_z_velocity: 5
|
|||||||
max_z_accel: 100
|
max_z_accel: 100
|
||||||
|
|
||||||
[mcp4018 x_axis_pot]
|
[mcp4018 x_axis_pot]
|
||||||
scl_pin: PJ5
|
i2c_software_scl_pin: PJ5
|
||||||
sda_pin: PF3
|
i2c_software_sda_pin: PF3
|
||||||
wiper: 0.50
|
wiper: 0.50
|
||||||
scale: 0.773
|
scale: 0.773
|
||||||
|
|
||||||
[mcp4018 y_axis_pot]
|
[mcp4018 y_axis_pot]
|
||||||
scl_pin: PJ5
|
i2c_software_scl_pin: PJ5
|
||||||
sda_pin: PF7
|
i2c_software_sda_pin: PF7
|
||||||
wiper: 0.50
|
wiper: 0.50
|
||||||
scale: 0.773
|
scale: 0.773
|
||||||
|
|
||||||
[mcp4018 z_axis_pot]
|
[mcp4018 z_axis_pot]
|
||||||
scl_pin: PJ5
|
i2c_software_scl_pin: PJ5
|
||||||
sda_pin: PK3
|
i2c_software_sda_pin: PK3
|
||||||
wiper: 0.50
|
wiper: 0.50
|
||||||
scale: 0.773
|
scale: 0.773
|
||||||
|
|
||||||
[mcp4018 a_axis_pot]
|
[mcp4018 a_axis_pot]
|
||||||
scl_pin: PJ5
|
i2c_software_scl_pin: PJ5
|
||||||
sda_pin: PA5
|
i2c_software_sda_pin: PA5
|
||||||
wiper: 0.50
|
wiper: 0.50
|
||||||
scale: 0.773
|
scale: 0.773
|
||||||
|
|
||||||
[mcp4018 b_axis_pot]
|
[mcp4018 b_axis_pot]
|
||||||
scl_pin: PJ5
|
i2c_software_scl_pin: PJ5
|
||||||
sda_pin: PJ6
|
i2c_software_sda_pin: PJ6
|
||||||
wiper: 0.50
|
wiper: 0.50
|
||||||
scale: 0.773
|
scale: 0.773
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
# FSR switch (z endstop) location [homing_override] section
|
# FSR switch (z endstop) location [homing_override] section
|
||||||
# FSR switch (z endstop) offset for Z0 [stepper_z] section
|
# FSR switch (z endstop) offset for Z0 [stepper_z] section
|
||||||
# Probe points [quad_gantry_level] section
|
# Probe points [quad_gantry_level] section
|
||||||
# Min & Max gantry corner postions [quad_gantry_level] section
|
# Min & Max gantry corner positions [quad_gantry_level] section
|
||||||
# PID tune [extruder] and [heater_bed] sections
|
# PID tune [extruder] and [heater_bed] sections
|
||||||
# Fine tune E steps [extruder] section
|
# Fine tune E steps [extruder] section
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
# FSR switch (z endstop) location [homing_override] section
|
# FSR switch (z endstop) location [homing_override] section
|
||||||
# FSR switch (z endstop) offset for Z0 [stepper_z] section
|
# FSR switch (z endstop) offset for Z0 [stepper_z] section
|
||||||
# Probe points [quad_gantry_level] section
|
# Probe points [quad_gantry_level] section
|
||||||
# Min & Max gantry corner postions [quad_gantry_level] section
|
# Min & Max gantry corner positions [quad_gantry_level] section
|
||||||
# PID tune [extruder] and [heater_bed] sections
|
# PID tune [extruder] and [heater_bed] sections
|
||||||
# Fine tune E steps [extruder] section
|
# Fine tune E steps [extruder] section
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ endstop_pin: ^PE4
|
|||||||
homing_speed: 60
|
homing_speed: 60
|
||||||
# The next parameter needs to be adjusted for
|
# The next parameter needs to be adjusted for
|
||||||
# your printer. You may want to start with 280
|
# your printer. You may want to start with 280
|
||||||
# and meassure the distance from nozzle to bed.
|
# and measure the distance from nozzle to bed.
|
||||||
# This value then needs to be added.
|
# This value then needs to be added.
|
||||||
position_endstop: 273.0
|
position_endstop: 273.0
|
||||||
arm_length: 229.4
|
arm_length: 229.4
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ position_max: 400
|
|||||||
#Uncomment if you have a BL-Touch:
|
#Uncomment if you have a BL-Touch:
|
||||||
#position_min: -4
|
#position_min: -4
|
||||||
#endstop_pin: probe:z_virtual_endstop
|
#endstop_pin: probe:z_virtual_endstop
|
||||||
#and comment the follwing lines:
|
#and comment the following lines:
|
||||||
position_endstop: 0.0
|
position_endstop: 0.0
|
||||||
endstop_pin: ^PD3 #ar18
|
endstop_pin: ^PD3 #ar18
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# This file contains pin mappings for the stock 2020 Creality CR6-SE.
|
# This file contains pin mappings for the stock 2020 Creality CR6-SE
|
||||||
|
# with the early 4.5.2 board only.
|
||||||
# To use this config, during "make menuconfig" select the STM32F103
|
# To use this config, during "make menuconfig" select the STM32F103
|
||||||
# with a "28KiB bootloader" and serial (on USART1 PA10/PA9)
|
# with a "28KiB bootloader" and serial (on USART1 PA10/PA9)
|
||||||
# communication.
|
# communication.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# This file contains pin mappings for the Creality CR6-SE with Rev. 4.5.3 Motherboard (Late 2020/2021) as the heater pins changed.
|
# This file contains pin mappings for the Creality CR6-SE
|
||||||
|
# with Rev. 4.5.3 Motherboard (Late 2020/2021) as the heater pins changed.
|
||||||
|
# This config also works for the CR-ERA_V1.1.0.3
|
||||||
# To use this config, during "make menuconfig" select the STM32F103
|
# To use this config, during "make menuconfig" select the STM32F103
|
||||||
# with a "28KiB bootloader" and serial (on USART1 PA10/PA9)
|
# with a "28KiB bootloader" and serial (on USART1 PA10/PA9)
|
||||||
# communication.
|
# communication.
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ pin: PA0
|
|||||||
kick_start_time: 0.5
|
kick_start_time: 0.5
|
||||||
|
|
||||||
# Hotend fan
|
# Hotend fan
|
||||||
# set fan runnig when extruder temperature is over 60
|
# set fan running when extruder temperature is over 60
|
||||||
[heater_fan heatbreak_fan]
|
[heater_fan heatbreak_fan]
|
||||||
pin: PC0
|
pin: PC0
|
||||||
heater:extruder
|
heater:extruder
|
||||||
|
|||||||
@@ -127,32 +127,32 @@ max_z_velocity: 5
|
|||||||
max_z_accel: 100
|
max_z_accel: 100
|
||||||
|
|
||||||
[mcp4018 x_axis_pot]
|
[mcp4018 x_axis_pot]
|
||||||
scl_pin: PJ5
|
i2c_software_scl_pin: PJ5
|
||||||
sda_pin: PF3
|
i2c_software_sda_pin: PF3
|
||||||
wiper: 118
|
wiper: 118
|
||||||
scale: 127
|
scale: 127
|
||||||
|
|
||||||
[mcp4018 y_axis_pot]
|
[mcp4018 y_axis_pot]
|
||||||
scl_pin: PJ5
|
i2c_software_scl_pin: PJ5
|
||||||
sda_pin: PF7
|
i2c_software_sda_pin: PF7
|
||||||
wiper: 118
|
wiper: 118
|
||||||
scale: 127
|
scale: 127
|
||||||
|
|
||||||
[mcp4018 z_axis_pot]
|
[mcp4018 z_axis_pot]
|
||||||
scl_pin: PJ5
|
i2c_software_scl_pin: PJ5
|
||||||
sda_pin: PK3
|
i2c_software_sda_pin: PK3
|
||||||
wiper: 40
|
wiper: 40
|
||||||
scale: 127
|
scale: 127
|
||||||
|
|
||||||
[mcp4018 a_axis_pot]
|
[mcp4018 a_axis_pot]
|
||||||
scl_pin: PJ5
|
i2c_software_scl_pin: PJ5
|
||||||
sda_pin: PA5
|
i2c_software_sda_pin: PA5
|
||||||
wiper: 118
|
wiper: 118
|
||||||
scale: 127
|
scale: 127
|
||||||
|
|
||||||
[mcp4018 b_axis_pot]
|
[mcp4018 b_axis_pot]
|
||||||
scl_pin: PJ5
|
i2c_software_scl_pin: PJ5
|
||||||
sda_pin: PJ6
|
i2c_software_sda_pin: PJ6
|
||||||
wiper: 118
|
wiper: 118
|
||||||
scale: 127
|
scale: 127
|
||||||
|
|
||||||
|
|||||||
256
config/printer-geeetech-A10T-A20T-2021.cfg
Normal file
256
config/printer-geeetech-A10T-A20T-2021.cfg
Normal 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
|
||||||
@@ -195,7 +195,7 @@ samples_tolerance: 0.200
|
|||||||
samples_tolerance_retries: 2
|
samples_tolerance_retries: 2
|
||||||
|
|
||||||
[bed_tilt]
|
[bed_tilt]
|
||||||
# Enable bed tilt measurments using the probe we defined above
|
# Enable bed tilt measurements using the probe we defined above
|
||||||
# Probe points using X0 Y0 offsets @ 0.01mm/step
|
# Probe points using X0 Y0 offsets @ 0.01mm/step
|
||||||
points: -2, -6
|
points: -2, -6
|
||||||
156, -6
|
156, -6
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ samples: 2
|
|||||||
samples_tolerance: 0.100
|
samples_tolerance: 0.100
|
||||||
|
|
||||||
[bed_tilt]
|
[bed_tilt]
|
||||||
#Enable bed tilt measurments using the probe we defined above
|
#Enable bed tilt measurements using the probe we defined above
|
||||||
#Probe points using X0 Y0 offsets @ 0.01mm/step
|
#Probe points using X0 Y0 offsets @ 0.01mm/step
|
||||||
points: -3, -6
|
points: -3, -6
|
||||||
282, -6
|
282, -6
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ microsteps: 16
|
|||||||
rotation_distance: 4
|
rotation_distance: 4
|
||||||
# Required if not using probe for the virtual endstop
|
# Required if not using probe for the virtual endstop
|
||||||
# endstop_pin: ^PD3
|
# endstop_pin: ^PD3
|
||||||
# position_endstop: 250 # Will need ajustment
|
# position_endstop: 250 # Will need adjustment
|
||||||
endstop_pin: probe:z_virtual_endstop
|
endstop_pin: probe:z_virtual_endstop
|
||||||
homing_speed: 10.0
|
homing_speed: 10.0
|
||||||
position_max: 250
|
position_max: 250
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# This file constains the pin mappings for the SeeMeCNC Rostock Max
|
# This file contains the pin mappings for the SeeMeCNC Rostock Max
|
||||||
# (version 2) delta printer from 2015. To use this config, the
|
# (version 2) delta printer from 2015. To use this config, the
|
||||||
# firmware should be compiled for the AVR atmega2560.
|
# firmware should be compiled for the AVR atmega2560.
|
||||||
|
|
||||||
|
|||||||
177
config/sample-corexyuv.cfg
Normal file
177
config/sample-corexyuv.cfg
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
# This file contains a configuration snippet for a CoreXYUV
|
||||||
|
# printer with an independent dual extruder moving over X and Y axes.
|
||||||
|
|
||||||
|
# See docs/Config_Reference.md for a description of parameters.
|
||||||
|
|
||||||
|
[carriage x]
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 300
|
||||||
|
homing_speed: 50
|
||||||
|
endstop_pin: ^PE5
|
||||||
|
|
||||||
|
[carriage y]
|
||||||
|
position_endstop: 0
|
||||||
|
position_max: 200
|
||||||
|
homing_speed: 50
|
||||||
|
endstop_pin: ^PJ1
|
||||||
|
|
||||||
|
[dual_carriage u]
|
||||||
|
primary_carriage: x
|
||||||
|
safe_distance: 70
|
||||||
|
position_endstop: 300
|
||||||
|
position_max: 300
|
||||||
|
homing_speed: 50
|
||||||
|
endstop_pin: ^PE4
|
||||||
|
|
||||||
|
[dual_carriage v]
|
||||||
|
primary_carriage: y
|
||||||
|
safe_distance: 50
|
||||||
|
position_endstop: 200
|
||||||
|
position_max: 200
|
||||||
|
homing_speed: 50
|
||||||
|
endstop_pin: ^PD4
|
||||||
|
|
||||||
|
[stepper a]
|
||||||
|
carriages: x+y
|
||||||
|
step_pin: PF0
|
||||||
|
dir_pin: PF1
|
||||||
|
enable_pin: !PD7
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
|
||||||
|
[stepper b]
|
||||||
|
carriages: u-v
|
||||||
|
step_pin: PH1
|
||||||
|
dir_pin: PH0
|
||||||
|
enable_pin: !PA1
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
|
||||||
|
[stepper c]
|
||||||
|
carriages: x-y
|
||||||
|
step_pin: PF6
|
||||||
|
dir_pin: !PF7
|
||||||
|
enable_pin: !PF2
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
|
||||||
|
[stepper d]
|
||||||
|
carriages: u+v
|
||||||
|
step_pin: PE3
|
||||||
|
dir_pin: !PH6
|
||||||
|
enable_pin: !PG5
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 40
|
||||||
|
|
||||||
|
[extruder]
|
||||||
|
step_pin: PA4
|
||||||
|
dir_pin: PA6
|
||||||
|
enable_pin: !PA2
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 33.5
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: PB4
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PK5
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 22.2
|
||||||
|
pid_Ki: 1.08
|
||||||
|
pid_Kd: 114
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 250
|
||||||
|
|
||||||
|
[gcode_macro PARK_extruder]
|
||||||
|
gcode:
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=x
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=y
|
||||||
|
G90
|
||||||
|
G1 X0 Y0
|
||||||
|
|
||||||
|
[gcode_macro T0]
|
||||||
|
gcode:
|
||||||
|
PARK_{printer.toolhead.extruder}
|
||||||
|
ACTIVATE_EXTRUDER EXTRUDER=extruder
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=x
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=y
|
||||||
|
|
||||||
|
[extruder1]
|
||||||
|
step_pin: PC1
|
||||||
|
dir_pin: PC3
|
||||||
|
enable_pin: !PC7
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 33.5
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: PB5
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PK7
|
||||||
|
control: pid
|
||||||
|
pid_Kp: 22.2
|
||||||
|
pid_Ki: 1.08
|
||||||
|
pid_Kd: 114
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 250
|
||||||
|
|
||||||
|
[gcode_macro PARK_extruder1]
|
||||||
|
gcode:
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=u
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=v
|
||||||
|
G90
|
||||||
|
G1 X300 Y200
|
||||||
|
|
||||||
|
[gcode_macro T1]
|
||||||
|
gcode:
|
||||||
|
PARK_{printer.toolhead.extruder}
|
||||||
|
ACTIVATE_EXTRUDER EXTRUDER=extruder1
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=u
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=v
|
||||||
|
|
||||||
|
# A helper script to activate copy mode
|
||||||
|
[gcode_macro ACTIVATE_COPY_MODE]
|
||||||
|
gcode:
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=x MODE=PRIMARY
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=y MODE=PRIMARY
|
||||||
|
G1 X0 Y0
|
||||||
|
ACTIVATE_EXTRUDER EXTRUDER=extruder
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=u MODE=PRIMARY
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=v MODE=PRIMARY
|
||||||
|
G1 X150 Y100
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=u MODE=COPY
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=v MODE=COPY
|
||||||
|
SYNC_EXTRUDER_MOTION EXTRUDER=extruder1 MOTION_QUEUE=extruder
|
||||||
|
|
||||||
|
# A helper script to activate mirror mode
|
||||||
|
[gcode_macro ACTIVATE_MIRROR_MODE]
|
||||||
|
gcode:
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=x MODE=PRIMARY
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=y MODE=PRIMARY
|
||||||
|
G1 X0 Y0
|
||||||
|
ACTIVATE_EXTRUDER EXTRUDER=extruder
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=u MODE=PRIMARY
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=v MODE=PRIMARY
|
||||||
|
G1 X300 Y100
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=u MODE=MIRROR
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=v MODE=COPY
|
||||||
|
SYNC_EXTRUDER_MOTION EXTRUDER=extruder1 MOTION_QUEUE=extruder
|
||||||
|
|
||||||
|
[printer]
|
||||||
|
kinematics: generic_cartesian
|
||||||
|
max_velocity: 300
|
||||||
|
max_accel: 3000
|
||||||
|
max_z_velocity: 5
|
||||||
|
max_z_accel: 100
|
||||||
|
|
||||||
|
## An optional input shaper support
|
||||||
|
#[input_shaper]
|
||||||
|
## The section is intentionally empty
|
||||||
|
#
|
||||||
|
#[delayed_gcode init_shaper]
|
||||||
|
#initial_duration: 0.1
|
||||||
|
#gcode:
|
||||||
|
# SET_DUAL_CARRIAGE CARRIAGE=u
|
||||||
|
# SET_DUAL_CARRIAGE CARRIAGE=v
|
||||||
|
# SET_INPUT_SHAPER SHAPER_TYPE_X=<dual_carriage_x_shaper> SHAPER_FREQ_X=<dual_carriage_x_freq> SHAPER_TYPE_Y=<dual_carriage_y_shaper> SHAPER_FREQ_Y=<dual_carriage_y_freq>
|
||||||
|
# SET_DUAL_CARRIAGE CARRIAGE=x MODE=PRIMARY
|
||||||
|
# SET_DUAL_CARRIAGE CARRIAGE=y MODE=PRIMARY
|
||||||
|
# SET_INPUT_SHAPER SHAPER_TYPE_X=<primary_carriage_x_shaper> SHAPER_FREQ_X=<primary_carriage_x_freq> SHAPER_TYPE_Y=<primary_carriage_y_shaper> SHAPER_FREQ_Y=<primary_carriage_y_freq>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
# Communication interface of "CAN bus (on PA25/PA24)"
|
# Communication interface of "CAN bus (on PA25/PA24)"
|
||||||
|
|
||||||
# To flash the board use a debugger, or use a raspberry pi and follow
|
# To flash the board use a debugger, or use a raspberry pi and follow
|
||||||
# the instructions at docs/Bootloaders.md fot the SAMC21. You may
|
# the instructions at docs/Bootloaders.md for the SAMC21. You may
|
||||||
# supply power to the 1LC by connecting the 3.3v rail on the Pi to the
|
# supply power to the 1LC by connecting the 3.3v rail on the Pi to the
|
||||||
# 5v input of the SWD header on the 1LC.
|
# 5v input of the SWD header on the 1LC.
|
||||||
|
|
||||||
@@ -77,5 +77,14 @@ heater_temp: 50.0
|
|||||||
pin: toolboard:PA9
|
pin: toolboard:PA9
|
||||||
z_offset: 20
|
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]
|
[mcu toolboard]
|
||||||
canbus_uuid: 4b194673554e
|
canbus_uuid: 4b194673554e
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ switch_pin: !P1.28 # P1.28 for X-max
|
|||||||
# variable_pause_z : z lift when MMU2S need intervention and the printer is paused
|
# variable_pause_z : z lift when MMU2S need intervention and the printer is paused
|
||||||
# variable_min_temp_extruder : minimal required heater temperature to load/unload filament from the extruder gear to the nozzle
|
# variable_min_temp_extruder : minimal required heater temperature to load/unload filament from the extruder gear to the nozzle
|
||||||
# variable_extruder_eject_temp : heater temperature used to eject filament during home if the filament is already loaded
|
# variable_extruder_eject_temp : heater temperature used to eject filament during home if the filament is already loaded
|
||||||
# variable_enable_5in1 : pass from MMU2S standart (0) to MMU2S-5in1 mode with splitter
|
# variable_enable_5in1 : pass from MMU2S standard (0) to MMU2S-5in1 mode with splitter
|
||||||
#
|
#
|
||||||
################################
|
################################
|
||||||
[gcode_macro VAR_MMU2S]
|
[gcode_macro VAR_MMU2S]
|
||||||
@@ -394,7 +394,7 @@ gcode:
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
# Retry unload, try correct misalignement of bondtech gear
|
# Retry unload, try correct misalignment of bondtech gear
|
||||||
[gcode_macro RETRY_UNLOAD_FILAMENT_IN_EXTRUDER]
|
[gcode_macro RETRY_UNLOAD_FILAMENT_IN_EXTRUDER]
|
||||||
gcode:
|
gcode:
|
||||||
{% if printer["filament_switch_sensor ir_sensor"].filament_detected == True %}
|
{% if printer["filament_switch_sensor ir_sensor"].filament_detected == True %}
|
||||||
@@ -444,7 +444,7 @@ gcode:
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
# Ramming process for standart PLA, code extracted from slic3r gcode
|
# Ramming process for standard PLA, code extracted from slic3r gcode
|
||||||
[gcode_macro RAMMING_SLICER]
|
[gcode_macro RAMMING_SLICER]
|
||||||
gcode:
|
gcode:
|
||||||
G91
|
G91
|
||||||
|
|||||||
@@ -364,35 +364,42 @@ and might later produce asynchronous messages such as:
|
|||||||
The "header" field in the initial query response is used to describe
|
The "header" field in the initial query response is used to describe
|
||||||
the fields found in later "data" responses.
|
the fields found in later "data" responses.
|
||||||
|
|
||||||
### hx71x/dump_hx71x
|
### load_cell/dump_force
|
||||||
|
|
||||||
This endpoint is used to subscribe to raw HX711 and HX717 ADC data.
|
This endpoint is used to subscribe to force data produced by a load_cell.
|
||||||
Obtaining these low-level ADC updates may be useful for diagnostic
|
Using this endpoint may increase Klipper's system load.
|
||||||
and debugging purposes. Using this endpoint may increase Klipper's
|
|
||||||
system load.
|
|
||||||
|
|
||||||
A request may look like:
|
A request may look like:
|
||||||
`{"id": 123, "method":"hx71x/dump_hx71x",
|
`{"id": 123, "method":"load_cell/dump_force",
|
||||||
"params": {"sensor": "load_cell", "response_template": {}}}`
|
"params": {"sensor": "load_cell", "response_template": {}}}`
|
||||||
and might return:
|
and might return:
|
||||||
`{"id": 123,"result":{"header":["time","counts"]}}`
|
`{"id": 123,"result":{"header":["time", "force (g)", "counts", "tare_counts"]}}`
|
||||||
and might later produce asynchronous messages such as:
|
and might later produce asynchronous messages such as:
|
||||||
`{"params":{"data":[[3292.432935, 562534], [3292.4394937, 5625322]]}}`
|
`{"params":{"data":[[3292.432935, 40.65, 562534, -234467]]}}`
|
||||||
|
|
||||||
### ads1220/dump_ads1220
|
The "header" field in the initial query response is used to describe
|
||||||
|
the fields found in later "data" responses.
|
||||||
|
|
||||||
This endpoint is used to subscribe to raw ADS1220 ADC data.
|
### load_cell_probe/dump_taps
|
||||||
Obtaining these low-level ADC updates may be useful for diagnostic
|
|
||||||
and debugging purposes. Using this endpoint may increase Klipper's
|
This endpoint is used to subscribe to details of probing "tap" events.
|
||||||
system load.
|
Using this endpoint may increase Klipper's system load.
|
||||||
|
|
||||||
A request may look like:
|
A request may look like:
|
||||||
`{"id": 123, "method":"ads1220/dump_ads1220",
|
`{"id": 123, "method":"load_cell/dump_force",
|
||||||
"params": {"sensor": "load_cell", "response_template": {}}}`
|
"params": {"sensor": "load_cell", "response_template": {}}}`
|
||||||
and might return:
|
and might return:
|
||||||
`{"id": 123,"result":{"header":["time","counts"]}}`
|
`{"id": 123,"result":{"header":["probe_tap_event"]}}`
|
||||||
and might later produce asynchronous messages such as:
|
and might later produce asynchronous messages such as:
|
||||||
`{"params":{"data":[[3292.432935, 562534], [3292.4394937, 5625322]]}}`
|
```
|
||||||
|
{"params":{"tap":'{
|
||||||
|
"time": [118032.28039, 118032.2834, ...],
|
||||||
|
"force": [-459.4213119680034, -458.1640702543264, ...],
|
||||||
|
}}}
|
||||||
|
```
|
||||||
|
|
||||||
|
This data can be used to render:
|
||||||
|
* The time/force graph
|
||||||
|
|
||||||
### pause_resume/cancel
|
### pause_resume/cancel
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Axis Twist Compensation
|
# Axis Twist Compensation
|
||||||
|
|
||||||
This document describes the [axis_twist_compensation] module.
|
This document describes the `[axis_twist_compensation]` module.
|
||||||
|
|
||||||
Some printers may have a small twist in their X rail which can skew the results
|
Some printers may have a small twist in their X rail which can skew the results
|
||||||
of a probe attached to the X carriage.
|
of a probe attached to the X carriage.
|
||||||
@@ -24,27 +24,50 @@ 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
|
> **Tip:** Make sure the [probe X and Y offsets](Config_Reference.md#probe) are
|
||||||
> correctly set as they greatly influence calibration.
|
> correctly set as they greatly influence calibration.
|
||||||
|
|
||||||
1. After setting up the [axis_twist_compensation] module,
|
### Basic Usage: X-Axis Calibration
|
||||||
perform `AXIS_TWIST_COMPENSATION_CALIBRATE`
|
1. After setting up the `[axis_twist_compensation]` module, run:
|
||||||
* The calibration wizard will prompt you to measure the probe Z offset at a few
|
```
|
||||||
points along the bed
|
AXIS_TWIST_COMPENSATION_CALIBRATE
|
||||||
* The calibration defaults to 3 points but you can use the option
|
```
|
||||||
`SAMPLE_COUNT=` to use a different number.
|
This command will calibrate the X-axis by default.
|
||||||
2. [Adjust your Z offset](Probe_Calibrate.md#calibrating-probe-z-offset)
|
- The calibration wizard will prompt you to measure the probe Z offset at
|
||||||
3. Perform automatic/probe-based bed tramming operations, such as
|
several points along the bed.
|
||||||
[Screws Tilt Adjust](G-Codes.md#screws_tilt_adjust),
|
- By default, the calibration uses 3 points, but you can specify a different
|
||||||
[Z Tilt Adjust](G-Codes.md#z_tilt_adjust) etc
|
number with the option:
|
||||||
4. Home all axis, then perform a [Bed Mesh](Bed_Mesh.md) if required
|
``
|
||||||
5. Perform a test print, followed by any
|
SAMPLE_COUNT=<value>
|
||||||
[fine-tuning](Axis_Twist_Compensation.md#fine-tuning) as desired
|
``
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
> **Tip:** Bed temperature and nozzle temperature and size do not seem to have
|
> **Tip:** Bed temperature and nozzle temperature and size do not seem to have
|
||||||
> an influence to the calibration process.
|
> an influence to the calibration process.
|
||||||
|
|
||||||
## [axis_twist_compensation] setup and commands
|
## [axis_twist_compensation] setup and commands
|
||||||
|
|
||||||
Configuration options for [axis_twist_compensation] can be found in the
|
Configuration options for `[axis_twist_compensation]` can be found in the
|
||||||
[Configuration Reference](Config_Reference.md#axis_twist_compensation).
|
[Configuration Reference](Config_Reference.md#axis_twist_compensation).
|
||||||
|
|
||||||
Commands for [axis_twist_compensation] can be found in the
|
Commands for `[axis_twist_compensation]` can be found in the
|
||||||
[G-Codes Reference](G-Codes.md#axis_twist_compensation)
|
[G-Codes Reference](G-Codes.md#axis_twist_compensation)
|
||||||
|
|||||||
@@ -267,9 +267,9 @@ by heat or interference. This can make calculating the probe's z-offset
|
|||||||
challenging, particularly at different bed temperatures. As such, some
|
challenging, particularly at different bed temperatures. As such, some
|
||||||
printers use an endstop for homing the Z axis and a probe for calibrating the
|
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)
|
mesh. In this configuration it is possible offset the mesh so that the (X, Y)
|
||||||
`reference position` applies zero adjustment. The `reference postion` should
|
`reference position` applies zero adjustment. The `reference position` should
|
||||||
be the location on the bed where a
|
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
|
paper test is performed. The bed_mesh module provides the
|
||||||
`zero_reference_position` option for specifying this coordinate:
|
`zero_reference_position` option for specifying this coordinate:
|
||||||
|
|
||||||
@@ -292,33 +292,6 @@ probe_count: 5, 3
|
|||||||
z-offset. Note that this coordinate must NOT be in a location specified as
|
z-offset. Note that this coordinate must NOT be in a location specified as
|
||||||
a `faulty_region` if a probe is necessary.
|
a `faulty_region` if a probe is necessary.
|
||||||
|
|
||||||
#### The deprecated relative_reference_index
|
|
||||||
|
|
||||||
Existing configurations using the `relative_reference_index` option must be
|
|
||||||
updated to use the `zero_reference_position`. The response to the
|
|
||||||
[BED_MESH_OUTPUT PGP=1](#output) gcode command will include the (X, Y)
|
|
||||||
coordinate associated with the index; this position may be used as the value for
|
|
||||||
the `zero_reference_position`. The output will look similar to the following:
|
|
||||||
|
|
||||||
```
|
|
||||||
// bed_mesh: generated points
|
|
||||||
// Index | Tool Adjusted | Probe
|
|
||||||
// 0 | (1.0, 1.0) | (24.0, 6.0)
|
|
||||||
// 1 | (36.7, 1.0) | (59.7, 6.0)
|
|
||||||
// 2 | (72.3, 1.0) | (95.3, 6.0)
|
|
||||||
// 3 | (108.0, 1.0) | (131.0, 6.0)
|
|
||||||
... (additional generated points)
|
|
||||||
// bed_mesh: relative_reference_index 24 is (131.5, 108.0)
|
|
||||||
```
|
|
||||||
|
|
||||||
_Note: The above output is also printed in `klippy.log` during initialization._
|
|
||||||
|
|
||||||
Using the example above we see that the `relative_reference_index` is
|
|
||||||
printed along with its coordinate. Thus the `zero_reference_position`
|
|
||||||
is `131.5, 108`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Faulty Regions
|
### Faulty Regions
|
||||||
|
|
||||||
It is possible for some areas of a bed to report inaccurate results when
|
It is possible for some areas of a bed to report inaccurate results when
|
||||||
@@ -497,7 +470,8 @@ _Default Adaptive Margin: 0_
|
|||||||
|
|
||||||
Initiates the probing procedure for Bed Mesh Calibration.
|
Initiates the probing procedure for Bed Mesh Calibration.
|
||||||
|
|
||||||
The mesh will be saved into a profile specified by the `PROFILE` parameter,
|
The mesh will be immediately ready to use when the command completes and saved
|
||||||
|
into a profile specified by the `PROFILE` parameter,
|
||||||
or `default` if unspecified. The `METHOD` parameter takes one of the following
|
or `default` if unspecified. The `METHOD` parameter takes one of the following
|
||||||
values:
|
values:
|
||||||
|
|
||||||
@@ -561,6 +535,10 @@ load the `default` profile it is recommended to add
|
|||||||
`BED_MESH_PROFILE LOAD=default` to either their `START_PRINT` macro or their
|
`BED_MESH_PROFILE LOAD=default` to either their `START_PRINT` macro or their
|
||||||
slicer's "Start G-Code" configuration, whichever is applicable.
|
slicer's "Start G-Code" configuration, whichever is applicable.
|
||||||
|
|
||||||
|
Note that this is not required if a new mesh is generated with
|
||||||
|
`BED_MESH_CALIBRATE` in the `START_PRINT` macro or the slicer's "Start G-Code"
|
||||||
|
and may produce unexpected results, especially with adaptive meshing.
|
||||||
|
|
||||||
Alternatively the old behavior of loading a profile at startup can be
|
Alternatively the old behavior of loading a profile at startup can be
|
||||||
restored with a `[delayed_gcode]`:
|
restored with a `[delayed_gcode]`:
|
||||||
|
|
||||||
|
|||||||
@@ -250,23 +250,22 @@ results were obtained by running an STM32F407 binary on an STM32F446
|
|||||||
|
|
||||||
### STM32H7 step rate benchmark
|
### STM32H7 step rate benchmark
|
||||||
|
|
||||||
The following configuration sequence is used on a STM32H743VIT6:
|
The following configuration sequence is used on STM32H723:
|
||||||
```
|
```
|
||||||
allocate_oids count=3
|
allocate_oids count=3
|
||||||
config_stepper oid=0 step_pin=PD4 dir_pin=PD3 invert_step=-1 step_pulse_ticks=0
|
config_stepper oid=0 step_pin=PA13 dir_pin=PB5 invert_step=-1 step_pulse_ticks=52
|
||||||
config_stepper oid=1 step_pin=PA15 dir_pin=PA8 invert_step=-1 step_pulse_ticks=0
|
config_stepper oid=1 step_pin=PB2 dir_pin=PB6 invert_step=-1 step_pulse_ticks=52
|
||||||
config_stepper oid=2 step_pin=PE2 dir_pin=PE3 invert_step=-1 step_pulse_ticks=0
|
config_stepper oid=2 step_pin=PB3 dir_pin=PB7 invert_step=-1 step_pulse_ticks=52
|
||||||
finalize_config crc=0
|
finalize_config crc=0
|
||||||
```
|
```
|
||||||
|
|
||||||
The test was last run on commit `00191b5c` with gcc version
|
The test was last run on commit `554ae78d` with gcc version
|
||||||
`arm-none-eabi-gcc (15:8-2019-q3-1+b1) 8.3.1 20190703 (release)
|
`arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0`.
|
||||||
[gcc-8-branch revision 273027]`.
|
|
||||||
|
|
||||||
| stm32h7 | ticks |
|
| stm32h723 | ticks |
|
||||||
| -------------------- | ----- |
|
| -------------------- | ----- |
|
||||||
| 1 stepper | 44 |
|
| 1 stepper | 70 |
|
||||||
| 3 stepper | 198 |
|
| 3 stepper | 181 |
|
||||||
|
|
||||||
### STM32G0B1 step rate benchmark
|
### STM32G0B1 step rate benchmark
|
||||||
|
|
||||||
@@ -287,6 +286,25 @@ The test was last run on commit `247cd753` with gcc version
|
|||||||
| 1 stepper | 58 |
|
| 1 stepper | 58 |
|
||||||
| 3 stepper | 243 |
|
| 3 stepper | 243 |
|
||||||
|
|
||||||
|
### STM32G4 step rate benchmark
|
||||||
|
|
||||||
|
The following configuration sequence is used on the STM32G431:
|
||||||
|
```
|
||||||
|
allocate_oids count=3
|
||||||
|
config_stepper oid=0 step_pin=PA0 dir_pin=PB5 invert_step=-1 step_pulse_ticks=17
|
||||||
|
config_stepper oid=1 step_pin=PB2 dir_pin=PB6 invert_step=-1 step_pulse_ticks=17
|
||||||
|
config_stepper oid=2 step_pin=PB3 dir_pin=PB7 invert_step=-1 step_pulse_ticks=17
|
||||||
|
finalize_config crc=0
|
||||||
|
```
|
||||||
|
|
||||||
|
The test was last run on commit `cfa48fe3` with gcc version
|
||||||
|
`arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0`.
|
||||||
|
|
||||||
|
| stm32g431 | ticks |
|
||||||
|
| ---------------- | ----- |
|
||||||
|
| 1 stepper | 47 |
|
||||||
|
| 3 stepper | 208 |
|
||||||
|
|
||||||
### LPC176x step rate benchmark
|
### LPC176x step rate benchmark
|
||||||
|
|
||||||
The following configuration sequence is used on the LPC176x:
|
The following configuration sequence is used on the LPC176x:
|
||||||
@@ -354,6 +372,26 @@ micro-controller.
|
|||||||
| 1 stepper (200Mhz) | 39 |
|
| 1 stepper (200Mhz) | 39 |
|
||||||
| 3 stepper (200Mhz) | 181 |
|
| 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 ###
|
### AR100 step rate benchmark ###
|
||||||
|
|
||||||
The following configuration sequence is used on AR100 CPU (Allwinner A64):
|
The following configuration sequence is used on AR100 CPU (Allwinner A64):
|
||||||
@@ -366,7 +404,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
|
`or1k-linux-musl-gcc (GCC) 9.2.0` on an Allwinner A64-H
|
||||||
micro-controller.
|
micro-controller.
|
||||||
|
|
||||||
@@ -375,9 +413,9 @@ micro-controller.
|
|||||||
| 1 stepper | 85 |
|
| 1 stepper | 85 |
|
||||||
| 3 stepper | 359 |
|
| 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
|
allocate_oids count=3
|
||||||
@@ -387,14 +425,25 @@ config_stepper oid=2 step_pin=gpio27 dir_pin=gpio5 invert_step=-1 step_pulse_tic
|
|||||||
finalize_config crc=0
|
finalize_config crc=0
|
||||||
```
|
```
|
||||||
|
|
||||||
The test was last run on commit `59314d99` with gcc version
|
The test was last run on commit `14c105b8` with gcc version
|
||||||
`arm-none-eabi-gcc (Fedora 10.2.0-4.fc34) 10.2.0` on a Raspberry Pi
|
`arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0` on Raspberry Pi
|
||||||
Pico board.
|
Pico and Pico 2 boards.
|
||||||
|
|
||||||
| rp2040 | ticks |
|
| rp2040 (*) | ticks |
|
||||||
| -------------------- | ----- |
|
| -------------------- | ----- |
|
||||||
| 1 stepper | 5 |
|
| 1 stepper | 3 |
|
||||||
| 3 stepper | 22 |
|
| 3 stepper | 14 |
|
||||||
|
|
||||||
|
| 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 200Mhz internal ARM
|
||||||
|
processing rate. It is expected that 3 scheduling ticks corresponds to
|
||||||
|
~42 ARM core cycles and 14 scheduling ticks corresponds to ~225 ARM
|
||||||
|
core cycles.
|
||||||
|
|
||||||
### Linux MCU step rate benchmark
|
### Linux MCU step rate benchmark
|
||||||
|
|
||||||
@@ -433,18 +482,23 @@ When the test completes, determine the difference between the clocks
|
|||||||
reported in the two "uptime" response messages. The total number of
|
reported in the two "uptime" response messages. The total number of
|
||||||
commands per second is then `100000 * mcu_frequency / clock_diff`.
|
commands per second is then `100000 * mcu_frequency / clock_diff`.
|
||||||
|
|
||||||
Note that this test may saturate the USB/CPU capacity of a Raspberry
|
The USB tests may exceed the CPU capacity of a Raspberry Pi. If
|
||||||
Pi. If running on a Raspberry Pi, Beaglebone, or similar host computer
|
running on a Raspberry Pi, Beaglebone, or similar host computer then
|
||||||
then increase the delay (eg, `DELAY {clock + 20*freq} get_uptime`).
|
increase the delay (eg, `DELAY {clock + 20*freq} get_uptime`). Where
|
||||||
Where applicable, the benchmarks below are with console.py running on
|
applicable, the benchmarks below are with console.py running on a
|
||||||
a desktop class machine with the device connected via a high-speed
|
desktop class machine with the device connected via a super-speed hub.
|
||||||
hub.
|
|
||||||
|
The CAN bus tests may saturate the USB host controller of a Raspberry
|
||||||
|
Pi (when testing via a standard gs_usb USB to CAN bus adapter). Where
|
||||||
|
applicable, the CAN bus benchmarks below are with console.py running
|
||||||
|
on a desktop class machine with a USB to CAN bus adapter connected via
|
||||||
|
a super-speed USB hub.
|
||||||
|
|
||||||
| MCU | Rate | Build | Build compiler |
|
| MCU | Rate | Build | Build compiler |
|
||||||
| ------------------- | ---- | -------- | ------------------- |
|
| ------------------- | ---- | -------- | ------------------- |
|
||||||
| stm32f042 (CAN) | 18K | c105adc8 | arm-none-eabi-gcc (GNU Tools 7-2018-q3-update) 7.3.1 |
|
|
||||||
| atmega2560 (serial) | 23K | b161a69e | avr-gcc (GCC) 4.8.1 |
|
| atmega2560 (serial) | 23K | b161a69e | avr-gcc (GCC) 4.8.1 |
|
||||||
| sam3x8e (serial) | 23K | b161a69e | arm-none-eabi-gcc (Fedora 7.1.0-5.fc27) 7.1.0 |
|
| sam3x8e (serial) | 23K | b161a69e | arm-none-eabi-gcc (Fedora 7.1.0-5.fc27) 7.1.0 |
|
||||||
|
| rp2350 (CAN) | 59K | 17b8ce4c | arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0 |
|
||||||
| at90usb1286 (USB) | 75K | 01d2183f | avr-gcc (GCC) 5.4.0 |
|
| at90usb1286 (USB) | 75K | 01d2183f | avr-gcc (GCC) 5.4.0 |
|
||||||
| ar100 (serial) | 138K | 08d037c6 | or1k-linux-musl-gcc 9.3.0 |
|
| ar100 (serial) | 138K | 08d037c6 | or1k-linux-musl-gcc 9.3.0 |
|
||||||
| samd21 (USB) | 223K | 01d2183f | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 |
|
| samd21 (USB) | 223K | 01d2183f | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 |
|
||||||
@@ -456,7 +510,8 @@ hub.
|
|||||||
| sam4s8c (USB) | 650K | 8d4a5c16 | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 |
|
| 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 |
|
| 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 |
|
| 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
|
## Host Benchmarks
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ Alternatively, one can use a
|
|||||||
|
|
||||||
When using OpenOCD with the SAMC21, extra steps must be taken to first
|
When using OpenOCD with the SAMC21, extra steps must be taken to first
|
||||||
put the chip into Cold Plugging mode if the board makes use of the
|
put the chip into Cold Plugging mode if the board makes use of the
|
||||||
SWD pins for other purposes. If using OpenOCD on a Rasberry Pi, this
|
SWD pins for other purposes. If using OpenOCD on a Raspberry Pi, this
|
||||||
can be done by running the following commands before invoking OpenOCD.
|
can be done by running the following commands before invoking OpenOCD.
|
||||||
```
|
```
|
||||||
SWCLK=25
|
SWCLK=25
|
||||||
|
|||||||
@@ -125,10 +125,14 @@ iface can0 can static
|
|||||||
frequency. As a result, it is recommended to use a CAN bus frequency
|
frequency. As a result, it is recommended to use a CAN bus frequency
|
||||||
of 1000000 when using "USB to CAN bus bridge mode".
|
of 1000000 when using "USB to CAN bus bridge mode".
|
||||||
|
|
||||||
Even at a CAN bus frequency of 1000000, there may not be sufficient
|
* It is only valid to use USB to CAN bridge mode if there is a
|
||||||
bandwidth to run a `SHAPER_CALIBRATE` test if both the XY steppers
|
functioning CAN bus with at least one other node available (in
|
||||||
and the accelerometer all communicate via a single "USB to CAN bus"
|
addition to the bridge node itself). Use a standard USB
|
||||||
interface.
|
configuration if the goal is to communicate only with the single USB
|
||||||
|
device. Using USB to CAN bridge mode without a fully functioning CAN
|
||||||
|
bus (including terminating resistors and an additional node) may
|
||||||
|
result in sporadic errors even when communicating with the bridge
|
||||||
|
node.
|
||||||
|
|
||||||
* A USB to CAN bridge board will not appear as a USB serial device, it
|
* A USB to CAN bridge board will not appear as a USB serial device, it
|
||||||
will not show up when running `ls /dev/serial/by-id`, and it can not
|
will not show up when running `ls /dev/serial/by-id`, and it can not
|
||||||
|
|||||||
@@ -37,20 +37,36 @@ hours or more frequently) then it is an indication of a severe
|
|||||||
problem.
|
problem.
|
||||||
|
|
||||||
Incrementing `bytes_invalid` on a CAN bus connection is a symptom of
|
Incrementing `bytes_invalid` on a CAN bus connection is a symptom of
|
||||||
reordered messages on the CAN bus. There are two known causes of
|
reordered messages on the CAN bus. If seen, make sure to:
|
||||||
reordered messages:
|
* Use a Linux kernel version 6.6.0 or later.
|
||||||
1. Old versions of the popular candlight_firmware for USB CAN adapters
|
* If using a USB-to-CANBUS adapter running candlelight firmware, use
|
||||||
had a bug that could cause reordered messages. If using a USB CAN
|
v2.0 or later of candleLight_fw.
|
||||||
adapter running this firmware then make sure to update to the
|
* If using Klipper's USB-to-CANBUS bridge mode, make sure the bridge
|
||||||
latest firmware if incrementing `bytes_invalid` is observed.
|
node is flashed with Klipper v0.12.0 or later.
|
||||||
2. Some Linux kernel builds for embedded devices have been known to
|
|
||||||
reorder CAN bus messages. It may be necessary to use an alternative
|
|
||||||
Linux kernel or to use alternative hardware that supports
|
|
||||||
mainstream Linux kernels that do not exhibit this problem.
|
|
||||||
|
|
||||||
Reordered messages is a severe problem that must be fixed. It will
|
Reordered messages is a severe problem that must be fixed. It will
|
||||||
result in unstable behavior and can lead to confusing errors at any
|
result in unstable behavior and can lead to confusing errors at any
|
||||||
part of a print.
|
part of a print. An incrementing `bytes_invalid` is not caused by
|
||||||
|
wiring or similar hardware issues and can only be fixed by identifying
|
||||||
|
and updating the faulty software.
|
||||||
|
|
||||||
|
Older versions of the Linux kernel had a bug in the gs_usb canbus
|
||||||
|
driver code that could cause reordered canbus packets. The issue is
|
||||||
|
thought to be fixed in
|
||||||
|
[Linux commit 24bc41b4](https://github.com/torvalds/linux/commit/24bc41b4558347672a3db61009c339b1f5692169)
|
||||||
|
which was released in v6.6.0. In some cases, older Linux versions may
|
||||||
|
not show the problem (due to how hardware interrupts are configured),
|
||||||
|
however if problems are seen the recommended solution is to upgrade to
|
||||||
|
a newer kernel.
|
||||||
|
|
||||||
|
Older versions of candlelight firmware could reorder canbus packets,
|
||||||
|
and the issue is thought to be fixed in
|
||||||
|
[candlelight_fw commit 8b3a7b45](https://github.com/candle-usb/candleLight_fw/commit/8b3a7b4565a3c9521b762b154c94c72c5acb2bcf).
|
||||||
|
|
||||||
|
Older versions of Klipper's USB-to-CANBUS bridge code could
|
||||||
|
incorrectly drop canbus messages. This is not as severe as reordering
|
||||||
|
messages, but it should still be fixed. It is thought to be fixed with
|
||||||
|
[Klipper PR #6175](https://github.com/Klipper3d/klipper/pull/6175).
|
||||||
|
|
||||||
## Use an appropriate txqueuelen setting
|
## Use an appropriate txqueuelen setting
|
||||||
|
|
||||||
@@ -102,6 +118,23 @@ necessary to increase the `txqueuelen` above the recommended value
|
|||||||
of 128. However, as above, care should be taken when selecting a new
|
of 128. However, as above, care should be taken when selecting a new
|
||||||
value to avoid excessive round-trip-time latency.
|
value to avoid excessive round-trip-time latency.
|
||||||
|
|
||||||
|
## Use `canbus_query.py` only to identify nodes never previously seen
|
||||||
|
|
||||||
|
It is only valid to use the
|
||||||
|
[`canbus_query.py` tool](CANBUS.md#finding-the-canbus_uuid-for-new-micro-controllers)
|
||||||
|
to identify micro-controllers that have never been previously
|
||||||
|
identified. Once all nodes on a bus are identified, record the
|
||||||
|
resulting uuids in the printer.cfg, and avoid running the tool
|
||||||
|
unnecessarily.
|
||||||
|
|
||||||
|
The tool is implemented using a low-level mechanism that can cause
|
||||||
|
nodes to internally observe bus errors. These internal errors may
|
||||||
|
result in communication interruptions and may result is some nodes
|
||||||
|
disconnecting from the bus.
|
||||||
|
|
||||||
|
It is not valid to use the tool to "ping" if a node is connected. Do
|
||||||
|
not run the tool during an active print.
|
||||||
|
|
||||||
## Obtaining candump logs
|
## Obtaining candump logs
|
||||||
|
|
||||||
The CAN bus messages sent to and from the micro-controller are handled
|
The CAN bus messages sent to and from the micro-controller are handled
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ a month without updates.
|
|||||||
|
|
||||||
Once the requirements are met, you need to:
|
Once the requirements are met, you need to:
|
||||||
|
|
||||||
1. update klipper-tranlations repository
|
1. update klipper-translations repository
|
||||||
[active_translations](https://github.com/Klipper3d/klipper-translations/blob/translations/active_translations)
|
[active_translations](https://github.com/Klipper3d/klipper-translations/blob/translations/active_translations)
|
||||||
2. Optional: add a manual-index.md file in klipper-translations repository's
|
2. Optional: add a manual-index.md file in klipper-translations repository's
|
||||||
`docs\locals\<lang>` folder to replace the language specific index.md (generated
|
`docs\locals\<lang>` folder to replace the language specific index.md (generated
|
||||||
|
|||||||
@@ -286,6 +286,11 @@ The following may also be useful:
|
|||||||
during the `load_config()` or "connect event" phases. Use either
|
during the `load_config()` or "connect event" phases. Use either
|
||||||
`raise config.error("my error")` or `raise printer.config_error("my
|
`raise config.error("my error")` or `raise printer.config_error("my
|
||||||
error")` to report the error.
|
error")` to report the error.
|
||||||
|
* Do not store a reference to the `config` object in a class member
|
||||||
|
variable (nor in any similar location that may persist past initial
|
||||||
|
module loading). The `config` object is a reference to a "config
|
||||||
|
loading phase" class and it is not valid to invoke its methods after
|
||||||
|
the "config loading phase" has completed.
|
||||||
* Use the "pins" module to configure a pin on a micro-controller. This
|
* Use the "pins" module to configure a pin on a micro-controller. This
|
||||||
is typically done with something similar to
|
is typically done with something similar to
|
||||||
`printer.lookup_object("pins").setup_pin("pwm",
|
`printer.lookup_object("pins").setup_pin("pwm",
|
||||||
@@ -359,10 +364,10 @@ Useful steps:
|
|||||||
be efficient as it is typically only called during homing and
|
be efficient as it is typically only called during homing and
|
||||||
probing operations.
|
probing operations.
|
||||||
5. Other methods. Implement the `check_move()`, `get_status()`,
|
5. Other methods. Implement the `check_move()`, `get_status()`,
|
||||||
`get_steppers()`, `home()`, and `set_position()` methods. These
|
`get_steppers()`, `home()`, `clear_homing_state()`, and `set_position()`
|
||||||
functions are typically used to provide kinematic specific checks.
|
methods. These functions are typically used to provide kinematic
|
||||||
However, at the start of development one can use boiler-plate code
|
specific checks. However, at the start of development one can use
|
||||||
here.
|
boiler-plate code here.
|
||||||
6. Implement test cases. Create a g-code file with a series of moves
|
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
|
that can test important cases for the given kinematics. Follow the
|
||||||
[debugging documentation](Debugging.md) to convert this g-code file
|
[debugging documentation](Debugging.md) to convert this g-code file
|
||||||
|
|||||||
@@ -8,6 +8,72 @@ All dates in this document are approximate.
|
|||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
|
|
||||||
|
20250811: Support for the `max_accel_to_decel` parameter in the
|
||||||
|
`[printer]` config section has been removed and support for the
|
||||||
|
`ACCEL_TO_DECEL` parameter in the `SET_VELOCITY_LIMIT` command has
|
||||||
|
been removed. These capabilities were deprecated on 20240313.
|
||||||
|
|
||||||
|
20250721: The `[pca9632]` and `[mcp4018]` modules no longer accept the
|
||||||
|
`scl_pin` and `sda_pin` options. Use `i2c_software_scl_pin` and
|
||||||
|
`i2c_software_sda_pin` instead.
|
||||||
|
|
||||||
|
20250428: The maximum `cycle_time` for pwm `[output_pin]`,
|
||||||
|
`[pwm_cycle_time]`, `[pwm_tool]`, and similar config sections is now 3
|
||||||
|
seconds (reduced from 5 seconds). The `maximum_mcu_duration` in
|
||||||
|
`[pwm_tool]` is now also 3 seconds.
|
||||||
|
|
||||||
|
20250418: The manual_stepper `STOP_ON_ENDSTOP` feature may now take
|
||||||
|
less time to complete. Previously, the command would wait the entire
|
||||||
|
time the move could possibly take even if the endstop triggered
|
||||||
|
earlier. Now, the command finishes shortly after the endstop trigger.
|
||||||
|
|
||||||
|
20250417: SPI devices using "software SPI" are now rate limited.
|
||||||
|
Previously, the `spi_speed` in the config was ignored and the
|
||||||
|
transmission speed was only limited by the processing speed of the
|
||||||
|
micro-controller. Now, speeds are limited by the `spi_speed` config
|
||||||
|
parameter (actual hardware speeds are likely to be lower than the
|
||||||
|
configured value due to software overhead).
|
||||||
|
|
||||||
|
20250411: Klipper v0.13.0 released.
|
||||||
|
|
||||||
|
20250308: The `AUTO` parameter of the
|
||||||
|
`AXIS_TWIST_COMPENSATION_CALIBRATE` command has been removed.
|
||||||
|
|
||||||
|
20250131: Option `VARIABLE=<name>` in `SAVE_VARIABLE` requires lowercase
|
||||||
|
value. For example, `extruder` instead of mixedcase `Extruder` or
|
||||||
|
uppercase `EXTRUDER`. Using any uppercase letter will raise an error.
|
||||||
|
|
||||||
|
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
|
||||||
|
required 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]`
|
20240415: The `on_error_gcode` parameter in the `[virtual_sdcard]`
|
||||||
config section now has a default. If this parameter is not specified
|
config section now has a default. If this parameter is not specified
|
||||||
it now defaults to `TURN_OFF_HEATERS`. If the previous behavior is
|
it now defaults to `TURN_OFF_HEATERS`. If the previous behavior is
|
||||||
@@ -74,7 +140,7 @@ carriage are exported as `printer.dual_carriage.carriage_0` and
|
|||||||
`printer.dual_carriage.carriage_1`.
|
`printer.dual_carriage.carriage_1`.
|
||||||
|
|
||||||
20230619: The `relative_reference_index` option has been deprecated
|
20230619: The `relative_reference_index` option has been deprecated
|
||||||
and superceded by the `zero_reference_position` option. Refer to the
|
and superseded by the `zero_reference_position` option. Refer to the
|
||||||
[Bed Mesh Documentation](./Bed_Mesh.md#the-deprecated-relative_reference_index)
|
[Bed Mesh Documentation](./Bed_Mesh.md#the-deprecated-relative_reference_index)
|
||||||
for details on how to update the configuration. With this deprecation
|
for details on how to update the configuration. With this deprecation
|
||||||
the `RELATIVE_REFERENCE_INDEX` is no longer available as a parameter
|
the `RELATIVE_REFERENCE_INDEX` is no longer available as a parameter
|
||||||
@@ -308,7 +374,7 @@ endstop phases by running the ENDSTOP_PHASE_CALIBRATE command.
|
|||||||
`gear_ratio` for their rotary steppers, and they may no longer specify
|
`gear_ratio` for their rotary steppers, and they may no longer specify
|
||||||
a `step_distance` parameter. See the
|
a `step_distance` parameter. See the
|
||||||
[config reference](Config_Reference.md#stepper) for the format of the
|
[config reference](Config_Reference.md#stepper) for the format of the
|
||||||
new gear_ratio paramter.
|
new gear_ratio parameter.
|
||||||
|
|
||||||
20201213: It is not valid to specify a Z "position_endstop" when using
|
20201213: It is not valid to specify a Z "position_endstop" when using
|
||||||
"probe:z_virtual_endstop". An error will now be raised if a Z
|
"probe:z_virtual_endstop". An error will now be raised if a Z
|
||||||
|
|||||||
@@ -84,8 +84,9 @@ The printer section controls high level printer settings.
|
|||||||
[printer]
|
[printer]
|
||||||
kinematics:
|
kinematics:
|
||||||
# The type of printer in use. This option may be one of: cartesian,
|
# The type of printer in use. This option may be one of: cartesian,
|
||||||
# corexy, corexz, hybrid_corexy, hybrid_corexz, rotary_delta, delta,
|
# corexy, corexz, hybrid_corexy, hybrid_corexz, generic_cartesian,
|
||||||
# deltesian, polar, winch, or none. This parameter must be specified.
|
# rotary_delta, delta, deltesian, polar, winch, or none.
|
||||||
|
# This parameter must be specified.
|
||||||
max_velocity:
|
max_velocity:
|
||||||
# Maximum velocity (in mm/s) of the toolhead (relative to the
|
# Maximum velocity (in mm/s) of the toolhead (relative to the
|
||||||
# print). This value may be changed at runtime using the
|
# print). This value may be changed at runtime using the
|
||||||
@@ -125,8 +126,6 @@ max_accel:
|
|||||||
# decelerate to zero at each corner. The value specified here may be
|
# decelerate to zero at each corner. The value specified here may be
|
||||||
# changed at runtime using the SET_VELOCITY_LIMIT command. The
|
# changed at runtime using the SET_VELOCITY_LIMIT command. The
|
||||||
# default is 5mm/s.
|
# default is 5mm/s.
|
||||||
#max_accel_to_decel:
|
|
||||||
# This parameter is deprecated and should no longer be used.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### [stepper]
|
### [stepper]
|
||||||
@@ -712,6 +711,171 @@ anchor_z:
|
|||||||
# These parameters must be provided.
|
# These parameters must be provided.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Generic Cartesian Kinematics
|
||||||
|
|
||||||
|
See [example-generic-cartesian.cfg](../config/example-generic-caretesian.cfg)
|
||||||
|
for an example generic Cartesian kinematics config file.
|
||||||
|
|
||||||
|
This printer kinematic class allows a user to define in a pretty flexible
|
||||||
|
manner an arbitrary Cartesian-style kinematics. In principle, the regular
|
||||||
|
cartesian, corexy, hybrid_corexy can be defined this way too. However,
|
||||||
|
more importantly, various otherwise unsupported kinematics such as
|
||||||
|
inverted hybrid_corexy or corexyuv can be defined using this kinematic.
|
||||||
|
|
||||||
|
Notably, the definition of a generic Cartesian kinematic deviates
|
||||||
|
significantly from the other kinematic types. It follows the following
|
||||||
|
convention: a user defines a set of carriages with certain range of motion
|
||||||
|
that can move independently from each other (they should move over the
|
||||||
|
Cartesian axes X, Y, and Z, hence the name of the kinematic) and
|
||||||
|
corresponding endstops that allow the firmware to determine the position
|
||||||
|
of carriages during homing, as well as a set of steppers that move those
|
||||||
|
carriages. The `[printer]` section must specify the kinematic and
|
||||||
|
other printer-level settings same as the regular Cartesian kinematic:
|
||||||
|
```
|
||||||
|
[printer]
|
||||||
|
kinematics: generic_cartesian
|
||||||
|
max_velocity:
|
||||||
|
max_accel:
|
||||||
|
#minimum_cruise_ratio:
|
||||||
|
#square_corner_velocity:
|
||||||
|
#max_z_velocity:
|
||||||
|
#max_z_accel:
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Then a user must define the following three carriages: `[carriage x]`,
|
||||||
|
`[carriage y]`, and `[carriage z]`, e.g.
|
||||||
|
```
|
||||||
|
[carriage x]
|
||||||
|
endstop_pin:
|
||||||
|
# Endstop switch detection pin. If this endstop pin is on a
|
||||||
|
# different mcu than the stepper motor(s) moving this carriage,
|
||||||
|
# then it enables "multi-mcu homing". This parameter must be provided.
|
||||||
|
#position_min: 0
|
||||||
|
# Minimum valid distance (in mm) the user may command the carriage to
|
||||||
|
# move to. The default is 0mm.
|
||||||
|
position_endstop:
|
||||||
|
# Location of the endstop (in mm). This parameter must be provided.
|
||||||
|
position_max:
|
||||||
|
# Maximum valid distance (in mm) the user may command the stepper to
|
||||||
|
# move to. This parameter must be provided.
|
||||||
|
#homing_speed: 5.0
|
||||||
|
# Maximum velocity (in mm/s) of the carriage when homing. The default
|
||||||
|
# is 5mm/s.
|
||||||
|
#homing_retract_dist: 5.0
|
||||||
|
# Distance to backoff (in mm) before homing a second time during
|
||||||
|
# homing. Set this to zero to disable the second home. The default
|
||||||
|
# is 5mm.
|
||||||
|
#homing_retract_speed:
|
||||||
|
# Speed to use on the retract move after homing in case this should
|
||||||
|
# be different from the homing speed, which is the default for this
|
||||||
|
# parameter
|
||||||
|
#second_homing_speed:
|
||||||
|
# Velocity (in mm/s) of the carriage when performing the second home.
|
||||||
|
# The default is homing_speed/2.
|
||||||
|
#homing_positive_dir:
|
||||||
|
# If true, homing will cause the carriage to move in a positive
|
||||||
|
# direction (away from zero); if false, home towards zero. It is
|
||||||
|
# better to use the default than to specify this parameter. The
|
||||||
|
# default is true if position_endstop is near position_max and false
|
||||||
|
# if near position_min.
|
||||||
|
```
|
||||||
|
|
||||||
|
Afterwards, a user specifies the stepper motors that move these carriages,
|
||||||
|
for instance
|
||||||
|
```
|
||||||
|
[stepper my_stepper]
|
||||||
|
carriages:
|
||||||
|
# A string describing the carriages the stepper moves. All defined
|
||||||
|
# carriages can be specified here, as well as their linear combinations,
|
||||||
|
# e.g. x, x+y, y-0.5*z, x-z, etc. This parameter must be provided.
|
||||||
|
step_pin:
|
||||||
|
dir_pin:
|
||||||
|
enable_pin:
|
||||||
|
rotation_distance:
|
||||||
|
microsteps:
|
||||||
|
#full_steps_per_rotation: 200
|
||||||
|
#gear_ratio:
|
||||||
|
#step_pulse_duration:
|
||||||
|
```
|
||||||
|
See [stepper](#stepper) section for more information on the regular
|
||||||
|
stepper parameters. The `carriages` parameter defines how the stepper
|
||||||
|
affects the motion of the carriages. For example, `x+y` indicates that
|
||||||
|
the motion of the stepper in the positive direction by the distance `d`
|
||||||
|
moves the carriages `x` and `y` by the same distance `d` in the positive
|
||||||
|
direction, while `x-0.5*y` means the motion of the stepper in the positive
|
||||||
|
direction by the distance `d` moves the carriage `x` by the distance `d`
|
||||||
|
in the positive direction, but the carriage `y` will travel distance `d/2`
|
||||||
|
in the negative direction.
|
||||||
|
|
||||||
|
More than a single stepper motor can be defined to drive the same axis
|
||||||
|
or belt. For example, on a CoreXY AWD setups two motors driving the same
|
||||||
|
belt can be defined as
|
||||||
|
```
|
||||||
|
[carriage x]
|
||||||
|
endstop_pin: ...
|
||||||
|
...
|
||||||
|
|
||||||
|
[carriage y]
|
||||||
|
endstop_pin: ...
|
||||||
|
...
|
||||||
|
|
||||||
|
[stepper a0]
|
||||||
|
carriages: x-y
|
||||||
|
step_pin: ...
|
||||||
|
dir_pin: ...
|
||||||
|
enable_pin: ...
|
||||||
|
rotation_distance: ...
|
||||||
|
...
|
||||||
|
|
||||||
|
[stepper a1]
|
||||||
|
carriages: x-y
|
||||||
|
step_pin: ...
|
||||||
|
dir_pin: ...
|
||||||
|
enable_pin: ...
|
||||||
|
rotation_distance: ...
|
||||||
|
...
|
||||||
|
```
|
||||||
|
with `a0` and `a1` steppers having their own control pins, but
|
||||||
|
sharing the same `carriages` and corresponding endstops.
|
||||||
|
|
||||||
|
There are situations when a user wants to have more than one endstop
|
||||||
|
per axis. Examples of such configurations include Y axis driven by
|
||||||
|
two independent stepper motors with belts attached to both ends of the
|
||||||
|
X beam, with effectively two carriages on Y axis each having an
|
||||||
|
independent endstop, and multi-stepper Z axis with each stepper having
|
||||||
|
its own endstop (not to be confused with the configurations with
|
||||||
|
multiple Z motors but only a single endstop). These configurations
|
||||||
|
can be declared by specifying additional carriage(s) with their endstops:
|
||||||
|
|
||||||
|
```
|
||||||
|
[extra_carriage my_carriage]
|
||||||
|
primary_carriage:
|
||||||
|
# The name of the primary carriage this carriage corresponds to.
|
||||||
|
# It also effectively defines the axis the carriage moves over.
|
||||||
|
# This parameter must be provided.
|
||||||
|
endstop_pin:
|
||||||
|
# Endstop switch detection pin. This parameter must be provided.
|
||||||
|
```
|
||||||
|
|
||||||
|
and the corresponding stepper motors, for example:
|
||||||
|
```
|
||||||
|
[extra_carriage y1]
|
||||||
|
primary_carriage: y
|
||||||
|
endstop_pin: ...
|
||||||
|
|
||||||
|
[stepper sy1]
|
||||||
|
carriages: y1
|
||||||
|
...
|
||||||
|
```
|
||||||
|
Notably, an `[extra_carriage]` does not define parameters such as
|
||||||
|
`position_min`, `position_max`, and `position_endstop`, but instead
|
||||||
|
inherits them from the specified `primary_carriage`, thus sharing
|
||||||
|
the same range of motion with the primary carriage.
|
||||||
|
|
||||||
|
For the references on how to configure IDEX setups, see the
|
||||||
|
[dual carriage](#dual-carriage) section.
|
||||||
|
|
||||||
### None Kinematics
|
### None Kinematics
|
||||||
|
|
||||||
It is possible to define a special "none" kinematics to disable
|
It is possible to define a special "none" kinematics to disable
|
||||||
@@ -1669,14 +1833,34 @@ cs_pin:
|
|||||||
# measurements.
|
# measurements.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### [icm20948]
|
||||||
|
|
||||||
|
Support for icm20948 accelerometers.
|
||||||
|
|
||||||
|
```
|
||||||
|
[icm20948]
|
||||||
|
#i2c_address:
|
||||||
|
# Default is 104 (0x68). If AD0 is high, it would be 0x69 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.
|
||||||
|
```
|
||||||
|
|
||||||
### [lis2dw]
|
### [lis2dw]
|
||||||
|
|
||||||
Support for LIS2DW accelerometers.
|
Support for LIS2DW accelerometers.
|
||||||
|
|
||||||
```
|
```
|
||||||
[lis2dw]
|
[lis2dw]
|
||||||
cs_pin:
|
#cs_pin:
|
||||||
# The SPI enable pin for the sensor. This parameter must be provided.
|
# The SPI enable pin for the sensor. This parameter must be provided
|
||||||
|
# if using SPI.
|
||||||
#spi_speed: 5000000
|
#spi_speed: 5000000
|
||||||
# The SPI speed (in hz) to use when communicating with the chip.
|
# The SPI speed (in hz) to use when communicating with the chip.
|
||||||
# The default is 5000000.
|
# The default is 5000000.
|
||||||
@@ -1686,6 +1870,46 @@ cs_pin:
|
|||||||
#spi_software_miso_pin:
|
#spi_software_miso_pin:
|
||||||
# See the "common SPI settings" section for a description of the
|
# See the "common SPI settings" section for a description of the
|
||||||
# above parameters.
|
# 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
|
#axes_map: x, y, z
|
||||||
# See the "adxl345" section for information on this parameter.
|
# See the "adxl345" section for information on this parameter.
|
||||||
```
|
```
|
||||||
@@ -1749,11 +1973,14 @@ section of the measuring resonances guide for more information on
|
|||||||
# auto-calibration (with 'SHAPER_CALIBRATE' command). By default no
|
# auto-calibration (with 'SHAPER_CALIBRATE' command). By default no
|
||||||
# maximum smoothing is specified. Refer to Measuring_Resonances guide
|
# maximum smoothing is specified. Refer to Measuring_Resonances guide
|
||||||
# for more details on using this feature.
|
# 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
|
#min_freq: 5
|
||||||
# Minimum frequency to test for resonances. The default is 5 Hz.
|
# Minimum frequency to test for resonances. The default is 5 Hz.
|
||||||
#max_freq: 133.33
|
#max_freq: 133.33
|
||||||
# Maximum frequency to test for resonances. The default is 133.33 Hz.
|
# 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
|
# This parameter is used to determine which acceleration to use to
|
||||||
# test a specific frequency: accel = accel_per_hz * freq. Higher the
|
# test a specific frequency: accel = accel_per_hz * freq. Higher the
|
||||||
# value, the higher is the energy of the oscillations. Can be set to
|
# value, the higher is the energy of the oscillations. Can be set to
|
||||||
@@ -1767,6 +1994,13 @@ section of the measuring resonances guide for more information on
|
|||||||
# hz_per_sec. Small values make the test slow, and the large values
|
# 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
|
# will decrease the precision of the test. The default value is 1.0
|
||||||
# (Hz/sec == sec^-2).
|
# (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
|
## Config file helpers
|
||||||
@@ -2014,6 +2248,9 @@ Support for eddy current inductive probes. One may define this section
|
|||||||
sensor_type: ldc1612
|
sensor_type: ldc1612
|
||||||
# The sensor chip used to perform eddy current measurements. This
|
# The sensor chip used to perform eddy current measurements. This
|
||||||
# parameter must be provided and must be set to ldc1612.
|
# parameter must be provided and must be set to ldc1612.
|
||||||
|
#frequency:
|
||||||
|
# The external crystal frequency (in Hz) of the LDC1612 chip.
|
||||||
|
# The default is 12000000.
|
||||||
#intb_pin:
|
#intb_pin:
|
||||||
# MCU gpio pin connected to the ldc1612 sensor's INTB pin (if
|
# MCU gpio pin connected to the ldc1612 sensor's INTB pin (if
|
||||||
# available). The default is to not use the INTB pin.
|
# available). The default is to not use the INTB pin.
|
||||||
@@ -2042,9 +2279,9 @@ sensor_type: ldc1612
|
|||||||
|
|
||||||
### [axis_twist_compensation]
|
### [axis_twist_compensation]
|
||||||
|
|
||||||
A tool to compensate for inaccurate probe readings due to twist in X gantry. See
|
A tool to compensate for inaccurate probe readings due to twist in X or Y
|
||||||
the [Axis Twist Compensation Guide](Axis_Twist_Compensation.md) for more
|
gantry. See the [Axis Twist Compensation Guide](Axis_Twist_Compensation.md)
|
||||||
detailed information regarding symptoms, configuration and setup.
|
for more detailed information regarding symptoms, configuration and setup.
|
||||||
|
|
||||||
```
|
```
|
||||||
[axis_twist_compensation]
|
[axis_twist_compensation]
|
||||||
@@ -2057,16 +2294,33 @@ detailed information regarding symptoms, configuration and setup.
|
|||||||
calibrate_start_x: 20
|
calibrate_start_x: 20
|
||||||
# Defines the minimum X coordinate of the calibration
|
# Defines the minimum X coordinate of the calibration
|
||||||
# This should be the X coordinate that positions the nozzle at the starting
|
# 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
|
calibrate_end_x: 200
|
||||||
# Defines the maximum X coordinate of the calibration
|
# Defines the maximum X coordinate of the calibration
|
||||||
# This should be the X coordinate that positions the nozzle at the ending
|
# 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
|
calibrate_y: 112.5
|
||||||
# Defines the Y coordinate of the calibration
|
# Defines the Y coordinate of the calibration
|
||||||
# This should be the Y coordinate that positions the nozzle during the
|
# 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
|
# 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
|
## Additional stepper motors and extruders
|
||||||
@@ -2117,8 +2371,8 @@ for an example configuration.
|
|||||||
|
|
||||||
### [dual_carriage]
|
### [dual_carriage]
|
||||||
|
|
||||||
Support for cartesian and hybrid_corexy/z printers with dual carriages
|
Support for cartesian, generic_cartesian and hybrid_corexy/z printers with
|
||||||
on a single axis. The carriage mode can be set via the
|
dual carriages on a single axis. The carriage mode can be set via the
|
||||||
SET_DUAL_CARRIAGE extended g-code command. For example,
|
SET_DUAL_CARRIAGE extended g-code command. For example,
|
||||||
"SET_DUAL_CARRIAGE CARRIAGE=1" command will activate the carriage defined
|
"SET_DUAL_CARRIAGE CARRIAGE=1" command will activate the carriage defined
|
||||||
in this section (CARRIAGE=0 will return activation to the primary carriage).
|
in this section (CARRIAGE=0 will return activation to the primary carriage).
|
||||||
@@ -2145,7 +2399,7 @@ typically be achieved with
|
|||||||
or a similar command.
|
or a similar command.
|
||||||
|
|
||||||
See [sample-idex.cfg](../config/sample-idex.cfg) for an example
|
See [sample-idex.cfg](../config/sample-idex.cfg) for an example
|
||||||
configuration.
|
configuration with a regular Cartesian kinematic.
|
||||||
|
|
||||||
```
|
```
|
||||||
[dual_carriage]
|
[dual_carriage]
|
||||||
@@ -2159,7 +2413,7 @@ axis:
|
|||||||
# error. If safe_distance is not provided, it will be inferred from
|
# error. If safe_distance is not provided, it will be inferred from
|
||||||
# position_min and position_max for the dual and primary carriages. If set
|
# position_min and position_max for the dual and primary carriages. If set
|
||||||
# to 0 (or safe_distance is unset and position_min and position_max are
|
# to 0 (or safe_distance is unset and position_min and position_max are
|
||||||
# identical for the primary and dual carraiges), the carriages proximity
|
# identical for the primary and dual carriages), the carriages proximity
|
||||||
# checks will be disabled.
|
# checks will be disabled.
|
||||||
#step_pin:
|
#step_pin:
|
||||||
#dir_pin:
|
#dir_pin:
|
||||||
@@ -2173,6 +2427,83 @@ axis:
|
|||||||
# See the "stepper" section for the definition of the above parameters.
|
# See the "stepper" section for the definition of the above parameters.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For an example of dual carriage configuration with `generic_cartesian`
|
||||||
|
kinematic, see the following configuration
|
||||||
|
[sample](../config/example-generic-caretesian.cfg).
|
||||||
|
Please note that in this case the `[dual_carriage]` configuration deviates
|
||||||
|
from the configuration described above:
|
||||||
|
```
|
||||||
|
[dual_carriage my_dc_carriage]
|
||||||
|
primary_carriage:
|
||||||
|
# Defines the matching primary carriage of this dual carriage and
|
||||||
|
# the corresponding IDEX axis. Valid choices are x, y, z.
|
||||||
|
# This parameter must be provided.
|
||||||
|
#safe_distance:
|
||||||
|
# The minimum distance (in mm) to enforce between the dual and the primary
|
||||||
|
# carriages. If a G-Code command is executed that will bring the carriages
|
||||||
|
# closer than the specified limit, such a command will be rejected with an
|
||||||
|
# error. If safe_distance is not provided, it will be inferred from
|
||||||
|
# position_min and position_max for the dual and primary carriages. If set
|
||||||
|
# to 0 (or safe_distance is unset and position_min and position_max are
|
||||||
|
# identical for the primary and dual carriages), the carriages proximity
|
||||||
|
# checks will be disabled.
|
||||||
|
endstop_pin:
|
||||||
|
#position_min:
|
||||||
|
position_endstop:
|
||||||
|
position_max:
|
||||||
|
#homing_speed:
|
||||||
|
#homing_retract_dist:
|
||||||
|
#homing_retract_speed:
|
||||||
|
#second_homing_speed:
|
||||||
|
#homing_positive_dir:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
Refer to [generic cartesian](#generic-cartesian) section for more information
|
||||||
|
on the regular `carriage` parameters.
|
||||||
|
|
||||||
|
Then a user must define one or more stepper motors moving the dual carriage
|
||||||
|
(and other carriages as appropriate), for instance
|
||||||
|
```
|
||||||
|
[carriage x]
|
||||||
|
...
|
||||||
|
|
||||||
|
[carriage y]
|
||||||
|
...
|
||||||
|
|
||||||
|
[dual_carriage u]
|
||||||
|
primary_carriage: x
|
||||||
|
...
|
||||||
|
|
||||||
|
[stepper dc_stepper]
|
||||||
|
carriages: u-y
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
`[dual_carriage]` requires special configuration for the input shaper.
|
||||||
|
In general, it is necessary to run input shaper calibration twice -
|
||||||
|
for the `dual_carriage` and its `primary_carriage` for the axis they
|
||||||
|
share. Then the input shaper can be configured as follows, assuming the
|
||||||
|
example above:
|
||||||
|
```
|
||||||
|
[input_shaper]
|
||||||
|
# Intentionally empty
|
||||||
|
|
||||||
|
[delayed_gcode init_shaper]
|
||||||
|
initial_duration: 0.1
|
||||||
|
gcode:
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=u
|
||||||
|
SET_INPUT_SHAPER SHAPER_TYPE_X=<dual_carriage_x_shaper> SHAPER_FREQ_X=<dual_carriage_x_freq> SHAPER_TYPE_Y=<y_shaper> SHAPER_FREQ_Y=<y_freq>
|
||||||
|
SET_DUAL_CARRIAGE CARRIAGE=x
|
||||||
|
SET_INPUT_SHAPER SHAPER_TYPE_X=<primary_carriage_x_shaper> SHAPER_FREQ_X=<primary_carriage_x_freq> SHAPER_TYPE_Y=<y_shaper> SHAPER_FREQ_Y=<y_freq>
|
||||||
|
```
|
||||||
|
Note that `SHAPER_TYPE_Y` and `SHAPER_FREQ_Y` must be the same in both
|
||||||
|
commands in this case, since the same motors drive Y axis when either
|
||||||
|
of the `x` and `u` carriages are active.
|
||||||
|
|
||||||
|
It is worth noting that `generic_cartesian` kinematic can support two
|
||||||
|
dual carriages for X and Y axes. For reference, see for instance a
|
||||||
|
[sample](../config/sample-corexyuv.cfg) of CoreXYUV configuration.
|
||||||
|
|
||||||
### [extruder_stepper]
|
### [extruder_stepper]
|
||||||
|
|
||||||
Support for additional steppers synchronized to the movement of an
|
Support for additional steppers synchronized to the movement of an
|
||||||
@@ -2227,6 +2558,13 @@ printer kinematics.
|
|||||||
# Endstop switch detection pin. If specified, then one may perform
|
# Endstop switch detection pin. If specified, then one may perform
|
||||||
# "homing moves" by adding a STOP_ON_ENDSTOP parameter to
|
# "homing moves" by adding a STOP_ON_ENDSTOP parameter to
|
||||||
# MANUAL_STEPPER movement commands.
|
# MANUAL_STEPPER movement commands.
|
||||||
|
#position_min:
|
||||||
|
#position_max:
|
||||||
|
# The minimum and maximum position the stepper can be commanded to
|
||||||
|
# move to. If specified then one may not command the stepper to move
|
||||||
|
# past the given position. Note that these limits do not prevent
|
||||||
|
# setting an arbitrary position with the `MANUAL_STEPPER
|
||||||
|
# SET_POSITION=x` command. The default is to not enforce a limit.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Custom heaters and sensors
|
## Custom heaters and sensors
|
||||||
@@ -2458,6 +2796,10 @@ postfix for both sections.
|
|||||||
# "calibration_extruder_temp" option is set. Its recommended to heat
|
# "calibration_extruder_temp" option is set. Its recommended to heat
|
||||||
# the extruder some distance from the bed to minimize its impact on
|
# the extruder some distance from the bed to minimize its impact on
|
||||||
# the probe coil temperature. The default is 50.
|
# 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
|
## Temperature sensors
|
||||||
@@ -3132,11 +3474,6 @@ PCA9632 LED support. The PCA9632 is used on the FlashForge Dreamer.
|
|||||||
#i2c_speed:
|
#i2c_speed:
|
||||||
# See the "common I2C settings" section for a description of the
|
# See the "common I2C settings" section for a description of the
|
||||||
# above parameters.
|
# above parameters.
|
||||||
#scl_pin:
|
|
||||||
#sda_pin:
|
|
||||||
# Alternatively, if the pca9632 is not connected to a hardware I2C
|
|
||||||
# bus, then one may specify the "clock" (scl_pin) and "data"
|
|
||||||
# (sda_pin) pins. The default is to use hardware I2C.
|
|
||||||
#color_order: RGBW
|
#color_order: RGBW
|
||||||
# Set the pixel order of the LED (using a string containing the
|
# Set the pixel order of the LED (using a string containing the
|
||||||
# letters R, G, B, W). The default is RGBW.
|
# letters R, G, B, W). The default is RGBW.
|
||||||
@@ -3206,6 +3543,10 @@ pin:
|
|||||||
# A list of G-Code commands to execute when the button is released.
|
# A list of G-Code commands to execute when the button is released.
|
||||||
# G-Code templates are supported. The default is to not run any
|
# G-Code templates are supported. The default is to not run any
|
||||||
# commands on a button release.
|
# commands on a button release.
|
||||||
|
#debounce_delay:
|
||||||
|
# A period of time in seconds to debounce events prior to running the
|
||||||
|
# button gcode. If the button is pressed and released during this
|
||||||
|
# delay, the entire button press is ignored. Default is 0.
|
||||||
```
|
```
|
||||||
|
|
||||||
### [output_pin]
|
### [output_pin]
|
||||||
@@ -3384,8 +3725,9 @@ run_current:
|
|||||||
#stealthchop_threshold: 0
|
#stealthchop_threshold: 0
|
||||||
# The velocity (in mm/s) to set the "stealthChop" threshold to. When
|
# The velocity (in mm/s) to set the "stealthChop" threshold to. When
|
||||||
# set, "stealthChop" mode will be enabled if the stepper motor
|
# set, "stealthChop" mode will be enabled if the stepper motor
|
||||||
# velocity is below this value. The default is 0, which disables
|
# velocity is below this value. Note that the "sensorless homing"
|
||||||
# "stealthChop" mode.
|
# code may temporarily override this setting during homing
|
||||||
|
# operations. The default is 0, which disables "stealthChop" mode.
|
||||||
#coolstep_threshold:
|
#coolstep_threshold:
|
||||||
# The velocity (in mm/s) to set the TMC driver internal "CoolStep"
|
# The velocity (in mm/s) to set the TMC driver internal "CoolStep"
|
||||||
# threshold to. If set, the coolstep feature will be enabled when
|
# threshold to. If set, the coolstep feature will be enabled when
|
||||||
@@ -3434,6 +3776,7 @@ run_current:
|
|||||||
#driver_PWM_FREQ: 1
|
#driver_PWM_FREQ: 1
|
||||||
#driver_PWM_GRAD: 4
|
#driver_PWM_GRAD: 4
|
||||||
#driver_PWM_AMPL: 128
|
#driver_PWM_AMPL: 128
|
||||||
|
#driver_FREEWHEEL: 0
|
||||||
#driver_SGT: 0
|
#driver_SGT: 0
|
||||||
#driver_SEMIN: 0
|
#driver_SEMIN: 0
|
||||||
#driver_SEUP: 0
|
#driver_SEUP: 0
|
||||||
@@ -3497,8 +3840,9 @@ run_current:
|
|||||||
#stealthchop_threshold: 0
|
#stealthchop_threshold: 0
|
||||||
# The velocity (in mm/s) to set the "stealthChop" threshold to. When
|
# The velocity (in mm/s) to set the "stealthChop" threshold to. When
|
||||||
# set, "stealthChop" mode will be enabled if the stepper motor
|
# set, "stealthChop" mode will be enabled if the stepper motor
|
||||||
# velocity is below this value. The default is 0, which disables
|
# velocity is below this value. Note that the "sensorless homing"
|
||||||
# "stealthChop" mode.
|
# code may temporarily override this setting during homing
|
||||||
|
# operations. The default is 0, which disables "stealthChop" mode.
|
||||||
#driver_MULTISTEP_FILT: True
|
#driver_MULTISTEP_FILT: True
|
||||||
#driver_IHOLDDELAY: 8
|
#driver_IHOLDDELAY: 8
|
||||||
#driver_TPOWERDOWN: 20
|
#driver_TPOWERDOWN: 20
|
||||||
@@ -3513,6 +3857,7 @@ run_current:
|
|||||||
#driver_PWM_FREQ: 1
|
#driver_PWM_FREQ: 1
|
||||||
#driver_PWM_GRAD: 14
|
#driver_PWM_GRAD: 14
|
||||||
#driver_PWM_OFS: 36
|
#driver_PWM_OFS: 36
|
||||||
|
#driver_FREEWHEEL: 0
|
||||||
# Set the given register during the configuration of the TMC2208
|
# Set the given register during the configuration of the TMC2208
|
||||||
# chip. This may be used to set custom motor parameters. The
|
# chip. This may be used to set custom motor parameters. The
|
||||||
# defaults for each parameter are next to the parameter name in the
|
# defaults for each parameter are next to the parameter name in the
|
||||||
@@ -3562,6 +3907,7 @@ run_current:
|
|||||||
#driver_PWM_FREQ: 1
|
#driver_PWM_FREQ: 1
|
||||||
#driver_PWM_GRAD: 14
|
#driver_PWM_GRAD: 14
|
||||||
#driver_PWM_OFS: 36
|
#driver_PWM_OFS: 36
|
||||||
|
#driver_FREEWHEEL: 0
|
||||||
#driver_SGTHRS: 0
|
#driver_SGTHRS: 0
|
||||||
#driver_SEMIN: 0
|
#driver_SEMIN: 0
|
||||||
#driver_SEUP: 0
|
#driver_SEUP: 0
|
||||||
@@ -3700,8 +4046,9 @@ run_current:
|
|||||||
#stealthchop_threshold: 0
|
#stealthchop_threshold: 0
|
||||||
# The velocity (in mm/s) to set the "stealthChop" threshold to. When
|
# The velocity (in mm/s) to set the "stealthChop" threshold to. When
|
||||||
# set, "stealthChop" mode will be enabled if the stepper motor
|
# set, "stealthChop" mode will be enabled if the stepper motor
|
||||||
# velocity is below this value. The default is 0, which disables
|
# velocity is below this value. Note that the "sensorless homing"
|
||||||
# "stealthChop" mode.
|
# code may temporarily override this setting during homing
|
||||||
|
# operations. The default is 0, which disables "stealthChop" mode.
|
||||||
#coolstep_threshold:
|
#coolstep_threshold:
|
||||||
# The velocity (in mm/s) to set the TMC driver internal "CoolStep"
|
# The velocity (in mm/s) to set the TMC driver internal "CoolStep"
|
||||||
# threshold to. If set, the coolstep feature will be enabled when
|
# threshold to. If set, the coolstep feature will be enabled when
|
||||||
@@ -3774,6 +4121,7 @@ run_current:
|
|||||||
#driver_SEIMIN: 0
|
#driver_SEIMIN: 0
|
||||||
#driver_SFILT: 0
|
#driver_SFILT: 0
|
||||||
#driver_SG4_ANGLE_OFFSET: 1
|
#driver_SG4_ANGLE_OFFSET: 1
|
||||||
|
#driver_SLOPE_CONTROL: 0
|
||||||
# Set the given register during the configuration of the TMC2240
|
# Set the given register during the configuration of the TMC2240
|
||||||
# chip. This may be used to set custom motor parameters. The
|
# chip. This may be used to set custom motor parameters. The
|
||||||
# defaults for each parameter are next to the parameter name in the
|
# defaults for each parameter are next to the parameter name in the
|
||||||
@@ -3833,8 +4181,9 @@ run_current:
|
|||||||
#stealthchop_threshold: 0
|
#stealthchop_threshold: 0
|
||||||
# The velocity (in mm/s) to set the "stealthChop" threshold to. When
|
# The velocity (in mm/s) to set the "stealthChop" threshold to. When
|
||||||
# set, "stealthChop" mode will be enabled if the stepper motor
|
# set, "stealthChop" mode will be enabled if the stepper motor
|
||||||
# velocity is below this value. The default is 0, which disables
|
# velocity is below this value. Note that the "sensorless homing"
|
||||||
# "stealthChop" mode.
|
# code may temporarily override this setting during homing
|
||||||
|
# operations. The default is 0, which disables "stealthChop" mode.
|
||||||
#coolstep_threshold:
|
#coolstep_threshold:
|
||||||
# The velocity (in mm/s) to set the TMC driver internal "CoolStep"
|
# The velocity (in mm/s) to set the TMC driver internal "CoolStep"
|
||||||
# threshold to. If set, the coolstep feature will be enabled when
|
# threshold to. If set, the coolstep feature will be enabled when
|
||||||
@@ -4040,16 +4389,21 @@ prefix).
|
|||||||
|
|
||||||
### [mcp4018]
|
### [mcp4018]
|
||||||
|
|
||||||
Statically configured MCP4018 digipot connected via two gpio "bit
|
Statically configured MCP4018 digipot connected via i2c (one may
|
||||||
banging" pins (one may define any number of sections with an "mcp4018"
|
define any number of sections with an "mcp4018" prefix).
|
||||||
prefix).
|
|
||||||
|
|
||||||
```
|
```
|
||||||
[mcp4018 my_digipot]
|
[mcp4018 my_digipot]
|
||||||
scl_pin:
|
#i2c_address: 47
|
||||||
# The SCL "clock" pin. This parameter must be provided.
|
# The i2c address that the chip is using on the i2c bus. The default
|
||||||
sda_pin:
|
# is 47.
|
||||||
# The SDA "data" pin. This parameter must be provided.
|
#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.
|
||||||
wiper:
|
wiper:
|
||||||
# The value to statically set the given MCP4018 "wiper" to. This is
|
# The value to statically set the given MCP4018 "wiper" to. This is
|
||||||
# typically set to a number between 0.0 and 1.0 with 1.0 being the
|
# typically set to a number between 0.0 and 1.0 with 1.0 being the
|
||||||
@@ -4077,15 +4431,16 @@ Support for a display attached to the micro-controller.
|
|||||||
[display]
|
[display]
|
||||||
lcd_type:
|
lcd_type:
|
||||||
# The type of LCD chip in use. This may be "hd44780", "hd44780_spi",
|
# 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
|
# See the display sections below for information on each type and
|
||||||
# additional parameters they provide. This parameter must be
|
# additional parameters they provide. This parameter must be
|
||||||
# provided.
|
# provided.
|
||||||
#display_group:
|
#display_group:
|
||||||
# The name of the display_data group to show on the display. This
|
# The name of the display_data group to show on the display. This
|
||||||
# controls the content of the screen (see the "display_data" section
|
# controls the content of the screen (see the "display_data" section
|
||||||
# for more information). The default is _default_20x4 for hd44780
|
# for more information). The default is _default_20x4 for hd44780 or
|
||||||
# displays and _default_16x4 for other displays.
|
# aip31068_spi displays and _default_16x4 for other displays.
|
||||||
#menu_timeout:
|
#menu_timeout:
|
||||||
# Timeout for menu. Being inactive this amount of seconds will
|
# Timeout for menu. Being inactive this amount of seconds will
|
||||||
# trigger menu exit or return to root menu when having autorun
|
# trigger menu exit or return to root menu when having autorun
|
||||||
@@ -4211,6 +4566,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
|
#### st7920 display
|
||||||
|
|
||||||
Information on configuring st7920 displays (which is used in
|
Information on configuring st7920 displays (which is used in
|
||||||
@@ -4538,6 +4918,11 @@ more information.
|
|||||||
# dispatch and execution of the runout_gcode. It may be useful to
|
# dispatch and execution of the runout_gcode. It may be useful to
|
||||||
# increase this delay if OctoPrint exhibits strange pause behavior.
|
# increase this delay if OctoPrint exhibits strange pause behavior.
|
||||||
# Default is 0.5 seconds.
|
# Default is 0.5 seconds.
|
||||||
|
#debounce_delay:
|
||||||
|
# A period of time in seconds to debounce events prior to running the
|
||||||
|
# switch gcode. The switch must he held in a single state for at least
|
||||||
|
# this long to activate. If the switch is toggled on/off during this delay,
|
||||||
|
# the event is ignored. Default is 0.
|
||||||
#switch_pin:
|
#switch_pin:
|
||||||
# The pin on which the switch is connected. This parameter must be
|
# The pin on which the switch is connected. This parameter must be
|
||||||
# provided.
|
# provided.
|
||||||
@@ -4655,9 +5040,19 @@ scale.
|
|||||||
[load_cell]
|
[load_cell]
|
||||||
sensor_type:
|
sensor_type:
|
||||||
# This must be one of the supported sensor types, see below.
|
# This must be one of the supported sensor types, see below.
|
||||||
|
#counts_per_gram:
|
||||||
|
# The floating point number of sensor counts that indicates 1 gram of force.
|
||||||
|
# This value is calculated by the LOAD_CELL_CALIBRATE command.
|
||||||
|
#reference_tare_counts:
|
||||||
|
# The integer tare value, in raw sensor counts, taken when LOAD_CELL_CALIBRATE
|
||||||
|
# is run. This is the default tare value when klipper starts up.
|
||||||
|
#sensor_orientation:
|
||||||
|
# Change the sensor's orientation. Can be either 'normal' or 'inverted'.
|
||||||
|
# The default is 'normal'. Use 'inverted' if the sensor reports a
|
||||||
|
# decreasing force value when placed under load.
|
||||||
```
|
```
|
||||||
|
|
||||||
#### XH711
|
#### HX711
|
||||||
This is a 24 bit low sample rate chip using "bit-bang" communications. It is
|
This is a 24 bit low sample rate chip using "bit-bang" communications. It is
|
||||||
suitable for filament scales.
|
suitable for filament scales.
|
||||||
```
|
```
|
||||||
@@ -4725,13 +5120,89 @@ data_ready_pin:
|
|||||||
#gain: 128
|
#gain: 128
|
||||||
# Valid gain values are 128, 64, 32, 16, 8, 4, 2, 1
|
# Valid gain values are 128, 64, 32, 16, 8, 4, 2, 1
|
||||||
# The default is 128
|
# 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
|
#sample_rate: 660
|
||||||
# This chip supports two ranges of sample rates, Normal and Turbo. In turbo
|
# This chip supports two ranges of sample rates, Normal and Turbo. In turbo
|
||||||
# mode the chips c internal clock runs twice as fast and the SPI communication
|
# mode the chip's internal clock runs twice as fast and the SPI communication
|
||||||
# speed is also doubled.
|
# speed is also doubled.
|
||||||
# Normal sample rates: 20, 45, 90, 175, 330, 600, 1000
|
# Normal sample rates: 20, 45, 90, 175, 330, 600, 1000
|
||||||
# Turbo sample rates: 40, 90, 180, 350, 660, 1200, 2000
|
# Turbo sample rates: 40, 90, 180, 350, 660, 1200, 2000
|
||||||
# The default is 660
|
# 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'.
|
||||||
|
```
|
||||||
|
|
||||||
|
### [load_cell_probe]
|
||||||
|
Load Cell Probe. This combines the functionality of a [probe] and a [load_cell].
|
||||||
|
|
||||||
|
```
|
||||||
|
[load_cell_probe]
|
||||||
|
sensor_type:
|
||||||
|
# This must be one of the supported bulk ADC sensor types and support
|
||||||
|
# load cell endstops on the mcu.
|
||||||
|
#counts_per_gram:
|
||||||
|
#reference_tare_counts:
|
||||||
|
#sensor_orientation:
|
||||||
|
# These parameters must be configured before the probe will operate.
|
||||||
|
# See the [load_cell] section for further details.
|
||||||
|
#force_safety_limit: 2000
|
||||||
|
# The safe limit for probing force relative to the reference_tare_counts on
|
||||||
|
# the load_cell. The default is +/-2Kg.
|
||||||
|
#trigger_force: 75.0
|
||||||
|
# The force that the probe will trigger at. 75g is the default.
|
||||||
|
#drift_filter_cutoff_frequency: 0.8
|
||||||
|
# Enable optional continuous taring while homing & probing to reject drift.
|
||||||
|
# The value is a frequency, in Hz, below which drift will be ignored. This
|
||||||
|
# option requires the SciPy library. Default: None
|
||||||
|
#drift_filter_delay: 2
|
||||||
|
# The delay, or 'order', of the drift filter. This controls the number of
|
||||||
|
# samples required to make a trigger detection. Can be 1 or 2, the default
|
||||||
|
# is 2.
|
||||||
|
#buzz_filter_cutoff_frequency: 100.0
|
||||||
|
# The value is a frequency, in Hz, above which high frequency noise in the
|
||||||
|
# load cell will be igfiltered outnored. This option requires the SciPy
|
||||||
|
# library. Default: None
|
||||||
|
#buzz_filter_delay: 2
|
||||||
|
# The delay, or 'order', of the buzz filter. This controls the number of
|
||||||
|
# samples required to make a trigger detection. Can be 1 or 2, the default
|
||||||
|
# is 2.
|
||||||
|
#notch_filter_frequencies: 50, 60
|
||||||
|
# 1 or 2 frequencies, in Hz, to filter out of the load cell data. This is
|
||||||
|
# intended to reject power line noise. This option requires the SciPy
|
||||||
|
# library. Default: None
|
||||||
|
#notch_filter_quality: 2.0
|
||||||
|
# Controls how narrow the range of frequencies are that the notch filter
|
||||||
|
# removes. Larger numbers produce a narrower filter. Minimum value is 0.5 and
|
||||||
|
# maximum is 3.0. Default: 2.0
|
||||||
|
#tare_time:
|
||||||
|
# The rime in seconds used for taring the load_cell before each probe. The
|
||||||
|
# default value is: 4 / 60 = 0.066. This collects samples from 4 cycles of
|
||||||
|
# 60Hz mains power to cancel power line noise.
|
||||||
|
#z_offset:
|
||||||
|
#speed:
|
||||||
|
#samples:
|
||||||
|
#sample_retract_dist:
|
||||||
|
#lift_speed:
|
||||||
|
#samples_result:
|
||||||
|
#samples_tolerance:
|
||||||
|
#samples_tolerance_retries:
|
||||||
|
#activate_gcode:
|
||||||
|
#deactivate_gcode:
|
||||||
|
# See the "[probe]" section for a description of the above parameters.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Board specific hardware support
|
## Board specific hardware support
|
||||||
@@ -4820,6 +5291,50 @@ vssa_pin:
|
|||||||
# noise. The default is 2 seconds.
|
# 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 supply 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
|
||||||
|
# corresponding lines. For example
|
||||||
|
# DIFF03 measures the differential between line 0 and 3. Only specific
|
||||||
|
# combinations for the differentials are allowed.
|
||||||
|
```
|
||||||
|
|
||||||
### [replicape]
|
### [replicape]
|
||||||
|
|
||||||
Replicape support - see the [beaglebone guide](Beaglebone.md) and the
|
Replicape support - see the [beaglebone guide](Beaglebone.md) and the
|
||||||
@@ -4901,7 +5416,7 @@ Octoprint as they will conflict, and 1 will fail to initialize
|
|||||||
properly likely aborting your print.
|
properly likely aborting your print.
|
||||||
|
|
||||||
If you use Octoprint and stream gcode over the serial port instead of
|
If you use Octoprint and stream gcode over the serial port instead of
|
||||||
printing from virtual_sd, then remo **M1** and **M0** from *Pausing commands*
|
printing from virtual_sd, then remove **M1** and **M0** from *Pausing commands*
|
||||||
in *Settings > Serial Connection > Firmware & protocol* will prevent
|
in *Settings > Serial Connection > Firmware & protocol* will prevent
|
||||||
the need to start print on the Palette 2 and unpausing in Octoprint
|
the need to start print on the Palette 2 and unpausing in Octoprint
|
||||||
for your print to begin.
|
for your print to begin.
|
||||||
@@ -4925,8 +5440,9 @@ serial:
|
|||||||
### [angle]
|
### [angle]
|
||||||
|
|
||||||
Magnetic hall angle sensor support for reading stepper motor angle
|
Magnetic hall angle sensor support for reading stepper motor angle
|
||||||
shaft measurements using a1333, as5047d, or tle5012b SPI chips. The
|
shaft measurements using a1333, as5047d, mt6816, mt6826s,
|
||||||
measurements are available via the [API Server](API_Server.md) and
|
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).
|
[motion analysis tool](Debugging.md#motion-analysis-and-data-logging).
|
||||||
See the [G-Code reference](G-Codes.md#angle) for available commands.
|
See the [G-Code reference](G-Codes.md#angle) for available commands.
|
||||||
|
|
||||||
@@ -4934,7 +5450,7 @@ See the [G-Code reference](G-Codes.md#angle) for available commands.
|
|||||||
[angle my_angle_sensor]
|
[angle my_angle_sensor]
|
||||||
sensor_type:
|
sensor_type:
|
||||||
# The type of the magnetic hall sensor chip. Available choices are
|
# 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.
|
# specified.
|
||||||
#sample_period: 0.000400
|
#sample_period: 0.000400
|
||||||
# The query period (in seconds) to use during measurements. The
|
# The query period (in seconds) to use during measurements. The
|
||||||
@@ -4997,8 +5513,9 @@ Most Klipper micro-controller implementations only support an
|
|||||||
micro-controller supports a 400000 speed (*fast mode*, 400kbit/s), but it must be
|
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)
|
[set in the operating system](RPi_microcontroller.md#optional-enabling-i2c)
|
||||||
and the `i2c_speed` parameter is otherwise ignored. The Klipper
|
and the `i2c_speed` parameter is otherwise ignored. The Klipper
|
||||||
"RP2040" micro-controller and ATmega AVR family support a rate of 400000
|
"RP2040" micro-controller and ATmega AVR family and some STM32
|
||||||
via the `i2c_speed` parameter. All other Klipper micro-controllers use a
|
(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.
|
100000 rate and ignore the `i2c_speed` parameter.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -132,3 +132,10 @@ There are several
|
|||||||
you have questions on the code then you can also ask in the
|
you have questions on the code then you can also ask in the
|
||||||
[Klipper Discourse Forum](#discourse-forum) or on the
|
[Klipper Discourse Forum](#discourse-forum) or on the
|
||||||
[Klipper Discord Chat](#discord-chat).
|
[Klipper Discord Chat](#discord-chat).
|
||||||
|
|
||||||
|
## Professional Services
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Custom software development, software support, and solutions:
|
||||||
|
[https://ko-fi.com/koconnor](https://ko-fi.com/koconnor)
|
||||||
|
|||||||
@@ -78,7 +78,9 @@ for further details on how to configure a `temperature_probe`. It is
|
|||||||
advised to configure the `calibration_position`,
|
advised to configure the `calibration_position`,
|
||||||
`calibration_extruder_temp`, `extruder_heating_z`, and
|
`calibration_extruder_temp`, `extruder_heating_z`, and
|
||||||
`calibration_bed_temp` options, as doing so will automate some of the
|
`calibration_bed_temp` options, as doing so will automate some of the
|
||||||
steps outlined below.
|
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
|
Eddy probe manufacturers may offer a stock drift calibration that can be
|
||||||
manually added to `drift_calibration` option of the `[probe_eddy_current]`
|
manually added to `drift_calibration` option of the `[probe_eddy_current]`
|
||||||
|
|||||||
@@ -102,11 +102,13 @@ Klipper supports many standard 3d printer features:
|
|||||||
printers.
|
printers.
|
||||||
|
|
||||||
* Automatic bed leveling support. Klipper can be configured for basic
|
* Automatic bed leveling support. Klipper can be configured for basic
|
||||||
bed tilt detection or full mesh bed leveling. If the bed uses
|
bed tilt detection or full mesh bed leveling. The bed mesh can be
|
||||||
|
customized to the print size (adaptive bed mesh). If the bed uses
|
||||||
multiple Z steppers then Klipper can also level by independently
|
multiple Z steppers then Klipper can also level by independently
|
||||||
manipulating the Z steppers. Most Z height probes are supported,
|
manipulating the Z steppers. Most Z height probes are supported,
|
||||||
including BL-Touch probes and servo activated probes. Probes may be
|
including BL-Touch probes and servo activated probes. Probes may be
|
||||||
calibrated for axis twist compensation.
|
calibrated for axis twist compensation. If using an "eddy current
|
||||||
|
probe" then one can utilize fast bed mesh scanning,
|
||||||
|
|
||||||
* Automatic delta calibration support. The calibration tool can
|
* Automatic delta calibration support. The calibration tool can
|
||||||
perform basic height calibration as well as an enhanced X and Y
|
perform basic height calibration as well as an enhanced X and Y
|
||||||
@@ -118,7 +120,7 @@ Klipper supports many standard 3d printer features:
|
|||||||
|
|
||||||
* Support for common temperature sensors (eg, common thermistors,
|
* Support for common temperature sensors (eg, common thermistors,
|
||||||
AD595, AD597, AD849x, PT100, PT1000, MAX6675, MAX31855, MAX31856,
|
AD595, AD597, AD849x, PT100, PT1000, MAX6675, MAX31855, MAX31856,
|
||||||
MAX31865, BME280, HTU21D, DS18B20, AHT10, and LM75). Custom
|
MAX31865, BME280, HTU21D, DS18B20, AHT10, SHT3x, and LM75). Custom
|
||||||
thermistors and custom analog temperature sensors can also be
|
thermistors and custom analog temperature sensors can also be
|
||||||
configured. One can monitor the internal micro-controller
|
configured. One can monitor the internal micro-controller
|
||||||
temperature sensor and the internal temperature sensor of a
|
temperature sensor and the internal temperature sensor of a
|
||||||
@@ -128,7 +130,8 @@ Klipper supports many standard 3d printer features:
|
|||||||
|
|
||||||
* Support for standard fans, nozzle fans, and temperature controlled
|
* Support for standard fans, nozzle fans, and temperature controlled
|
||||||
fans. No need to keep fans running when the printer is idle. Fan
|
fans. No need to keep fans running when the printer is idle. Fan
|
||||||
speed can be monitored on fans that have a tachometer.
|
speed can be monitored on fans that have a tachometer. One can
|
||||||
|
assign a "math formula" to a fan for automatic fan speed updating.
|
||||||
|
|
||||||
* Support for run-time configuration of TMC2130, TMC2208/TMC2224,
|
* Support for run-time configuration of TMC2130, TMC2208/TMC2224,
|
||||||
TMC2209, TMC2240, TMC2660, and TMC5160 stepper motor drivers. There
|
TMC2209, TMC2240, TMC2660, and TMC5160 stepper motor drivers. There
|
||||||
@@ -154,7 +157,7 @@ Klipper supports many standard 3d printer features:
|
|||||||
filament width sensors.
|
filament width sensors.
|
||||||
|
|
||||||
* Support for measuring and recording acceleration using adxl345,
|
* Support for measuring and recording acceleration using adxl345,
|
||||||
mpu9250, mpu6050, and lis2dw12 accelerometers.
|
mpu9250, mpu6050, lis2dw12, lis3dh, and icm20948 accelerometers.
|
||||||
|
|
||||||
* Support for limiting the top speed of short "zigzag" moves to reduce
|
* Support for limiting the top speed of short "zigzag" moves to reduce
|
||||||
printer vibration and noise. See the [kinematics](Kinematics.md)
|
printer vibration and noise. See the [kinematics](Kinematics.md)
|
||||||
@@ -184,13 +187,16 @@ represent total number of steps per second on the micro-controller.
|
|||||||
| SAM4S8C | 1690K | 1385K |
|
| SAM4S8C | 1690K | 1385K |
|
||||||
| LPC1768 | 1923K | 1351K |
|
| LPC1768 | 1923K | 1351K |
|
||||||
| LPC1769 | 2353K | 1622K |
|
| LPC1769 | 2353K | 1622K |
|
||||||
| RP2040 | 2400K | 1636K |
|
|
||||||
| SAM4E8E | 2500K | 1674K |
|
| SAM4E8E | 2500K | 1674K |
|
||||||
| SAMD51 | 3077K | 1885K |
|
| SAMD51 | 3077K | 1885K |
|
||||||
| AR100 | 3529K | 2507K |
|
| AR100 | 3529K | 2507K |
|
||||||
|
| STM32G431 | 3617K | 2452K |
|
||||||
| STM32F407 | 3652K | 2459K |
|
| STM32F407 | 3652K | 2459K |
|
||||||
| STM32F446 | 3913K | 2634K |
|
| STM32F446 | 3913K | 2634K |
|
||||||
| STM32H743 | 9091K | 6061K |
|
| RP2040 | 4000K | 2571K |
|
||||||
|
| RP2350 | 4167K | 2663K |
|
||||||
|
| SAME70 | 6667K | 4737K |
|
||||||
|
| STM32H723 | 7429K | 8619K |
|
||||||
|
|
||||||
If unsure of the micro-controller on a particular board, find the
|
If unsure of the micro-controller on a particular board, find the
|
||||||
appropriate [config file](../config/), and look for the
|
appropriate [config file](../config/), and look for the
|
||||||
|
|||||||
487
docs/G-Codes.md
487
docs/G-Codes.md
@@ -127,6 +127,14 @@ use this tool the Python "numpy" package must be installed (see the
|
|||||||
[measuring resonance document](Measuring_Resonances.md#software-installation)
|
[measuring resonance document](Measuring_Resonances.md#software-installation)
|
||||||
for more information).
|
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
|
||||||
`ANGLE_DEBUG_READ CHIP=<config_name> REG=<register>`: Queries sensor
|
`ANGLE_DEBUG_READ CHIP=<config_name> REG=<register>`: Queries sensor
|
||||||
register "register" (e.g. 44 or 0x2C). Can be useful for debugging
|
register "register" (e.g. 44 or 0x2C). Can be useful for debugging
|
||||||
@@ -146,9 +154,13 @@ The following commands are available when the
|
|||||||
section](Config_Reference.md#axis_twist_compensation) is enabled.
|
section](Config_Reference.md#axis_twist_compensation) is enabled.
|
||||||
|
|
||||||
#### AXIS_TWIST_COMPENSATION_CALIBRATE
|
#### AXIS_TWIST_COMPENSATION_CALIBRATE
|
||||||
`AXIS_TWIST_COMPENSATION_CALIBRATE [SAMPLE_COUNT=<value>]`: Initiates the X
|
`AXIS_TWIST_COMPENSATION_CALIBRATE [AXIS=<X|Y>] [SAMPLE_COUNT=<value>]`
|
||||||
twist calibration wizard. `SAMPLE_COUNT` specifies the number of points along
|
|
||||||
the X axis to calibrate at and defaults to 3.
|
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'`.
|
||||||
|
|
||||||
### [bed_mesh]
|
### [bed_mesh]
|
||||||
|
|
||||||
@@ -162,8 +174,10 @@ The following commands are available when the
|
|||||||
[ADAPTIVE_MARGIN=<value>]`: This command probes the bed using generated points
|
[ADAPTIVE_MARGIN=<value>]`: This command probes the bed using generated points
|
||||||
specified by the parameters in the config. After probing, a mesh is generated
|
specified by the parameters in the config. After probing, a mesh is generated
|
||||||
and z-movement is adjusted according to the mesh.
|
and z-movement is adjusted according to the mesh.
|
||||||
|
The mesh is immediately active after successful completion of `BED_MESH_CALIBRATE`.
|
||||||
The mesh will be saved into a profile specified by the `PROFILE` parameter,
|
The mesh will be saved into a profile specified by the `PROFILE` parameter,
|
||||||
or `default` if unspecified.
|
or `default` if unspecified. If ADAPTIVE=1 is specified then the profile
|
||||||
|
name will begin with `adaptive-` and should not be saved for reuse.
|
||||||
See the PROBE command for details on the optional probe parameters. If
|
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
|
METHOD=manual is specified then the manual probing tool is activated - see the
|
||||||
MANUAL_PROBE command above for details on the additional commands available
|
MANUAL_PROBE command above for details on the additional commands available
|
||||||
@@ -329,15 +343,18 @@ The following command is available when the
|
|||||||
enabled.
|
enabled.
|
||||||
|
|
||||||
#### SET_DUAL_CARRIAGE
|
#### SET_DUAL_CARRIAGE
|
||||||
`SET_DUAL_CARRIAGE CARRIAGE=[0|1] [MODE=[PRIMARY|COPY|MIRROR]]`:
|
`SET_DUAL_CARRIAGE CARRIAGE=<carriage> [MODE=[PRIMARY|COPY|MIRROR]]`:
|
||||||
This command will change the mode of the specified carriage.
|
This command will change the mode of the specified carriage.
|
||||||
If no `MODE` is provided it defaults to `PRIMARY`. Setting the mode
|
If no `MODE` is provided it defaults to `PRIMARY`. `<carriage>` must
|
||||||
to `PRIMARY` deactivates the other carriage and makes the specified
|
reference a defined primary or dual carriage for `generic_cartesian`
|
||||||
carriage execute subsequent G-Code commands as-is. `COPY` and `MIRROR`
|
kinematics or be 0 (for primary carriage) or 1 (for dual carriage)
|
||||||
modes are supported only for `CARRIAGE=1`. When set to either of these
|
for all other kinematics supporting IDEX. Setting the mode to `PRIMARY`
|
||||||
modes, carriage 1 will then track the subsequent moves of the carriage 0
|
deactivates the other carriage and makes the specified carriage execute
|
||||||
and either copy relative movements of it (in `COPY` mode) or execute them
|
subsequent G-Code commands as-is. `COPY` and `MIRROR` modes are supported
|
||||||
in the opposite (mirror) direction (in `MIRROR` mode).
|
only for dual carriages. When set to either of these modes, dual carriage
|
||||||
|
will then track the subsequent moves of its primary carriage and either
|
||||||
|
copy relative movements of it (in `COPY` mode) or execute them in the
|
||||||
|
opposite (mirror) direction (in `MIRROR` mode).
|
||||||
|
|
||||||
#### SAVE_DUAL_CARRIAGE_STATE
|
#### SAVE_DUAL_CARRIAGE_STATE
|
||||||
`SAVE_DUAL_CARRIAGE_STATE [NAME=<state_name>]`: Save the current positions
|
`SAVE_DUAL_CARRIAGE_STATE [NAME=<state_name>]`: Save the current positions
|
||||||
@@ -355,7 +372,7 @@ restored and "MOVE_SPEED" is specified, then the toolhead moves will be
|
|||||||
performed with the given speed (in mm/s); otherwise the toolhead move will
|
performed with the given speed (in mm/s); otherwise the toolhead move will
|
||||||
use the rail homing speed. Note that the carriages restore their positions
|
use the rail homing speed. Note that the carriages restore their positions
|
||||||
only over their own axis, which may be necessary to correctly restore COPY
|
only over their own axis, which may be necessary to correctly restore COPY
|
||||||
and MIRROR mode of the dual carraige.
|
and MIRROR mode of the dual carriage.
|
||||||
|
|
||||||
### [endstop_phase]
|
### [endstop_phase]
|
||||||
|
|
||||||
@@ -476,6 +493,20 @@ enabled.
|
|||||||
`SET_FAN_SPEED FAN=config_name SPEED=<speed>` This command sets the
|
`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.
|
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]
|
### [filament_switch_sensor]
|
||||||
|
|
||||||
The following command is available when a
|
The following command is available when a
|
||||||
@@ -553,15 +584,51 @@ state; issue a G28 afterwards to reset the kinematics. This command is
|
|||||||
intended for low-level diagnostics and debugging.
|
intended for low-level diagnostics and debugging.
|
||||||
|
|
||||||
#### SET_KINEMATIC_POSITION
|
#### 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
|
`SET_KINEMATIC_POSITION [X=<value>] [Y=<value>] [Z=<value>]
|
||||||
cartesian position. This is a diagnostic and debugging command; use
|
[SET_HOMED=<[X][Y][Z]>] [CLEAR_HOMED=<[X][Y][Z]>]`: Force the
|
||||||
SET_GCODE_OFFSET and/or G92 for regular axis transformations. If an
|
low-level kinematic code to believe the toolhead is at the given
|
||||||
axis is not specified then it will default to the position that the
|
cartesian position and set/clear homed status. This is a diagnostic
|
||||||
head was last commanded to. Setting an incorrect or invalid position
|
and debugging command; use SET_GCODE_OFFSET and/or G92 for regular
|
||||||
may lead to internal software errors. This command may invalidate
|
axis transformations. Setting an incorrect or invalid position may
|
||||||
future boundary checks; issue a G28 afterwards to reset the
|
lead to internal software errors.
|
||||||
kinematics.
|
|
||||||
|
The `X`, `Y`, and `Z` parameters are used to alter the low-level
|
||||||
|
kinematic position tracking. If any of these parameters are not set
|
||||||
|
then the position is not changed - for example `SET_KINEMATIC_POSITION
|
||||||
|
Z=10` would set all axes as homed, set the internal Z position to 10,
|
||||||
|
and leave the X and Y positions unchanged. Changing the internal
|
||||||
|
position tracking is not dependent on the internal homing state - one
|
||||||
|
may alter the position for both homed and not homed axes, and
|
||||||
|
similarly one may set or clear the homing state of an axis without
|
||||||
|
altering its internal position.
|
||||||
|
|
||||||
|
The `SET_HOMED` parameter defaults to `XYZ` which instructs the
|
||||||
|
kinematics to consider all axes as homed. A bare
|
||||||
|
`SET_KINEMATIC_POSITION` command will result in all axes being
|
||||||
|
considered homed (and not change its current position). If it is not
|
||||||
|
desired to change the state of homed axes then assign `SET_HOMED` to
|
||||||
|
an empty string - for example:
|
||||||
|
`SET_KINEMATIC_POSITION SET_HOMED= X=10`. It is also possible to
|
||||||
|
request an individual axis be considered homed (eg, `SET_HOMED=X`),
|
||||||
|
but note that non-cartesian style kinematics (such as delta
|
||||||
|
kinematics) may not support setting an individual axis as homed.
|
||||||
|
|
||||||
|
The `CLEAR_HOMED` parameter instructs the kinematics to consider the
|
||||||
|
given axes as not homed. For example, `CLEAR_HOMED=XYZ` would request
|
||||||
|
all axes to be considered not homed (and thus require homing prior to
|
||||||
|
movement on those axes). The default is `SET_HOMED=XYZ` even if
|
||||||
|
`CLEAR_HOMED` is present, so the command `SET_KINEMATIC_POSITION
|
||||||
|
CLEAR_HOMED=Z` will set X and Y as homed and clear the homing state
|
||||||
|
for Z. Use `SET_KINEMATIC_POSITION SET_HOMED= CLEAR_HOMED=Z` if the
|
||||||
|
goal is to clear only the Z homing state. If an axis is specified in
|
||||||
|
neither `SET_HOMED` nor `CLEAR_HOMED` then its homing state is not
|
||||||
|
changed and if it is specified in both then `CLEAR_HOMED` has
|
||||||
|
precedence. It is possible to request clearing of an individual axis,
|
||||||
|
but on non-cartesian style kinematics (such as delta kinematics) doing
|
||||||
|
so may result in clearing the homing state of additional axes. Note
|
||||||
|
the `CLEAR` parameter is currently an alias for the `CLEAR_HOMED`
|
||||||
|
parameter, but this alias will be removed in the future.
|
||||||
|
|
||||||
### [gcode]
|
### [gcode]
|
||||||
|
|
||||||
@@ -653,6 +720,46 @@ is specified then the toolhead move will be performed with the given
|
|||||||
speed (in mm/s); otherwise the toolhead move will use the restored
|
speed (in mm/s); otherwise the toolhead move will use the restored
|
||||||
g-code speed.
|
g-code speed.
|
||||||
|
|
||||||
|
### [generic_cartesian]
|
||||||
|
The commands in this section become automatically available when
|
||||||
|
`kinematics: generic_cartesian` is specified as the printer kinematics.
|
||||||
|
|
||||||
|
#### SET_STEPPER_CARRIAGES
|
||||||
|
`SET_STEPPER_CARRIAGES STEPPER=<stepper_name> CARRIAGES=<carriages>
|
||||||
|
[DISABLE_CHECKS=[0|1]]`: Set or update the stepper carriages.
|
||||||
|
`<stepper_name>` must reference an existing stepper defined in `printer.cfg`,
|
||||||
|
and `<carriages>` describes the carriages the stepper moves. See
|
||||||
|
[Generic Cartesian Kinematics](Config_Reference.md#generic-cartesian-kinematics)
|
||||||
|
for a more detailed overview of the `carriages` parameter in the
|
||||||
|
stepper configuration section. Note that it is only possible
|
||||||
|
to change the coefficients or signs of the carriages with this
|
||||||
|
command, but a user cannot add or remove the carriages that the stepper
|
||||||
|
controls.
|
||||||
|
|
||||||
|
`SET_STEPPER_CARRIAGES` is an advanced tool, and the user is advised
|
||||||
|
to exercise an extreme caution using it, since specifying incorrect
|
||||||
|
configuration may physically damage the printer.
|
||||||
|
|
||||||
|
Note that `SET_STEPPER_CARRIAGES` performs certain internal validations
|
||||||
|
of the new printer kinematics after the change. Keep in mind that if it
|
||||||
|
detects an issue, it may leave printer kinematics in an invalid state.
|
||||||
|
This means that if `SET_STEPPER_CARRIAGES` reports an error, it is unsafe
|
||||||
|
to issue other GCode commands, and the user must inspect the error message
|
||||||
|
and either fix the problem, or manually restore the previous stepper(s)
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
Since `SET_STEPPER_CARRIAGES` can update a configuration of a single
|
||||||
|
stepper at a time, some sequences of changes can lead to invalid
|
||||||
|
intermediate kinematic configurations, even if the final configuration
|
||||||
|
is valid. In such cases a user can pass `DISABLE_CHECKS=1` parameters to
|
||||||
|
all but the last command to disable intermediate checks. For example,
|
||||||
|
if `stepper a` and `stepper b` initially have `x-y` and `x+y` carriages
|
||||||
|
correspondingly, then the following sequence of commands will let a user
|
||||||
|
effectively swap the carriage controls:
|
||||||
|
`SET_STEPPER_CARRIAGES STEPPER=a CARRIAGES=x+y DISABLE_CHECKS=1`
|
||||||
|
and `SET_STEPPER_CARRIAGES STEPPER=b CARRIAGES=x-y`, while
|
||||||
|
still validating the final kinematics state.
|
||||||
|
|
||||||
### [hall_filament_width_sensor]
|
### [hall_filament_width_sensor]
|
||||||
|
|
||||||
The following commands are available when the
|
The following commands are available when the
|
||||||
@@ -731,6 +838,116 @@ together with either of SHAPER_TYPE_X and SHAPER_TYPE_Y parameters.
|
|||||||
See [config reference](Config_Reference.md#input_shaper) for more
|
See [config reference](Config_Reference.md#input_shaper) for more
|
||||||
details on each of these parameters.
|
details on each of these parameters.
|
||||||
|
|
||||||
|
### [led]
|
||||||
|
|
||||||
|
The following command is available when any of the
|
||||||
|
[led config sections](Config_Reference.md#leds) are enabled.
|
||||||
|
|
||||||
|
#### SET_LED
|
||||||
|
`SET_LED LED=<config_name> RED=<value> GREEN=<value> BLUE=<value>
|
||||||
|
WHITE=<value> [INDEX=<index>] [TRANSMIT=0] [SYNC=1]`: This sets the
|
||||||
|
LED output. Each color `<value>` must be between 0.0 and 1.0. The
|
||||||
|
WHITE option is only valid on RGBW LEDs. If the LED supports multiple
|
||||||
|
chips in a daisy-chain then one may specify INDEX to alter the color
|
||||||
|
of just the given chip (1 for the first chip, 2 for the second,
|
||||||
|
etc.). If INDEX is not provided then all LEDs in the daisy-chain will
|
||||||
|
be set to the provided color. If TRANSMIT=0 is specified then the
|
||||||
|
color change will only be made on the next SET_LED command that does
|
||||||
|
not specify TRANSMIT=0; this may be useful in combination with the
|
||||||
|
INDEX parameter to batch multiple updates in a daisy-chain. By
|
||||||
|
default, the SET_LED command will sync it's changes with other ongoing
|
||||||
|
gcode commands. This can lead to undesirable behavior if LEDs are
|
||||||
|
being set while the printer is not printing as it will reset the idle
|
||||||
|
timeout. If careful timing is not needed, the optional SYNC=0
|
||||||
|
parameter can be specified to apply the changes without resetting the
|
||||||
|
idle timeout.
|
||||||
|
|
||||||
|
#### SET_LED_TEMPLATE
|
||||||
|
`SET_LED_TEMPLATE LED=<led_name> TEMPLATE=<template_name>
|
||||||
|
[<param_x>=<literal>] [INDEX=<index>]`: Assign a
|
||||||
|
[display_template](Config_Reference.md#display_template) to a given
|
||||||
|
[LED](Config_Reference.md#leds). For example, if one defined a
|
||||||
|
`[display_template my_led_template]` config section then one could
|
||||||
|
assign `TEMPLATE=my_led_template` here. The display_template should
|
||||||
|
produce a comma separated string containing four floating point
|
||||||
|
numbers corresponding to red, green, blue, and white color settings.
|
||||||
|
The template will be continuously evaluated and the LED will be
|
||||||
|
automatically set to the resulting colors. One may set
|
||||||
|
display_template parameters to use during template evaluation
|
||||||
|
(parameters will be parsed as Python literals). If INDEX is not
|
||||||
|
specified then all chips in the LED's daisy-chain will be set to the
|
||||||
|
template, otherwise only the chip with the given index will be
|
||||||
|
updated. If TEMPLATE is an empty string then this command will clear
|
||||||
|
any previous template assigned to the LED (one can then use `SET_LED`
|
||||||
|
commands to manage the LED's color settings).
|
||||||
|
|
||||||
|
### [load_cell]
|
||||||
|
|
||||||
|
The following commands are enabled if a
|
||||||
|
[load_cell config section](Config_Reference.md#load_cell) has been enabled.
|
||||||
|
|
||||||
|
### LOAD_CELL_DIAGNOSTIC
|
||||||
|
`LOAD_CELL_DIAGNOSTIC [LOAD_CELL=<config_name>]`: This command collects 10
|
||||||
|
seconds of load cell data and reports statistics that can help you verify proper
|
||||||
|
operation of the load cell. This command can be run on both calibrated and
|
||||||
|
uncalibrated load cells.
|
||||||
|
|
||||||
|
### LOAD_CELL_CALIBRATE
|
||||||
|
`LOAD_CELL_CALIBRATE [LOAD_CELL=<config_name>]`: Start the guided calibration
|
||||||
|
utility. Calibration is a 3 step process:
|
||||||
|
1. First you remove all load from the load cell and run the `TARE` command
|
||||||
|
2. Next you apply a known load to the load cell and run the
|
||||||
|
`CALIBRATE GRAMS=nnn` command
|
||||||
|
3. Finally use the `ACCEPT` command to save the results
|
||||||
|
|
||||||
|
You can cancel the calibration process at any time with `ABORT`.
|
||||||
|
|
||||||
|
### LOAD_CELL_TARE
|
||||||
|
`LOAD_CELL_TARE [LOAD_CELL=<config_name>]`: This works just like the tare button
|
||||||
|
on digital scale. It sets the current raw reading of the load cell to be the
|
||||||
|
zero point reference value. The response is the percentage of the sensors range
|
||||||
|
that was read and the raw value in counts. If the load cell is calibrated a
|
||||||
|
force in grams is also reported.
|
||||||
|
|
||||||
|
### LOAD_CELL_READ load_cell="name"
|
||||||
|
`LOAD_CELL_READ [LOAD_CELL=<config_name>]`:
|
||||||
|
This command takes a reading from the load cell. The response is the percentage
|
||||||
|
of the sensors range that was read and the raw value in counts. If the load cell
|
||||||
|
is calibrated a force in grams is also reported.
|
||||||
|
|
||||||
|
### [load_cell_probe]
|
||||||
|
|
||||||
|
The following commands are enabled if a
|
||||||
|
[load_cell config section](Config_Reference.md#load_cell_probe) has been
|
||||||
|
enabled.
|
||||||
|
|
||||||
|
### LOAD_CELL_TEST_TAP
|
||||||
|
`LOAD_CELL_TEST_TAP [TAPS=<taps>] [TIMEOUT=<timeout>]`: Run a testing routine
|
||||||
|
that reports taps on the load cell. The toolhead will not move but the load cell
|
||||||
|
probe will sense taps just as if it was probing. This can be used as a
|
||||||
|
sanity check to make sure that the probe works. This tool replaces
|
||||||
|
QUERY_ENDSTOPS and QUERY_PROBE for load cell probes.
|
||||||
|
- `TAPS`: the number of taps the tool expects
|
||||||
|
- `TIMEOOUT`: the time, in seconds, that the tool waits for each tab before
|
||||||
|
aborting.
|
||||||
|
|
||||||
|
### Load Cell Command Extensions
|
||||||
|
Commands that perform probes, such as [`PROBE`](#probe),
|
||||||
|
[`PROBE_ACCURACY`](#probe_accuracy),
|
||||||
|
[`BED_MESH_CALIBRATE`](#bed_mesh_calibrate) etc. will accept additional
|
||||||
|
parameters if a `[load_cell_probe]` is defined. The parameters override the
|
||||||
|
corresponding settings from the
|
||||||
|
[`[load_cell_probe]`](./Config_Reference.md#load_cell_probe) configuration:
|
||||||
|
- `FORCE_SAFETY_LIMIT=<grams>`
|
||||||
|
- `TRIGGER_FORCE=<grams>`
|
||||||
|
- `DRIFT_FILTER_CUTOFF_FREQUENCY=<frequency_hz>`
|
||||||
|
- `DRIFT_FILTER_DELAY=<1|2>`
|
||||||
|
- `BUZZ_FILTER_CUTOFF_FREQUENCY=<frequency_hz>`
|
||||||
|
- `BUZZ_FILTER_DELAY=<1|2>`
|
||||||
|
- `NOTCH_FILTER_FREQUENCIES=<list of frequency_hz>`
|
||||||
|
- `NOTCH_FILTER_QUALITY=<quality>`
|
||||||
|
- `TARE_TIME=<seconds>`
|
||||||
|
|
||||||
### [manual_probe]
|
### [manual_probe]
|
||||||
|
|
||||||
The manual_probe module is automatically loaded.
|
The manual_probe module is automatically loaded.
|
||||||
@@ -787,6 +1004,25 @@ scheduled to run after the stepper move completes, however if a manual
|
|||||||
stepper move uses SYNC=0 then future G-Code movement commands may run
|
stepper move uses SYNC=0 then future G-Code movement commands may run
|
||||||
in parallel with the stepper movement.
|
in parallel with the stepper movement.
|
||||||
|
|
||||||
|
`MANUAL_STEPPER STEPPER=config_name GCODE_AXIS=[A-Z]
|
||||||
|
[LIMIT_VELOCITY=<velocity>] [LIMIT_ACCEL=<accel>]
|
||||||
|
[INSTANTANEOUS_CORNER_VELOCITY=<velocity>]`: If the `GCODE_AXIS`
|
||||||
|
parameter is specified then it configures the stepper motor as an
|
||||||
|
extra axis on `G1` move commands. For example, if one were to issue a
|
||||||
|
`MANUAL_STEPPER ... GCODE_AXIS=R` command then one could issue
|
||||||
|
commands like `G1 X10 Y20 R30` to move the stepper motor. The
|
||||||
|
resulting moves will occur synchronously with the associated toolhead
|
||||||
|
xyz movements. If the motor is associated with a `GCODE_AXIS` then
|
||||||
|
one may no longer issue movements using the above `MANUAL_STEPPER`
|
||||||
|
command - one may unregister the stepper with a `MANUAL_STEPPER
|
||||||
|
... GCODE_AXIS=` command to resume manual control of the motor. The
|
||||||
|
`LIMIT_VELOCITY` and `LIMIT_ACCEL` parameters allow one to reduce the
|
||||||
|
speed of `G1` moves if those moves would result in a velocity or
|
||||||
|
acceleration above the specified limits. The
|
||||||
|
`INSTANTANEOUS_CORNER_VELOCITY` specifies the maximum instantaneous
|
||||||
|
velocity change (in mm/s) of the motor during the junction of two
|
||||||
|
moves (the default is 1mm/s).
|
||||||
|
|
||||||
### [mcp4018]
|
### [mcp4018]
|
||||||
|
|
||||||
The following command is available when a
|
The following command is available when a
|
||||||
@@ -801,49 +1037,6 @@ be between 0.0 and 1.0, unless a 'scale' is defined in the config.
|
|||||||
When 'scale' is defined, then this value should be between 0.0 and
|
When 'scale' is defined, then this value should be between 0.0 and
|
||||||
'scale'.
|
'scale'.
|
||||||
|
|
||||||
### [led]
|
|
||||||
|
|
||||||
The following command is available when any of the
|
|
||||||
[led config sections](Config_Reference.md#leds) are enabled.
|
|
||||||
|
|
||||||
#### SET_LED
|
|
||||||
`SET_LED LED=<config_name> RED=<value> GREEN=<value> BLUE=<value>
|
|
||||||
WHITE=<value> [INDEX=<index>] [TRANSMIT=0] [SYNC=1]`: This sets the
|
|
||||||
LED output. Each color `<value>` must be between 0.0 and 1.0. The
|
|
||||||
WHITE option is only valid on RGBW LEDs. If the LED supports multiple
|
|
||||||
chips in a daisy-chain then one may specify INDEX to alter the color
|
|
||||||
of just the given chip (1 for the first chip, 2 for the second,
|
|
||||||
etc.). If INDEX is not provided then all LEDs in the daisy-chain will
|
|
||||||
be set to the provided color. If TRANSMIT=0 is specified then the
|
|
||||||
color change will only be made on the next SET_LED command that does
|
|
||||||
not specify TRANSMIT=0; this may be useful in combination with the
|
|
||||||
INDEX parameter to batch multiple updates in a daisy-chain. By
|
|
||||||
default, the SET_LED command will sync it's changes with other ongoing
|
|
||||||
gcode commands. This can lead to undesirable behavior if LEDs are
|
|
||||||
being set while the printer is not printing as it will reset the idle
|
|
||||||
timeout. If careful timing is not needed, the optional SYNC=0
|
|
||||||
parameter can be specified to apply the changes without resetting the
|
|
||||||
idle timeout.
|
|
||||||
|
|
||||||
#### SET_LED_TEMPLATE
|
|
||||||
`SET_LED_TEMPLATE LED=<led_name> TEMPLATE=<template_name>
|
|
||||||
[<param_x>=<literal>] [INDEX=<index>]`: Assign a
|
|
||||||
[display_template](Config_Reference.md#display_template) to a given
|
|
||||||
[LED](Config_Reference.md#leds). For example, if one defined a
|
|
||||||
`[display_template my_led_template]` config section then one could
|
|
||||||
assign `TEMPLATE=my_led_template` here. The display_template should
|
|
||||||
produce a comma separated string containing four floating point
|
|
||||||
numbers corresponding to red, green, blue, and white color settings.
|
|
||||||
The template will be continuously evaluated and the LED will be
|
|
||||||
automatically set to the resulting colors. One may set
|
|
||||||
display_template parameters to use during template evaluation
|
|
||||||
(parameters will be parsed as Python literals). If INDEX is not
|
|
||||||
specified then all chips in the LED's daisy-chain will be set to the
|
|
||||||
template, otherwise only the chip with the given index will be
|
|
||||||
updated. If TEMPLATE is an empty string then this command will clear
|
|
||||||
any previous template assigned to the LED (one can then use `SET_LED`
|
|
||||||
commands to manage the LED's color settings).
|
|
||||||
|
|
||||||
### [output_pin]
|
### [output_pin]
|
||||||
|
|
||||||
The following command is available when an
|
The following command is available when an
|
||||||
@@ -857,6 +1050,20 @@ 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
|
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.
|
`scale` if a scale is configured 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]
|
### [palette2]
|
||||||
|
|
||||||
The following commands are available when the
|
The following commands are available when the
|
||||||
@@ -892,20 +1099,6 @@ Palette 2 once the loading has been completed. This command is the
|
|||||||
same as pressing **Smart Load** directly on the Palette 2 screen after
|
same as pressing **Smart Load** directly on the Palette 2 screen after
|
||||||
the filament load is complete.
|
the filament load is complete.
|
||||||
|
|
||||||
### [pid_calibrate]
|
|
||||||
|
|
||||||
The pid_calibrate module is automatically loaded if a heater is defined
|
|
||||||
in the config file.
|
|
||||||
|
|
||||||
#### PID_CALIBRATE
|
|
||||||
`PID_CALIBRATE HEATER=<config_name> TARGET=<temperature>
|
|
||||||
[WRITE_FILE=1]`: Perform a PID calibration test. The specified heater
|
|
||||||
will be enabled until the specified target temperature is reached, and
|
|
||||||
then the heater will be turned off and on for several cycles. If the
|
|
||||||
WRITE_FILE parameter is enabled, then the file /tmp/heattest.txt will
|
|
||||||
be created with a log of all temperature samples taken during the
|
|
||||||
test.
|
|
||||||
|
|
||||||
### [pause_resume]
|
### [pause_resume]
|
||||||
|
|
||||||
The following commands are available when the
|
The following commands are available when the
|
||||||
@@ -931,6 +1124,20 @@ the paused state is fresh for each print.
|
|||||||
#### CANCEL_PRINT
|
#### CANCEL_PRINT
|
||||||
`CANCEL_PRINT`: Cancels the current print.
|
`CANCEL_PRINT`: Cancels the current print.
|
||||||
|
|
||||||
|
### [pid_calibrate]
|
||||||
|
|
||||||
|
The pid_calibrate module is automatically loaded if a heater is defined
|
||||||
|
in the config file.
|
||||||
|
|
||||||
|
#### PID_CALIBRATE
|
||||||
|
`PID_CALIBRATE HEATER=<config_name> TARGET=<temperature>
|
||||||
|
[WRITE_FILE=1]`: Perform a PID calibration test. The specified heater
|
||||||
|
will be enabled until the specified target temperature is reached, and
|
||||||
|
then the heater will be turned off and on for several cycles. If the
|
||||||
|
WRITE_FILE parameter is enabled, then the file /tmp/heattest.txt will
|
||||||
|
be created with a log of all temperature samples taken during the
|
||||||
|
test.
|
||||||
|
|
||||||
### [print_stats]
|
### [print_stats]
|
||||||
|
|
||||||
The print_stats module is automatically loaded.
|
The print_stats module is automatically loaded.
|
||||||
@@ -1021,6 +1228,21 @@ CYCLE_TIME parameter is not stored between SET_PIN commands (any
|
|||||||
SET_PIN command without an explicit CYCLE_TIME parameter will use the
|
SET_PIN command without an explicit CYCLE_TIME parameter will use the
|
||||||
`cycle_time` specified in the pwm_cycle_time config section).
|
`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]
|
### [query_adc]
|
||||||
|
|
||||||
The query_adc module is automatically loaded.
|
The query_adc module is automatically loaded.
|
||||||
@@ -1056,20 +1278,19 @@ is enabled (also see the
|
|||||||
all enabled accelerometer chips.
|
all enabled accelerometer chips.
|
||||||
|
|
||||||
#### TEST_RESONANCES
|
#### TEST_RESONANCES
|
||||||
`TEST_RESONANCES AXIS=<axis> OUTPUT=<resonances,raw_data>
|
`TEST_RESONANCES AXIS=<axis> [OUTPUT=<resonances,raw_data>]
|
||||||
[NAME=<name>] [FREQ_START=<min_freq>] [FREQ_END=<max_freq>]
|
[NAME=<name>] [FREQ_START=<min_freq>] [FREQ_END=<max_freq>]
|
||||||
[HZ_PER_SEC=<hz_per_sec>] [CHIPS=<adxl345_chip_name>]
|
[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
|
[POINT=x,y,z] [INPUT_SHAPING=<0:1>]`: Runs the resonance
|
||||||
test in all configured probe points for the requested "axis" and
|
test in all configured probe points for the requested "axis" and
|
||||||
measures the acceleration using the accelerometer chips configured for
|
measures the acceleration using the accelerometer chips configured for
|
||||||
the respective axis. "axis" can either be X or Y, or specify an
|
the respective axis. "axis" can either be X or Y, or specify an
|
||||||
arbitrary direction as `AXIS=dx,dy`, where dx and dy are floating
|
arbitrary direction as `AXIS=dx,dy`, where dx and dy are floating
|
||||||
point numbers defining a direction vector (e.g. `AXIS=X`, `AXIS=Y`, or
|
point numbers defining a direction vector (e.g. `AXIS=X`, `AXIS=Y`, or
|
||||||
`AXIS=1,-1` to define a diagonal direction). Note that `AXIS=dx,dy`
|
`AXIS=1,-1` to define a diagonal direction). Note that `AXIS=dx,dy`
|
||||||
and `AXIS=-dx,-dy` is equivalent. `adxl345_chip_name` can be one or
|
and `AXIS=-dx,-dy` is equivalent. `chip_name` can be one or
|
||||||
more configured adxl345 chip,delimited with comma, for example
|
more configured accel chips, delimited with comma, for example
|
||||||
`CHIPS="adxl345, adxl345 rpi"`. Note that `adxl345` can be omitted from
|
`CHIPS="adxl345, adxl345 rpi"`. If POINT is specified it will override the point(s)
|
||||||
named adxl345 chips. If POINT is specified it will override the point(s)
|
|
||||||
configured in `[resonance_tester]`. If `INPUT_SHAPING=0` or not set(default),
|
configured in `[resonance_tester]`. If `INPUT_SHAPING=0` or not set(default),
|
||||||
disables input shaping for the resonance testing, because
|
disables input shaping for the resonance testing, because
|
||||||
it is not valid to run the resonance testing with the input shaper
|
it is not valid to run the resonance testing with the input shaper
|
||||||
@@ -1086,8 +1307,9 @@ frequency response is calculated (across all probe points) and written into
|
|||||||
|
|
||||||
#### SHAPER_CALIBRATE
|
#### SHAPER_CALIBRATE
|
||||||
`SHAPER_CALIBRATE [AXIS=<axis>] [NAME=<name>] [FREQ_START=<min_freq>]
|
`SHAPER_CALIBRATE [AXIS=<axis>] [NAME=<name>] [FREQ_START=<min_freq>]
|
||||||
[FREQ_END=<max_freq>] [HZ_PER_SEC=<hz_per_sec>] [CHIPS=<adxl345_chip_name>]
|
[FREQ_END=<max_freq>] [ACCEL_PER_HZ=<accel_per_hz>][HZ_PER_SEC=<hz_per_sec>]
|
||||||
[MAX_SMOOTHING=<max_smoothing>]`: Similarly to `TEST_RESONANCES`, runs
|
[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
|
the resonance test as configured, and tries to find the optimal
|
||||||
parameters for the input shaper for the requested axis (or both X and
|
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
|
Y axes if `AXIS` parameter is unset). If `MAX_SMOOTHING` is unset, its
|
||||||
@@ -1137,8 +1359,9 @@ has been enabled.
|
|||||||
|
|
||||||
#### SAVE_VARIABLE
|
#### SAVE_VARIABLE
|
||||||
`SAVE_VARIABLE VARIABLE=<name> VALUE=<value>`: Saves the variable to
|
`SAVE_VARIABLE VARIABLE=<name> VALUE=<value>`: Saves the variable to
|
||||||
disk so that it can be used across restarts. All stored variables are
|
disk so that it can be used across restarts. The VARIABLE must be lowercase.
|
||||||
loaded into the `printer.save_variables.variables` dict at startup and
|
All stored variables are loaded into the
|
||||||
|
`printer.save_variables.variables` dict at startup and
|
||||||
can be used in gcode macros. The provided VALUE is parsed as a Python
|
can be used in gcode macros. The provided VALUE is parsed as a Python
|
||||||
literal.
|
literal.
|
||||||
|
|
||||||
@@ -1282,6 +1505,42 @@ temperature_fan. If a target is not supplied, it is set to the
|
|||||||
specified temperature in the config file. If speeds are not supplied,
|
specified temperature in the config file. If speeds are not supplied,
|
||||||
no change is applied.
|
no change is applied.
|
||||||
|
|
||||||
|
### [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.
|
||||||
|
|
||||||
### [tmcXXXX]
|
### [tmcXXXX]
|
||||||
|
|
||||||
The following commands are available when any of the
|
The following commands are available when any of the
|
||||||
@@ -1410,44 +1669,10 @@ The following commands are available when the
|
|||||||
[z_tilt config section](Config_Reference.md#z_tilt) is enabled.
|
[z_tilt config section](Config_Reference.md#z_tilt) is enabled.
|
||||||
|
|
||||||
#### Z_TILT_ADJUST
|
#### Z_TILT_ADJUST
|
||||||
`Z_TILT_ADJUST [HORIZONTAL_MOVE_Z=<value>] [<probe_parameter>=<value>]`: This
|
`Z_TILT_ADJUST [RETRIES=<value>] [RETRY_TOLERANCE=<value>]
|
||||||
command will probe the points specified in the config and then make independent
|
[HORIZONTAL_MOVE_Z=<value>] [<probe_parameter>=<value>]`: This command
|
||||||
adjustments to each Z stepper to compensate for tilt. See the PROBE command for
|
will probe the points specified in the config and then make
|
||||||
details on the optional probe parameters. The optional `HORIZONTAL_MOVE_Z`
|
independent adjustments to each Z stepper to compensate for tilt. See
|
||||||
value overrides the `horizontal_move_z` option specified in the config file.
|
the PROBE command for details on the optional probe parameters. The
|
||||||
|
optional `RETRIES`, `RETRY_TOLERANCE`, and `HORIZONTAL_MOVE_Z` values
|
||||||
### [temperature_probe]
|
override those options specified in the config file.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
These instructions assume the software will run on a Raspberry Pi
|
These instructions assume the software will run on a Linux-based host
|
||||||
computer in conjunction with OctoPrint. It is recommended that a
|
running a Klipper-compatible front end. It is recommended that a
|
||||||
Raspberry Pi 2 (or later) be used as the host machine (see the
|
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)
|
[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 printer board. SBC relates to the term Small Board Computer
|
||||||
|
such as the Raspberry Pi.
|
||||||
|
|
||||||
## Obtain a Klipper Configuration File
|
## Obtain a Klipper Configuration File
|
||||||
|
|
||||||
Most Klipper settings are determined by a "printer 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
|
file can often be found by looking in the Klipper
|
||||||
[config directory](../config/) for a file starting with a "printer-"
|
[config directory](../config/) for a file starting with a "printer-"
|
||||||
prefix that corresponds to the target printer. The Klipper
|
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 file](../config/) and use the Klipper
|
||||||
[config reference](Config_Reference.md) for further information.
|
[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
|
Klipper is a 3d printer firmware, so it needs some way for the user to
|
||||||
Raspberry Pi computer. Use OctoPi v0.17.0 or later - see the
|
interact with it.
|
||||||
[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.
|
|
||||||
|
|
||||||
After installing OctoPi and upgrading OctoPrint, it will be necessary
|
Currently the best choices are front ends that retrieve information through
|
||||||
to ssh into the target machine to run a handful of system commands. If
|
the [Moonraker web API](https://moonraker.readthedocs.io/) and there is also
|
||||||
using a Linux or MacOS desktop, then the "ssh" software should already
|
the option to use [Octoprint](https://octoprint.org/) to control Klipper.
|
||||||
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:
|
|
||||||
|
|
||||||
```
|
The choice is up to the user on what to use, but the underlying Klipper is the
|
||||||
git clone https://github.com/Klipper3d/klipper
|
same in all cases. We encourage users to research the options available and
|
||||||
./klipper/scripts/install-octopi.sh
|
make an informed decision.
|
||||||
```
|
|
||||||
|
|
||||||
The above will download Klipper, install some system dependencies,
|
## Obtaining an OS image for SBC's
|
||||||
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
|
There are many ways to obtain an OS image for Klipper for SBC use, most depend on
|
||||||
minutes to complete.
|
what front end you wish to use. Some manufacturers 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"](https://docs-os.mainsail.xyz/), this has the option for Raspberry Pi
|
||||||
|
and some OrangePi variants.
|
||||||
|
|
||||||
|
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 an x86 Linux device, Ubuntu Server. Please note that Desktop
|
||||||
|
variants are not recommended due to certain helper programs that can stop some
|
||||||
|
Klipper functions from working and even mask access to some printer 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
|
## Building and flashing the micro-controller
|
||||||
|
|
||||||
To compile the micro-controller code, start by running these commands
|
To compile the micro-controller code, start by running these commands
|
||||||
on the Raspberry Pi:
|
on your host device:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd ~/klipper/
|
cd ~/klipper/
|
||||||
@@ -88,7 +106,7 @@ make
|
|||||||
If the comments at the top of the
|
If the comments at the top of the
|
||||||
[printer configuration file](#obtain-a-klipper-configuration-file)
|
[printer configuration file](#obtain-a-klipper-configuration-file)
|
||||||
describe custom steps for "flashing" the final image to the printer
|
describe custom steps for "flashing" the final image to the printer
|
||||||
control board then follow those steps and then proceed to
|
control board, then follow those steps and then proceed to
|
||||||
[configuring OctoPrint](#configuring-octoprint-to-use-klipper).
|
[configuring OctoPrint](#configuring-octoprint-to-use-klipper).
|
||||||
|
|
||||||
Otherwise, the following steps are often used to "flash" the printer
|
Otherwise, the following steps are often used to "flash" the printer
|
||||||
@@ -108,10 +126,40 @@ It should report something similar to the following:
|
|||||||
It's common for each printer to have its own unique serial port name.
|
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
|
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,
|
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).
|
[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
|
||||||
|
occurring.
|
||||||
|
|
||||||
|
Please note, that most print boards that use SD cards for flash will
|
||||||
|
implement some kind of flash loop protection for when the sd card is left
|
||||||
|
in place. There are two common methods:
|
||||||
|
|
||||||
|
Filename Change Required (usually "stock" print boards):
|
||||||
|
|
||||||
|
These boards require the firmware file to have a different name each
|
||||||
|
time you flash (for example, firmware1.bin, firmware2.bin, etc.).
|
||||||
|
If you reuse the same filename, the board may ignore it and not update.
|
||||||
|
|
||||||
|
Automatic File Renaming (usually aftermarket print boards:
|
||||||
|
|
||||||
|
Other boards allow using the same filename, commonly firmware.bin,
|
||||||
|
but after flashing, the board renames the file to firmware.cur.
|
||||||
|
This helps indicate the firmware was successfully flashed and prevents
|
||||||
|
it from flashing again on the next startup.
|
||||||
|
|
||||||
|
Before flashing, make sure to check which behavior your board follows.
|
||||||
|
|
||||||
|
For common micro-controllers using Atmega chips, for example the 2560,
|
||||||
|
the code can be flashed with something
|
||||||
similar to:
|
similar to:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -123,53 +171,38 @@ sudo service klipper start
|
|||||||
Be sure to update the FLASH_DEVICE with the printer's unique serial
|
Be sure to update the FLASH_DEVICE with the printer's unique serial
|
||||||
port name.
|
port name.
|
||||||
|
|
||||||
When flashing for the first time, make sure that OctoPrint is not
|
For common micro-controllers using RP2040 chips, the code can be flashed
|
||||||
connected directly to the printer (from the OctoPrint web page, under
|
with something similar to:
|
||||||
the "Connection" section, click "Disconnect").
|
|
||||||
|
|
||||||
## 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
|
It is important to note that RP2040 chips may need to be put into Boot mode
|
||||||
the Klipper host software. Using a web browser, login to the OctoPrint
|
before this operation.
|
||||||
web page and then configure the following items:
|
|
||||||
|
|
||||||
Navigate to the Settings tab (the wrench icon at the top of the
|
|
||||||
page). Under "Serial Connection" in "Additional serial ports" add
|
|
||||||
`/tmp/printer`. Then click "Save".
|
|
||||||
|
|
||||||
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
|
## Configuring Klipper
|
||||||
|
|
||||||
The next step is to copy the
|
The next step is to copy the
|
||||||
[printer configuration file](#obtain-a-klipper-configuration-file) to
|
[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
|
Arguably the easiest way to set the Klipper configuration file is using the
|
||||||
use a desktop editor that supports editing files over the "scp" and/or
|
built-in editors in Mainsail or Fluidd. These will allow the user to open
|
||||||
"sftp" protocols. There are freely available tools that support this
|
the configuration examples and save them to be printer.cfg.
|
||||||
(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
|
Another option is to use a desktop editor that supports editing files
|
||||||
home directory of the pi user (ie, `/home/pi/printer.cfg`).
|
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
|
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
|
sure to update the command to use the appropriate printer config
|
||||||
filename):
|
filename):
|
||||||
|
|
||||||
@@ -200,9 +233,9 @@ the `[mcu]` section to look something similar to:
|
|||||||
serial: /dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0
|
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
|
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
|
"status" command will report that the printer is ready if the Klipper
|
||||||
config file is successfully read and the micro-controller is
|
config file is successfully read and the micro-controller is
|
||||||
successfully found and configured.
|
successfully found and configured.
|
||||||
|
|
||||||
@@ -211,10 +244,10 @@ Klipper to report a configuration error. If an error occurs, make any
|
|||||||
necessary corrections to the printer config file and issue "restart"
|
necessary corrections to the printer config file and issue "restart"
|
||||||
until "status" reports the printer is ready.
|
until "status" reports the printer is ready.
|
||||||
|
|
||||||
Klipper reports error messages via the OctoPrint terminal tab. The
|
Klipper reports error messages via the command console and pop-ups in
|
||||||
"status" command can be used to re-report error messages. The default
|
Fluidd and Mainsail. The "status" command can be used to re-report error
|
||||||
Klipper startup script also places a log in **/tmp/klippy.log** which
|
messages. A log is available and usually located at
|
||||||
provides more detailed information.
|
`~/printer_data/logs/klippy.log`.
|
||||||
|
|
||||||
After Klipper reports that the printer is ready, proceed to the
|
After Klipper reports that the printer is ready, proceed to the
|
||||||
[config check document](Config_checks.md) to perform some basic checks
|
[config check document](Config_checks.md) to perform some basic checks
|
||||||
|
|||||||
489
docs/Load_Cell.md
Normal file
489
docs/Load_Cell.md
Normal file
@@ -0,0 +1,489 @@
|
|||||||
|
# Load Cells
|
||||||
|
|
||||||
|
This document describes Klipper's support for load cells. Basic load cell
|
||||||
|
functionality can be used to read force data and to weigh things like filament.
|
||||||
|
A calibrated force sensor is an important part of a load cell based probe.
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
* [load_cell Config Reference](Config_Reference.md#load_cell)
|
||||||
|
* [load_cell G-Code Commands](G-Codes.md#load_cell)
|
||||||
|
* [load_cell Status Reference](Status_Reference.md#load_cell)
|
||||||
|
|
||||||
|
## Using `LOAD_CELL_DIAGNOSTIC`
|
||||||
|
|
||||||
|
When you first connect a load cell its good practice to check for issues by
|
||||||
|
running `LOAD_CELL_DIAGNOSTIC`. This tool collects 10 seconds of data from the
|
||||||
|
load cell and resport statistics:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ LOAD_CELL_DIAGNOSTIC
|
||||||
|
// Collecting load cell data for 10 seconds...
|
||||||
|
// Samples Collected: 3211
|
||||||
|
// Measured samples per second: 332.0
|
||||||
|
// Good samples: 3211, Saturated samples: 0, Unique values: 900
|
||||||
|
// Sample range: [4.01% to 4.02%]
|
||||||
|
// Sample range / sensor capacity: 0.00524%
|
||||||
|
```
|
||||||
|
|
||||||
|
Things you can check with this data:
|
||||||
|
|
||||||
|
* The configured sample rate of the sensor should be close to the 'Measured
|
||||||
|
samples per second' value. If it is not you may have a configuration or wiring
|
||||||
|
issue.
|
||||||
|
* 'Saturated samples' should be 0. If you have saturated samples it means the
|
||||||
|
load sell is seeing more force than it can measure.
|
||||||
|
* 'Unique values' should be a large percentage of the 'Samples
|
||||||
|
Collected' value. If 'Unique values' is 1 it is very likely a wiring issue.
|
||||||
|
* Tap or push on the sensor while `LOAD_CELL_DIAGNOSTIC` runs. If
|
||||||
|
things are working correctly this should increase the 'Sample range'.
|
||||||
|
|
||||||
|
## Calibrating a Load Cell
|
||||||
|
|
||||||
|
Load cells are calibrated using the `LOAD_CELL_CALIBRATE` command. This is an
|
||||||
|
interactive calibration utility that walks you though a 3 step process:
|
||||||
|
|
||||||
|
1. First use the `TARE` command to establish the zero force value. This is the
|
||||||
|
`reference_tare_counts` config value.
|
||||||
|
2. Next you apply a known load or force to the load cell and run the
|
||||||
|
`CALIBRATE GRAMS=nnn` command. From this the `counts_per_gram` value is
|
||||||
|
calculated. See [the next section](#applying-a-known-force-or-load) for some
|
||||||
|
suggestions on how to do this.
|
||||||
|
3. Finally, use the `ACCEPT` command to save the results.
|
||||||
|
|
||||||
|
You can cancel the calibration process at any time with `ABORT`.
|
||||||
|
|
||||||
|
### Applying a Known Force or Load
|
||||||
|
|
||||||
|
The `CALIBRATE GRAMS=nnn` step can be accomplished in a number of ways. If your
|
||||||
|
load cell is under a platform like a bed or filament holder it might be easiest
|
||||||
|
to put a known mass on the platform. E.g. you could use a couple of 1KG filament
|
||||||
|
spools.
|
||||||
|
|
||||||
|
If your load cell is in the printer's toolhead a different approach is easier.
|
||||||
|
Put a digital scale on the printers bed and gently lower the toolhead onto the
|
||||||
|
scale (or raise the bed into the toolhead if your bed moves). You may be able to
|
||||||
|
do this using the `FORCE_MOVE` command. But more likely you will have to
|
||||||
|
manually moving the z axis with the motors off until the toolhead presses on the
|
||||||
|
scale.
|
||||||
|
|
||||||
|
A good calibration force would ideally be a large percentage of the load cell's
|
||||||
|
rated capacity. E.g. if you have a 5Kg load cell you would ideally calibrate it
|
||||||
|
with a 5kg mass. This might work well with under-bed sensors that have to
|
||||||
|
support a lot of weight. For toolhead probes this may not be a load that your
|
||||||
|
printer bed or toolhead can tolerate without damage. Do try to use at least 1Kg
|
||||||
|
of force, most printers should tolerate this without issue.
|
||||||
|
|
||||||
|
When calibrating make careful note of the values reported:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ CALIBRATE GRAMS=555
|
||||||
|
// Calibration value: -2.78% (-59803108), Counts/gram: 73039.78739,
|
||||||
|
Total capacity: +/- 29.14Kg
|
||||||
|
```
|
||||||
|
|
||||||
|
The `Total capacity` should be close to the theoretical rating of the load cell
|
||||||
|
based on the sensor's capacity. If it is much larger you could have used a
|
||||||
|
higher gain setting in the sensor or a more sensitive load cell. This isn't as
|
||||||
|
critical for 32bit and 24bit sensors but is much more critical for low bit width
|
||||||
|
sensors.
|
||||||
|
|
||||||
|
## Reading Force Data
|
||||||
|
Force data can be read with a GCode command:
|
||||||
|
|
||||||
|
```
|
||||||
|
LOAD_CELL_READ
|
||||||
|
// 10.6g (1.94%)
|
||||||
|
```
|
||||||
|
|
||||||
|
Data is also continuously read and can be consumed from the load_cell printer
|
||||||
|
object in a macro:
|
||||||
|
|
||||||
|
```
|
||||||
|
{% set grams = printer.load_cell.force_g %}
|
||||||
|
```
|
||||||
|
|
||||||
|
This provides an average force over the last 1 second, similar to how
|
||||||
|
temperature sensors work.
|
||||||
|
|
||||||
|
## Taring a Load Cell
|
||||||
|
Taring, sometimes called zeroing, sets the current weight reported by the
|
||||||
|
load_cell to 0. This is useful for measuring relative to a known weight. e.g.
|
||||||
|
when measuring a filament spool, using `LOAD_CELL_TARE` sets the weight to 0.
|
||||||
|
Then as filament is printed the load_cell will report the weight of the
|
||||||
|
filament used.
|
||||||
|
|
||||||
|
```
|
||||||
|
LOAD_CELL_TARE
|
||||||
|
// Load cell tare value: 5.32% (445903)
|
||||||
|
```
|
||||||
|
|
||||||
|
The current tare value is reported in the printers status and can be read in
|
||||||
|
a macro:
|
||||||
|
|
||||||
|
```
|
||||||
|
{% set tare_counts = printer.load_cell.tare_counts %}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Load Cell Probes
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
* [load_cell_probe Config Reference](Config_Reference.md#load_cell_probe)
|
||||||
|
* [load_cell_probe G-Code Commands](G-Codes.md#load_cell_probe)
|
||||||
|
* [load_cell_probe Statuc Reference](Status_Reference.md#load_cell_probe)
|
||||||
|
|
||||||
|
## Load Cell Probe Safety
|
||||||
|
|
||||||
|
Because load cells are a direct nozzle contact probe there is a risk of
|
||||||
|
damage to your printer if too much force is used. The load cell probing system
|
||||||
|
includes a number of safety checks that try to keep your machine safe from
|
||||||
|
excessive force to the toolhead. It's important to understand what they are
|
||||||
|
and how they work as you can defeat most of them with poorly chosen config
|
||||||
|
values.
|
||||||
|
|
||||||
|
#### Calibration Check
|
||||||
|
Every time a homing move starts, load_cell_probe checks
|
||||||
|
that the load_cell is calibrated. If not it will stop the move with an error:
|
||||||
|
`!! Load Cell not calibrated`.
|
||||||
|
|
||||||
|
#### `counts_per_gram`
|
||||||
|
This setting is used to convert raw sensor counts into grams. All the safety
|
||||||
|
limits are in gram units for your convenience. If the `counts_per_gram`
|
||||||
|
setting is not accurate you can easily exceed the safe force on the toolhead.
|
||||||
|
You should never guess this value. Use `LOAD_CELL_CALIBRATE` to find your load
|
||||||
|
cells actual `counts_per_gram`.
|
||||||
|
|
||||||
|
#### `trigger_force`
|
||||||
|
This is the force in grams that triggers the endstop to halt the homing move.
|
||||||
|
When a homing move starts the endstop tares itself with the current reading
|
||||||
|
from the load cell. `trigger_force` is measured from that tare value. There is
|
||||||
|
always some overshoot of this value when the probe collides with the bed,
|
||||||
|
so be conservative. e.g. a setting of 100g could result in 350g of peak force
|
||||||
|
before the toolhead stops. This overshoot will increase with faster probing
|
||||||
|
`speed`, a low ADC sample rate or [multi MCU homing](Multi_MCU_Homing.md).
|
||||||
|
|
||||||
|
#### `reference_tare_counts`
|
||||||
|
This is the baseline tare value that is set by `LOAD_CELL_CALIBRATE`.
|
||||||
|
This value works with `force_safety_limit` to limit the maximum force on the
|
||||||
|
toolhead.
|
||||||
|
|
||||||
|
#### `force_safety_limit`
|
||||||
|
This is the maximum absolute force, relative to `reference_tare_counts`,
|
||||||
|
that the probe will allow while homing or probing. If the MCU sees this
|
||||||
|
force exceeded it will shut down the printer with the error `!! Load cell
|
||||||
|
endstop: too much force!`. There are a number of ways this can be triggered:
|
||||||
|
|
||||||
|
The first risk this protects against is picking too large of a value for
|
||||||
|
`drift_filter_cutoff_frequency`. This can cause the drift filter to filter out
|
||||||
|
a probe event and continue the homing move. If this happens the
|
||||||
|
`force_safety_limit` acts as a backup protection.
|
||||||
|
|
||||||
|
The second problem is probing repeatedly in one place. Klipper does not retract
|
||||||
|
the probe when doing a single `PROBE` command. This can result
|
||||||
|
in force applied to the toolhead at the end of a probing cycle. Because
|
||||||
|
external forces can vary greatly between probing locations,
|
||||||
|
`load_cell_probe` performs a tare before beginning each probe. If you repeat
|
||||||
|
the `PROBE` command, load_cell_probe will tare the endstop at the current force.
|
||||||
|
Multiple cycles of this will result in ever-increasing force on the toolhead.
|
||||||
|
`force_safety_limit` stops this cycle from running out of control.
|
||||||
|
|
||||||
|
Another way this run-away can happen is damage to a strain gauge. If the metal
|
||||||
|
part is permanently bent it will change the `reference_tare_counts` of the
|
||||||
|
device. This puts the starting tare value much closer to the limit making it
|
||||||
|
more likely to be violated. You want to be notified if this is happening
|
||||||
|
because your hardware has been permanently damaged.
|
||||||
|
|
||||||
|
The final way this can be triggered is due to temperature changes. If your
|
||||||
|
strain gauges are heated their `reference_tare_counts` may be very different
|
||||||
|
at ambient temperature vs operating temperature. In this case you may need
|
||||||
|
to increase the `force_safety_limit` to allow for thermal changes.
|
||||||
|
|
||||||
|
#### Load Cell Endstop Watchdog Task
|
||||||
|
When homing the load_cell_endstop starts a task on the MCU to trac
|
||||||
|
measurements arriving from the sensor. If the sensor fails to send
|
||||||
|
measurements for 2 sample periods the watchdog will shut down the printer
|
||||||
|
with an error `!! LoadCell Endstop timed out waiting on ADC data`.
|
||||||
|
|
||||||
|
If this happens, the most likely cause is a fault from the ADC. Inadequate
|
||||||
|
grounding of your printer can be the root cause. The frame, power supply
|
||||||
|
case and pint bed should all be connected to ground. You may need to ground
|
||||||
|
the frame in multiple places. Anodized aluminum extrusions do not conduct
|
||||||
|
electricity well. You might need to sand the area where the grounding wire
|
||||||
|
is attached to make good electrical contact.
|
||||||
|
|
||||||
|
## Load Cell Probe Setup
|
||||||
|
|
||||||
|
This section covers the process for commissioning a load cell probe.
|
||||||
|
|
||||||
|
### Verify the Load Cell First
|
||||||
|
|
||||||
|
A `[load_cell_probe]` is also a `[load_cell]` and G-code commands related to
|
||||||
|
`[load_cell]` work with `[load_cell_probe]`. Before attempting to use a load
|
||||||
|
cell probe, follow the directions for
|
||||||
|
[calibrating the load cell](Load_Cell.md#calibrating-a-load-cell) with
|
||||||
|
`CALIBRATE_LOAD_CELL` and checking its operation with `LOAD_CELL_DIAGNOSTIC`.
|
||||||
|
|
||||||
|
### Verify Probe Operation With LOAD_CELL_TEST_TAP
|
||||||
|
|
||||||
|
Use the command `LOAD_CELL_TEST_TAP` to test the operation of the load cell
|
||||||
|
probe before actually trying to probe with it. This command detects taps,
|
||||||
|
just like the PROBE command, but it does not move the z axis. By default, it
|
||||||
|
listens for 3 taps before ending the test. You have 30 seconds to do each
|
||||||
|
tap, if no taps are detected the command will time out.
|
||||||
|
|
||||||
|
If this test fails, check your configuration and `LOAD_CELL_DIAGNOSTIC`
|
||||||
|
carefully to look for issues.
|
||||||
|
|
||||||
|
Load cell probes don't support the `QUERY_ENDSTOPS` or `QUERY_PROBE`
|
||||||
|
commands. Use `LOAD_CELL_TEST_TAP` for testing functionality before probing.
|
||||||
|
|
||||||
|
### Homing Macros
|
||||||
|
|
||||||
|
Load cell probe is not an endstop and doesn't support `endstop:
|
||||||
|
prove:z_virtual_endstop`. For the time being you'll need to configure your z
|
||||||
|
axis with an MCU pin as its endstop. You won't actually be using the pin but
|
||||||
|
for the time being you have to configure something.
|
||||||
|
|
||||||
|
To home the axis with just the probe you need to set up a custom homing
|
||||||
|
macro. This requires setting up
|
||||||
|
[homing_override](Config_Reference.md#homing_override).
|
||||||
|
|
||||||
|
Here is a simple macro that can accomplish this. Note that the
|
||||||
|
`_HOME_Z_FROM_LAST_PROBE` macro has to be separate because of the way macros
|
||||||
|
work. The sub-call is needed so that the `_HOME_Z_FROM_LAST_PROBE` macro can
|
||||||
|
see the result of the probe in `printer.probe.last_z_result`.
|
||||||
|
|
||||||
|
```gcode
|
||||||
|
[gcode_macro _HOME_Z_FROM_LAST_PROBE]
|
||||||
|
gcode:
|
||||||
|
{% set z_probed = printer.probe.last_z_result %}
|
||||||
|
{% set z_position = printer.toolhead.position[2] %}
|
||||||
|
{% set z_actual = z_position - z_probed %}
|
||||||
|
SET_KINEMATIC_POSITION Z={z_actual}
|
||||||
|
|
||||||
|
[gcode_macro _HOME_Z]
|
||||||
|
gcode:
|
||||||
|
SET_GCODE_OFFSET Z=0 # load cell probes dont need a Z offset
|
||||||
|
# position toolhead for homing Z, edit for your printers size
|
||||||
|
#G90 # absolute move
|
||||||
|
#G1 Y50 X50 F{5 * 60} # move to X/Y position for homing
|
||||||
|
|
||||||
|
# soft home the z axis to its limit so it can be moved:
|
||||||
|
SET_KINEMATIC_POSITION Z={printer.toolhead.axis_maximum[2]}
|
||||||
|
|
||||||
|
# Fast approach and tap
|
||||||
|
PROBE PROBE_SPEED={5 * 60} # override the speed for faster homing
|
||||||
|
_HOME_Z_FROM_LAST_PROBE
|
||||||
|
|
||||||
|
# lift z to 2mm
|
||||||
|
G91 # relative move
|
||||||
|
G1 Z2 F{5 * 60}
|
||||||
|
|
||||||
|
# probe at standard speed
|
||||||
|
PROBE
|
||||||
|
_HOME_Z_FROM_LAST_PROBE
|
||||||
|
|
||||||
|
# lift z to 10mm for clearance
|
||||||
|
G91 # relative move
|
||||||
|
G1 Z10 F{5 * 60}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Suggested Probing Temperature
|
||||||
|
|
||||||
|
Currently, we suggest keeping the nozzle temperature below the level that causes
|
||||||
|
the filament to ooze while homing and probing. 140C is a good starting
|
||||||
|
point. This temperature is also low enough not to scar PEI build surfaces.
|
||||||
|
|
||||||
|
Fouling of the nozzle and the print bed due to oozing filament is the #1 source
|
||||||
|
of probing error with the load cell probe. Klipper does not yet have a universal
|
||||||
|
way to detect poor quality taps due to filament ooze. The existing code may
|
||||||
|
decide that a tap is valid when it is of poor quality. Classifying these poor
|
||||||
|
quality taps is an area of active research.
|
||||||
|
|
||||||
|
Klipper also lacks support for re-locating a probe point if the
|
||||||
|
location has become fouled by filament ooze. Modules like `quad_gantry_level`
|
||||||
|
will repeatedly probe the same coordinates even if a probe previously failed
|
||||||
|
there.
|
||||||
|
|
||||||
|
Give the above it is strongly suggested not to probe at printing temperatures.
|
||||||
|
|
||||||
|
### Hot Nozzle Protection
|
||||||
|
|
||||||
|
The Voron project has a great macro for protecting your print surface from the
|
||||||
|
hot nozzle. See [Voron Tap's
|
||||||
|
`activate_gcode`](https://github.com/VoronDesign/Voron-Tap/blob/main/config/tap_klipper_instructions.md)
|
||||||
|
|
||||||
|
It is highly suggested to add something like this to your config.
|
||||||
|
|
||||||
|
### Nozzle Cleaning
|
||||||
|
|
||||||
|
Before probing the nozzle should be clean. You could do this manually before
|
||||||
|
every print. You can also implement a nozzle scrubber and automate the process.
|
||||||
|
Here is a suggested sequence:
|
||||||
|
|
||||||
|
1. Wait for the nozzle to heat up to probing temp (e.g. `M109 S140`)
|
||||||
|
1. Home the machine (`G28`)
|
||||||
|
1. Scrub the nozzle on a brush
|
||||||
|
1. Heat soak the print bed
|
||||||
|
1. Perform probing tasks: QGL, bed mesh etc.
|
||||||
|
|
||||||
|
### Temperature Compensation for Nozzle Growth
|
||||||
|
|
||||||
|
If you are probing at a safe temperature, the nozzle will expand after
|
||||||
|
heating to printing temperatures. This will cause the nozzle to get longer
|
||||||
|
and closer to the print surface. You can compensate for this with
|
||||||
|
[[z_thermal_adjust]](Config_Reference.md#z_thermal_adjust). This adjustment will
|
||||||
|
work across a range of printing
|
||||||
|
temperatures from PLA to PC.
|
||||||
|
|
||||||
|
#### Calculating the `temp_coeff` for `[z_thermal_adjust]`
|
||||||
|
|
||||||
|
The easiest way to do this is to measure at 2 different temperatures.
|
||||||
|
Ideally these should be the upper and lower limits of the printing
|
||||||
|
temperature range. E.g. 180C and 290C. You can perform a `PROBE_ACCURACY` at
|
||||||
|
both temperatures and then calculate the difference of the `average z` at both.
|
||||||
|
|
||||||
|
The adjustment value is the change in nozzle length divided by the change in
|
||||||
|
temperature. e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
temp_coeff = -0.05 / (290 - 180) = -0.00045455
|
||||||
|
```
|
||||||
|
|
||||||
|
The expected result is a negative number. Positive values for `temp_coeff` move
|
||||||
|
the nozzle closer to the bed and negative values move it further away.
|
||||||
|
Expect to have to move the nozzle further away as it gets longer when hot.
|
||||||
|
|
||||||
|
#### Configure `[z_thermal_adjust]`
|
||||||
|
Set up z_thermal_adjust to reference the `extruder` as the source of temperature
|
||||||
|
data. E.g.:
|
||||||
|
|
||||||
|
```
|
||||||
|
[z_thermal_adjust nozzle]
|
||||||
|
temp_coeff=-0.00045455
|
||||||
|
sensor_type: temperature_combined
|
||||||
|
sensor_list: extruder
|
||||||
|
combination_method: max
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 400
|
||||||
|
max_z_adjustment: 0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Continuous Tare Filters for Toolhead Load Cells
|
||||||
|
|
||||||
|
Klipper implements a configurable IIR filter on the MCU to provide continuous
|
||||||
|
tareing of the load cell while probing. Continuous taring means the 0 value
|
||||||
|
moves with drift caused by external factors like bowden tubes and thermal
|
||||||
|
changes. This is aimed at toolhead sensors and moving beds that experience lots
|
||||||
|
of external forces that change while probing.
|
||||||
|
|
||||||
|
### Installing SciPy
|
||||||
|
|
||||||
|
The filtering code uses the excellent [SciPy](https://scipy.org/) library to
|
||||||
|
compute the filter coefficients based on the values your enter into the config.
|
||||||
|
|
||||||
|
Pre-compiled SciPi builds are available for Python 3 on 32 bit Raspberry Pi
|
||||||
|
systems. 32 bit + Python 3 is strongly recommended because it will streamline
|
||||||
|
your installation experience. It does work with Python 2 but installation can
|
||||||
|
take 30+ minutes and require installing additional tools.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/klippy-env/bin/pip install scipy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filter Workbench
|
||||||
|
|
||||||
|
The filter parameters should be selected based on drift seen on the printer
|
||||||
|
during normal operation. A Jupyter notebook is provided in scripts,
|
||||||
|
[filter_workbench.ipynb](../scripts/filter_workbench.ipynb), to perform a
|
||||||
|
detailed investigation with real captured data and FFTs.
|
||||||
|
|
||||||
|
### Filtering Suggestions
|
||||||
|
|
||||||
|
For those just trying to get a filter working follow these suggestions:
|
||||||
|
|
||||||
|
* The only essential option is `drift_filter_cutoff_frequency`. A conservative
|
||||||
|
starting value is `0.5`Hz. Prusa shipped the MK4 with a setting of `0.8`Hz and
|
||||||
|
the XL with `11.2`Hz. This is probably a safe range to experiment with. This
|
||||||
|
value should be increased only until normal drift due to bowden tube force is
|
||||||
|
eliminated. Setting this value too high will result in slow triggering and
|
||||||
|
excess force going through the toolhead.
|
||||||
|
* Keep `trigger_force` low. The default is `75`g. The drift filter keeps the
|
||||||
|
internal grams value very close to 0 so a large trigger force is not needed.
|
||||||
|
* Keep `force_safety_limit` to a conservative value. The default value is 2Kg
|
||||||
|
and should keep your toolhead safe while experimenting. If you hit this limit
|
||||||
|
the `drift_filter_cutoff_frequency` value may be too high.
|
||||||
|
|
||||||
|
## Suggestions for Load Cell Tool Boards
|
||||||
|
|
||||||
|
This section covers suggestions for those developing toolhead boards that want
|
||||||
|
to support [load_cell_probe]
|
||||||
|
|
||||||
|
### ADC Sensor Selection & Board Development Hints
|
||||||
|
|
||||||
|
Ideally a sensor would meet these criteria:
|
||||||
|
|
||||||
|
* At least 24 bits wide
|
||||||
|
* Use SPI communications
|
||||||
|
* Has a pin can be used to indicate sample ready without SPI communications.
|
||||||
|
This is often called the "data ready" or "DRDY" pin. Checking a pin is much
|
||||||
|
faster than running an SPI query.
|
||||||
|
* Has a programmable gain amplifier gain setting of 128. This should eliminate
|
||||||
|
the need for a separate amplifier.
|
||||||
|
* Indicates via SPI if the sensor has been reset. Detecting resets avoids
|
||||||
|
timing errors in homing and using noisy data at startup. It can also help
|
||||||
|
users
|
||||||
|
track down wiring and grounding issues.
|
||||||
|
* A selectable sample rate between 350Hz and 2Khz. Very high sample rates don't
|
||||||
|
turn out to be beneficial in our 3D printers because they produce so much
|
||||||
|
noise
|
||||||
|
when moving fast. Sample rates below 250Hz will require slower probing speeds.
|
||||||
|
They also increase the force on the toolhead due to longer delays between
|
||||||
|
measurements. E.g. a 500Hz sensor moving at 5mm/s has the same safety factor
|
||||||
|
as
|
||||||
|
a 100Hz sensor moving at only 1mm/s.
|
||||||
|
* If designing for under-bed applications, and you want to sense multiple load
|
||||||
|
cells, use a chip that can sample all of its inputs simultaneously. Multiplex
|
||||||
|
ADCs that require switching channels have a settling of several samples after
|
||||||
|
each channel switch making them unsuitable for probing applications.
|
||||||
|
|
||||||
|
Implementing support for a new sensor chip is not particularly difficult with
|
||||||
|
Klipper's `bulk_sensor` and `load_cell_endstop` infrastructure.
|
||||||
|
|
||||||
|
### 5V Power Filtering
|
||||||
|
|
||||||
|
It is strongly suggested to use larger capacitors than specified by the ADC chip
|
||||||
|
manufacturer. ADC chips are usually targeted at low noise environments, like
|
||||||
|
battery powered devices. Sensor manufacturers suggested application notes
|
||||||
|
generally assume a quiet power supply. Treat their suggested capacitor values as
|
||||||
|
minimums.
|
||||||
|
|
||||||
|
3D printers put huge amounts of noise onto the 5V bus and this can ruin the
|
||||||
|
sensor's accuracy. Test the sensor on the board with a typical 3D printer power
|
||||||
|
supply and active stepper drivers before deciding on smoothing capacitor sizes.
|
||||||
|
|
||||||
|
### Grounding & Ground Planes
|
||||||
|
|
||||||
|
Analog ADC chips contain components that are very vulnerable to noise and
|
||||||
|
ESD. A large ground plane on the first board layer under the chip can help with
|
||||||
|
noise. Keep the chip away from power sections and DC to DC converters. The board
|
||||||
|
should have proper grounding back to the DC supply.
|
||||||
|
|
||||||
|
### HX711 and HX717 Notes
|
||||||
|
|
||||||
|
This sensor is popular because of its low cost and availability in the
|
||||||
|
supply chain. However, this is a sensor with some drawbacks:
|
||||||
|
|
||||||
|
* The HX71x sensors use bit-bang communication which has a high overhead on the
|
||||||
|
MCU. Using a sensor that communicates via SPI would save resources on the tool
|
||||||
|
board's CPU.
|
||||||
|
* The HX71x lacks a way to communicate reset events to the MCU. Klipper detects
|
||||||
|
resets with a timing heuristic but this is not ideal. Resets indicate a
|
||||||
|
problem with wiring or grounding.
|
||||||
|
* For probing applications the HX717 version is strongly preferred because
|
||||||
|
of its higher sample rate (320 vs 80). Probing speed on the HX711 should be
|
||||||
|
limited to less than 2mm/s.
|
||||||
|
* The sample rate on the HX71x cannot be set from klipper's config. If you have
|
||||||
|
the 10SPS version of the sensor (which is widely distributed) it needs to
|
||||||
|
be physically re-wired to run at 80SPS.
|
||||||
@@ -1,24 +1,26 @@
|
|||||||
# Measuring Resonances
|
# 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
|
accelerometers which can be used to measure resonance frequencies of the printer
|
||||||
for different axes, and auto-tune [input shapers](Resonance_Compensation.md) to
|
for different axes, and auto-tune [input shapers](Resonance_Compensation.md) to
|
||||||
compensate for resonances. Note that using accelerometers requires some
|
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
|
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
|
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
|
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
|
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.
|
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).
|
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
|
For MPU-9250/MPU-9255/MPU-6515/MPU-6050/MPU-6500/ICM20948s and LIS2DW/LIS3DH there
|
||||||
board designs and clones with different I2C pull-up resistors which will need
|
are also a variety of board designs and clones with different I2C pull-up resistors
|
||||||
supplementing.
|
which will need supplementing.
|
||||||
|
|
||||||
## MCUs with Klipper I2C *fast-mode* Support
|
## MCUs with Klipper I2C *fast-mode* Support
|
||||||
|
|
||||||
@@ -27,6 +29,7 @@ supplementing.
|
|||||||
| Raspberry Pi | 3B+, Pico | 3A, 3A+, 3B, 4 |
|
| Raspberry Pi | 3B+, Pico | 3A, 3A+, 3B, 4 |
|
||||||
| AVR ATmega | ATmega328p | ATmega32u4, ATmega128, ATmega168, ATmega328, ATmega644p, ATmega1280, ATmega1284, ATmega2560 |
|
| AVR ATmega | ATmega328p | ATmega32u4, ATmega128, ATmega168, ATmega328, ATmega644p, ATmega1280, ATmega1284, ATmega2560 |
|
||||||
| AVR AT90 | - | AT90usb646, AT90usb1286 |
|
| AVR AT90 | - | AT90usb646, AT90usb1286 |
|
||||||
|
| SAMD | SAMC21G18 | SAMC21G18, SAMD21G18, SAMD21E18, SAMD21J18, SAMD21E15, SAMD51G19, SAMD51J19, SAMD51N19, SAMD51P20, SAME51J19, SAME51N19, SAME54P20 |
|
||||||
|
|
||||||
## Installation instructions
|
## Installation instructions
|
||||||
|
|
||||||
@@ -133,7 +136,7 @@ GND+SCL
|
|||||||
|
|
||||||
Note that unlike a cable shield, any GND(s) should be connected at both ends.
|
Note that unlike a cable shield, any GND(s) should be connected at both ends.
|
||||||
|
|
||||||
#### MPU-9250/MPU-9255/MPU-6515/MPU-6050/MPU-6500
|
#### MPU-9250/MPU-9255/MPU-6515/MPU-6050/MPU-6500/ICM20948
|
||||||
|
|
||||||
These accelerometers have been tested to work over I2C on the RPi, RP2040 (Pico)
|
These accelerometers have been tested to work over I2C on the RPi, RP2040 (Pico)
|
||||||
and AVR at 400kbit/s (*fast mode*). Some MPU accelerometer modules include
|
and AVR at 400kbit/s (*fast mode*). Some MPU accelerometer modules include
|
||||||
@@ -149,7 +152,7 @@ Recommended connection scheme for I2C on the Raspberry Pi:
|
|||||||
| SDA | 03 | GPIO02 (SDA1) |
|
| SDA | 03 | GPIO02 (SDA1) |
|
||||||
| SCL | 05 | GPIO03 (SCL1) |
|
| SCL | 05 | GPIO03 (SCL1) |
|
||||||
|
|
||||||
The RPi has buit-in 1.8K pull-ups on both SCL and SDA.
|
The RPi has built-in 1.8K pull-ups on both SCL and SDA.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -212,12 +215,20 @@ sudo apt install python3-numpy python3-matplotlib libatlas-base-dev libopenblas-
|
|||||||
|
|
||||||
Next, in order to install NumPy in the Klipper environment, run the command:
|
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*
|
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
|
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. 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
|
#### 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.
|
Restart Klipper via the `RESTART` command.
|
||||||
|
|
||||||
#### Configure LIS2DW series
|
#### Configure LIS2DW series over SPI
|
||||||
|
|
||||||
```
|
```
|
||||||
[mcu lis]
|
[mcu lis]
|
||||||
@@ -344,6 +355,7 @@ accel_chip: mpu9250
|
|||||||
probe_points:
|
probe_points:
|
||||||
100, 100, 20 # an example
|
100, 100, 20 # an example
|
||||||
```
|
```
|
||||||
|
If you are using the ICM20948, replace instances of "mpu9250" with "icm20948".
|
||||||
|
|
||||||
#### Configure MPU-9520 Compatibles With Pico
|
#### Configure MPU-9520 Compatibles With Pico
|
||||||
|
|
||||||
@@ -366,6 +378,7 @@ probe_points:
|
|||||||
[static_digital_output pico_3V3pwm] # Improve power stability
|
[static_digital_output pico_3V3pwm] # Improve power stability
|
||||||
pins: pico:gpio23
|
pins: pico:gpio23
|
||||||
```
|
```
|
||||||
|
If you are using the ICM20948, replace instances of "mpu9250" with "icm20948".
|
||||||
|
|
||||||
#### Configure MPU-9520 Compatibles with AVR
|
#### Configure MPU-9520 Compatibles with AVR
|
||||||
|
|
||||||
@@ -384,6 +397,7 @@ accel_chip: mpu9250
|
|||||||
probe_points:
|
probe_points:
|
||||||
100, 100, 20 # an example
|
100, 100, 20 # an example
|
||||||
```
|
```
|
||||||
|
If you are using the ICM20948, replace instances of "mpu9250" with "icm20948".
|
||||||
|
|
||||||
Restart Klipper via the `RESTART` command.
|
Restart Klipper via the `RESTART` command.
|
||||||
|
|
||||||
@@ -683,6 +697,24 @@ 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
|
suggested shaper configuration is almost the same as what you got during the
|
||||||
previous calibration, this step can be skipped.
|
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
|
### Testing custom axes
|
||||||
|
|
||||||
`TEST_RESONANCES` command supports custom axes. While this is not really
|
`TEST_RESONANCES` command supports custom axes. While this is not really
|
||||||
|
|||||||
79
docs/OctoPrint.md
Normal file
79
docs/OctoPrint.md
Normal 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
|
||||||
@@ -17,6 +17,7 @@ communication with the Klipper developers.
|
|||||||
## Installation and Configuration
|
## Installation and Configuration
|
||||||
|
|
||||||
- [Installation](Installation.md): Guide to installing Klipper.
|
- [Installation](Installation.md): Guide to installing Klipper.
|
||||||
|
- [Octoprint](OctoPrint.md): Guide to installing Octoprint with Klipper.
|
||||||
- [Config Reference](Config_Reference.md): Description of config
|
- [Config Reference](Config_Reference.md): Description of config
|
||||||
parameters.
|
parameters.
|
||||||
- [Rotation Distance](Rotation_Distance.md): Calculating the
|
- [Rotation Distance](Rotation_Distance.md): Calculating the
|
||||||
@@ -100,3 +101,4 @@ communication with the Klipper developers.
|
|||||||
- [TSL1401CL filament width sensor](TSL1401CL_Filament_Width_Sensor.md)
|
- [TSL1401CL filament width sensor](TSL1401CL_Filament_Width_Sensor.md)
|
||||||
- [Hall filament width sensor](Hall_Filament_Width_Sensor.md)
|
- [Hall filament width sensor](Hall_Filament_Width_Sensor.md)
|
||||||
- [Eddy Current Inductive probe](Eddy_Probe.md)
|
- [Eddy Current Inductive probe](Eddy_Probe.md)
|
||||||
|
- [Load Cells](Load_Cell.md)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Use a slicer to generate g-code for the large hollow square found in
|
|||||||
[docs/prints/square_tower.stl](prints/square_tower.stl). Use a high
|
[docs/prints/square_tower.stl](prints/square_tower.stl). Use a high
|
||||||
speed (eg, 100mm/s), zero infill, and a coarse layer height (the layer
|
speed (eg, 100mm/s), zero infill, and a coarse layer height (the layer
|
||||||
height should be around 75% of the nozzle diameter). Make sure any
|
height should be around 75% of the nozzle diameter). Make sure any
|
||||||
"dynamic acceleration control" is disabled in the slicer.
|
"dynamic acceleration control" and "scarf joint" seams are disabled in the slicer.
|
||||||
|
|
||||||
Prepare for the test by issuing the following G-Code command:
|
Prepare for the test by issuing the following G-Code command:
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -3,6 +3,35 @@
|
|||||||
History of Klipper releases. Please see
|
History of Klipper releases. Please see
|
||||||
[installation](Installation.md) for information on installing Klipper.
|
[installation](Installation.md) for information on installing Klipper.
|
||||||
|
|
||||||
|
## Klipper 0.13.0
|
||||||
|
|
||||||
|
Available on 20250411. Major changes in this release:
|
||||||
|
* New "sweeping vibrations" resonance testing mechanism for input
|
||||||
|
shaper.
|
||||||
|
* Fans and GPIO pins can now be assigned a formula (via Jinja2
|
||||||
|
"templates").
|
||||||
|
* The bed_mesh code now supports "adaptive bed mesh". The area probed
|
||||||
|
can be adjusted for the size of the print.
|
||||||
|
* A new `minimum_cruise_ratio` kinematic parameter has been added (it
|
||||||
|
replaces the previous `max_accel_to_decel` parameter).
|
||||||
|
* Several new sensors added:
|
||||||
|
* Support for ldc1612 "eddy" current sensors. This includes probing
|
||||||
|
support, fast "scan" probing, and temperature calibration.
|
||||||
|
* New support for "load cell" measurements. Support for connecting
|
||||||
|
these load cells to hx71x and ads1220 ADC sensors.
|
||||||
|
* Support for BMP180, BMP388, and SHT3x temperature sensors. Support
|
||||||
|
for measuring temperature with ADS1x1x ADC chips.
|
||||||
|
* New lis3dh and icm20948 accelerometer support.
|
||||||
|
* Support for mt6816 and mt6826s "hall angle" sensors.
|
||||||
|
* New micro-controller improvements:
|
||||||
|
* New support for rp2350 micro-controllers.
|
||||||
|
* Existing rp2040 chips now run at 200MHz (up from 125Mhz).
|
||||||
|
* The micro-controller code can now define many more commands (up to
|
||||||
|
16384 from 128).
|
||||||
|
* Other modules added: aip31068_spi, canbus_stats, error_mcu,
|
||||||
|
garbage_collection, pwm_cycle_time, pwm_tool, garbage_collection.
|
||||||
|
* Several bug fixes and code cleanups.
|
||||||
|
|
||||||
## Klipper 0.12.0
|
## Klipper 0.12.0
|
||||||
|
|
||||||
Available on 20231110. Major changes in this release:
|
Available on 20231110. Major changes in this release:
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ AD do not include the flats on the corners that some test objects provide.
|
|||||||
## Configure your skew
|
## Configure your skew
|
||||||
|
|
||||||
Make sure `[skew_correction]` is in printer.cfg. You may now use the `SET_SKEW`
|
Make sure `[skew_correction]` is in printer.cfg. You may now use the `SET_SKEW`
|
||||||
gcode to configure skew_correcton. For example, if your measured lengths
|
gcode to configure skew_correction. For example, if your measured lengths
|
||||||
along XY are as follows:
|
along XY are as follows:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -121,5 +121,5 @@ M104 S0
|
|||||||
before the macro call. Also note that SuperSlicer has a
|
before the macro call. Also note that SuperSlicer has a
|
||||||
"custom gcode only" button option, which achieves the same outcome.
|
"custom gcode only" button option, which achieves the same outcome.
|
||||||
|
|
||||||
An example of a START_PRINT macro using these paramaters can
|
An example of a START_PRINT macro using these parameters can
|
||||||
be found in config/sample-macros.cfg
|
be found in config/sample-macros.cfg
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ serve the 3D printing community better. Follow them on
|
|||||||
## Sponsors
|
## 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/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
|
## Klipper Developers
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ The following information is available in the
|
|||||||
## bed_screws
|
## bed_screws
|
||||||
|
|
||||||
The following information is available in the
|
The following information is available in the
|
||||||
`Config_Reference.md#bed_screws` object:
|
[bed_screws](Config_Reference.md#bed_screws) object:
|
||||||
- `is_active`: Returns True if the bed screws adjustment tool is currently
|
- `is_active`: Returns True if the bed screws adjustment tool is currently
|
||||||
active.
|
active.
|
||||||
- `state`: The bed screws adjustment tool state. It is one of
|
- `state`: The bed screws adjustment tool state. It is one of
|
||||||
@@ -39,6 +39,27 @@ the following strings: "adjust", "fine".
|
|||||||
- `current_screw`: The index for the current screw being adjusted.
|
- `current_screw`: The index for the current screw being adjusted.
|
||||||
- `accepted_screws`: The number of accepted screws.
|
- `accepted_screws`: The number of accepted screws.
|
||||||
|
|
||||||
|
## canbus_stats
|
||||||
|
|
||||||
|
The following information is available in the `canbus_stats
|
||||||
|
some_mcu_name` object (this object is automatically available if an
|
||||||
|
mcu is configured to use canbus):
|
||||||
|
- `rx_error`: The number of receive errors detected by the
|
||||||
|
micro-controller canbus hardware.
|
||||||
|
- `tx_error`: The number of transmit errors detected by the
|
||||||
|
micro-controller canbus hardware.
|
||||||
|
- `tx_retries`: The number of transmit attempts that were retried due
|
||||||
|
to bus contention or errors.
|
||||||
|
- `bus_state`: The status of the interface (typically "active" for a
|
||||||
|
bus in normal operation, "warn" for a bus with recent errors,
|
||||||
|
"passive" for a bus that will no longer transmit canbus error
|
||||||
|
frames, or "off" for a bus that will no longer transmit or receive
|
||||||
|
messages).
|
||||||
|
|
||||||
|
Note that only the rp2XXX micro-controllers report a non-zero
|
||||||
|
`tx_retries` field and the rp2XXX micro-controllers always report
|
||||||
|
`tx_error` as zero and `bus_state` as "active".
|
||||||
|
|
||||||
## configfile
|
## configfile
|
||||||
|
|
||||||
The following information is available in the `configfile` object
|
The following information is available in the `configfile` object
|
||||||
@@ -221,6 +242,8 @@ The following information is available in the `gcode_move` object
|
|||||||
The following information is available in the
|
The following information is available in the
|
||||||
[hall_filament_width_sensor](Config_Reference.md#hall_filament_width_sensor)
|
[hall_filament_width_sensor](Config_Reference.md#hall_filament_width_sensor)
|
||||||
object:
|
object:
|
||||||
|
- all items from
|
||||||
|
[filament_switch_sensor](Status_Reference.md#filament_switch_sensor)
|
||||||
- `is_active`: Returns True if the sensor is currently active.
|
- `is_active`: Returns True if the sensor is currently active.
|
||||||
- `Diameter`: The last reading from the sensor in mm.
|
- `Diameter`: The last reading from the sensor in mm.
|
||||||
- `Raw`: The last raw ADC reading from the sensor.
|
- `Raw`: The last raw ADC reading from the sensor.
|
||||||
@@ -268,6 +291,9 @@ is always available):
|
|||||||
- `printing_time`: The amount of time (in seconds) the printer has
|
- `printing_time`: The amount of time (in seconds) the printer has
|
||||||
been in the "Printing" state (as tracked by the idle_timeout
|
been in the "Printing" state (as tracked by the idle_timeout
|
||||||
module).
|
module).
|
||||||
|
- `idle_timeout`: The current 'timeout' (in seconds)
|
||||||
|
to wait for the gcode to be triggered.
|
||||||
|
(as set by [SET_IDLE_TIMEOUT](G-Codes.md#set_idle_timeout))
|
||||||
|
|
||||||
## led
|
## led
|
||||||
|
|
||||||
@@ -277,11 +303,31 @@ The following information is available for each `[led led_name]`,
|
|||||||
- `color_data`: A list of color lists containing the RGBW values for a
|
- `color_data`: A list of color lists containing the RGBW values for a
|
||||||
led in the chain. Each value is represented as a float from 0.0 to
|
led in the chain. Each value is represented as a float from 0.0 to
|
||||||
1.0. Each color list contains 4 items (red, green, blue, white) even
|
1.0. Each color list contains 4 items (red, green, blue, white) even
|
||||||
if the underyling LED supports fewer color channels. For example,
|
if the underlying LED supports fewer color channels. For example,
|
||||||
the blue value (3rd item in color list) of the second neopixel in a
|
the blue value (3rd item in color list) of the second neopixel in a
|
||||||
chain could be accessed at
|
chain could be accessed at
|
||||||
`printer["neopixel <config_name>"].color_data[1][2]`.
|
`printer["neopixel <config_name>"].color_data[1][2]`.
|
||||||
|
|
||||||
|
## load_cell
|
||||||
|
|
||||||
|
The following information is available for each `[load_cell name]`:
|
||||||
|
- 'is_calibrated': True/False is the load cell calibrated
|
||||||
|
- 'counts_per_gram': The number of raw sensor counts that equals 1 gram of force
|
||||||
|
- 'reference_tare_counts': The reference number of raw sensor counts for 0 force
|
||||||
|
- 'tare_counts': The current number of raw sensor counts for 0 force
|
||||||
|
- 'force_g': The force in grams, averaged over the last polling period.
|
||||||
|
- 'min_force_g': The minimum force in grams, over the last polling period.
|
||||||
|
- 'max_force_g': The maximum force in grams, over the last polling period.
|
||||||
|
|
||||||
|
## load_cell_probe
|
||||||
|
|
||||||
|
The following information is available for `[load_cell_probe]`:
|
||||||
|
- all items from [load_cell](Status_Reference.md#load_cell)
|
||||||
|
- all items from [probe](Status_Reference.md#probe)
|
||||||
|
- 'endstop_tare_counts': the load cell probe keeps a tare value independent of
|
||||||
|
the load cell. This re-set at the start of each probe.
|
||||||
|
- 'last_trigger_time': timestamp of the last homing trigger
|
||||||
|
|
||||||
## manual_probe
|
## manual_probe
|
||||||
|
|
||||||
The following information is available in the
|
The following information is available in the
|
||||||
@@ -426,6 +472,12 @@ The following information is available in
|
|||||||
- `printer["servo <config_name>"].value`: The last setting of the PWM
|
- `printer["servo <config_name>"].value`: The last setting of the PWM
|
||||||
pin (a value between 0.0 and 1.0) associated with the servo.
|
pin (a value between 0.0 and 1.0) associated with the servo.
|
||||||
|
|
||||||
|
## skew_correction.py
|
||||||
|
|
||||||
|
The following information is available in the `skew_correction` object (this
|
||||||
|
object is available if any skew_correction is defined):
|
||||||
|
- `current_profile_name`: Returns the name of the currently loaded SKEW_PROFILE.
|
||||||
|
|
||||||
## stepper_enable
|
## stepper_enable
|
||||||
|
|
||||||
The following information is available in the `stepper_enable` object (this
|
The following information is available in the `stepper_enable` object (this
|
||||||
@@ -530,6 +582,12 @@ on a cartesian, hybrid_corexy or hybrid_corexz robot
|
|||||||
- `carriage_1`: The mode of the carriage 1. Possible values are:
|
- `carriage_1`: The mode of the carriage 1. Possible values are:
|
||||||
"INACTIVE", "PRIMARY", "COPY", and "MIRROR".
|
"INACTIVE", "PRIMARY", "COPY", and "MIRROR".
|
||||||
|
|
||||||
|
On a `generic_cartesian` kinematic, the following information is
|
||||||
|
available in `dual_carriage`:
|
||||||
|
- `carriages["<carriage>"]`: The mode of the carriage `<carriage>`. Possible
|
||||||
|
values are "INACTIVE" and "PRIMARY" for the primary carriage and "INACTIVE",
|
||||||
|
"PRIMARY", "COPY", and "MIRROR" for the dual carriage.
|
||||||
|
|
||||||
## virtual_sdcard
|
## virtual_sdcard
|
||||||
|
|
||||||
The following information is available in the
|
The following information is available in the
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ setting `stealthchop_threshold` to 999999). Unfortunately, the drivers
|
|||||||
often produce poor and confusing results if the mode changes while the
|
often produce poor and confusing results if the mode changes while the
|
||||||
motor is at a non-zero velocity.
|
motor is at a non-zero velocity.
|
||||||
|
|
||||||
|
Note that the `stealthchop_threshold` config option does not impact
|
||||||
|
sensorless homing as Klipper automatically switches the TMC driver to
|
||||||
|
an appropriate mode during sensorless homing operations.
|
||||||
|
|
||||||
## TMC interpolate setting introduces small position deviation
|
## TMC interpolate setting introduces small position deviation
|
||||||
|
|
||||||
The TMC driver `interpolate` setting may reduce the audible noise of
|
The TMC driver `interpolate` setting may reduce the audible noise of
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ directory, the docs/CNAME file also controls the website generation.
|
|||||||
To test deploy the main English site locally one can use commands
|
To test deploy the main English site locally one can use commands
|
||||||
similar to the following:
|
similar to the following:
|
||||||
|
|
||||||
virtualenv ~/mkdocs-env && ~/python-env/bin/pip install -r ~/klipper/docs/_klipper3d/mkdocs-requirements.txt
|
virtualenv ~/mkdocs-env && ~/mkdocs-env/bin/pip install -r ~/klipper/docs/_klipper3d/mkdocs-requirements.txt
|
||||||
cd ~/klipper && ~/mkdocs-env/bin/mkdocs serve --config-file ~/klipper/docs/_klipper3d/mkdocs.yml -a 0.0.0.0:8000
|
cd ~/klipper && ~/mkdocs-env/bin/mkdocs serve --config-file ~/klipper/docs/_klipper3d/mkdocs.yml -a 0.0.0.0:8000
|
||||||
|
|
||||||
To test deploy the multi-language site locally one can use commands
|
To test deploy the multi-language site locally one can use commands
|
||||||
similar to the following:
|
similar to the following:
|
||||||
|
|
||||||
virtualenv ~/mkdocs-env && ~/python-env/bin/pip install -r ~/klipper/docs/_klipper3d/mkdocs-requirements.txt
|
virtualenv ~/mkdocs-env && ~/mkdocs-env/bin/pip install -r ~/klipper/docs/_klipper3d/mkdocs-requirements.txt
|
||||||
source ~/mkdocs-env/bin/activate
|
source ~/mkdocs-env/bin/activate
|
||||||
cd ~/klipper && ./docs/_klipper3d/build-translations.sh
|
cd ~/klipper && ./docs/_klipper3d/build-translations.sh
|
||||||
cd ~/klipper/site/ && python3 -m http.server 8000
|
cd ~/klipper/site/ && python3 -m http.server 8000
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Python virtualenv module requirements for mkdocs
|
# Python virtualenv module requirements for mkdocs
|
||||||
jinja2==3.1.4
|
jinja2==3.1.6
|
||||||
mkdocs==1.2.4
|
mkdocs==1.2.4
|
||||||
mkdocs-material==8.1.3
|
mkdocs-material==8.1.3
|
||||||
mkdocs-simple-hooks==0.1.3
|
mkdocs-simple-hooks==0.1.3
|
||||||
|
|||||||
@@ -88,7 +88,9 @@ nav:
|
|||||||
- Config_Changes.md
|
- Config_Changes.md
|
||||||
- Contact.md
|
- Contact.md
|
||||||
- Installation and Configuration:
|
- Installation and Configuration:
|
||||||
|
- Installation:
|
||||||
- Installation.md
|
- Installation.md
|
||||||
|
- OctoPrint.md
|
||||||
- Configuration Reference:
|
- Configuration Reference:
|
||||||
- Config_Reference.md
|
- Config_Reference.md
|
||||||
- Rotation_Distance.md
|
- Rotation_Distance.md
|
||||||
@@ -139,4 +141,5 @@ nav:
|
|||||||
- TSL1401CL_Filament_Width_Sensor.md
|
- TSL1401CL_Filament_Width_Sensor.md
|
||||||
- Hall_Filament_Width_Sensor.md
|
- Hall_Filament_Width_Sensor.md
|
||||||
- Eddy_Probe.md
|
- Eddy_Probe.md
|
||||||
|
- Load_Cell.md
|
||||||
- Sponsors.md
|
- Sponsors.md
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 20 KiB |
@@ -6,13 +6,14 @@ title: Welcome
|
|||||||
|
|
||||||
{ .center-image }
|
{ .center-image }
|
||||||
|
|
||||||
Klipper is a 3d-Printer firmware. It combines the power of a general
|
The Klipper firmware controls 3d-Printers. It combines the power of a
|
||||||
purpose computer with one or more micro-controllers. See the
|
general purpose computer with one or more micro-controllers. See the
|
||||||
[features](Features.md) document for more information on why you
|
[features document](Features.md) for more information on why you
|
||||||
should use Klipper.
|
should use the Klipper software.
|
||||||
|
|
||||||
To begin using Klipper start by [installing](Installation.md) it.
|
Start by [installing Klipper software](Installation.md).
|
||||||
|
|
||||||
Klipper is Free Software. Read the [documentation](Overview.md) or
|
Klipper software is Free Software. Read the
|
||||||
view [the Klipper code on github](https://github.com/Klipper3d/klipper).
|
[documentation](Overview.md), see the [license](../COPYING), or
|
||||||
We depend on the generous support from our [sponsors](Sponsors.md).
|
[download](https://github.com/Klipper3d/Klipper) the software. We
|
||||||
|
depend on the generous support from our [sponsors](Sponsors.md).
|
||||||
|
|||||||
@@ -17,16 +17,16 @@ COMPILE_ARGS = ("-Wall -g -O2 -shared -fPIC"
|
|||||||
" -o %s %s")
|
" -o %s %s")
|
||||||
SSE_FLAGS = "-mfpmath=sse -msse2"
|
SSE_FLAGS = "-mfpmath=sse -msse2"
|
||||||
SOURCE_FILES = [
|
SOURCE_FILES = [
|
||||||
'pyhelper.c', 'serialqueue.c', 'stepcompress.c', 'itersolve.c', 'trapq.c',
|
'pyhelper.c', 'serialqueue.c', 'stepcompress.c', 'steppersync.c',
|
||||||
'pollreactor.c', 'msgblock.c', 'trdispatch.c',
|
'itersolve.c', 'trapq.c', 'pollreactor.c', 'msgblock.c', 'trdispatch.c',
|
||||||
'kin_cartesian.c', 'kin_corexy.c', 'kin_corexz.c', 'kin_delta.c',
|
'kin_cartesian.c', 'kin_corexy.c', 'kin_corexz.c', 'kin_delta.c',
|
||||||
'kin_deltesian.c', 'kin_polar.c', 'kin_rotary_delta.c', 'kin_winch.c',
|
'kin_deltesian.c', 'kin_polar.c', 'kin_rotary_delta.c', 'kin_winch.c',
|
||||||
'kin_extruder.c', 'kin_shaper.c', 'kin_idex.c',
|
'kin_extruder.c', 'kin_shaper.c', 'kin_idex.c', 'kin_generic.c'
|
||||||
]
|
]
|
||||||
DEST_LIB = "c_helper.so"
|
DEST_LIB = "c_helper.so"
|
||||||
OTHER_FILES = [
|
OTHER_FILES = [
|
||||||
'list.h', 'serialqueue.h', 'stepcompress.h', 'itersolve.h', 'pyhelper.h',
|
'list.h', 'serialqueue.h', 'stepcompress.h', 'steppersync.h',
|
||||||
'trapq.h', 'pollreactor.h', 'msgblock.h'
|
'itersolve.h', 'pyhelper.h', 'trapq.h', 'pollreactor.h', 'msgblock.h'
|
||||||
]
|
]
|
||||||
|
|
||||||
defs_stepcompress = """
|
defs_stepcompress = """
|
||||||
@@ -54,25 +54,28 @@ defs_stepcompress = """
|
|||||||
int stepcompress_extract_old(struct stepcompress *sc
|
int stepcompress_extract_old(struct stepcompress *sc
|
||||||
, struct pull_history_steps *p, int max
|
, struct pull_history_steps *p, int max
|
||||||
, uint64_t start_clock, uint64_t end_clock);
|
, uint64_t start_clock, uint64_t end_clock);
|
||||||
|
void stepcompress_set_stepper_kinematics(struct stepcompress *sc
|
||||||
|
, struct stepper_kinematics *sk);
|
||||||
|
"""
|
||||||
|
|
||||||
|
defs_steppersync = """
|
||||||
struct steppersync *steppersync_alloc(struct serialqueue *sq
|
struct steppersync *steppersync_alloc(struct serialqueue *sq
|
||||||
, struct stepcompress **sc_list, int sc_num, int move_num);
|
, struct stepcompress **sc_list, int sc_num, int move_num);
|
||||||
void steppersync_free(struct steppersync *ss);
|
void steppersync_free(struct steppersync *ss);
|
||||||
void steppersync_set_time(struct steppersync *ss
|
void steppersync_set_time(struct steppersync *ss
|
||||||
, double time_offset, double mcu_freq);
|
, double time_offset, double mcu_freq);
|
||||||
int steppersync_flush(struct steppersync *ss, uint64_t move_clock
|
int32_t steppersync_generate_steps(struct steppersync *ss
|
||||||
, uint64_t clear_history_clock);
|
, double gen_steps_time, uint64_t flush_clock);
|
||||||
|
void steppersync_history_expire(struct steppersync *ss, uint64_t end_clock);
|
||||||
|
int steppersync_flush(struct steppersync *ss, uint64_t move_clock);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
defs_itersolve = """
|
defs_itersolve = """
|
||||||
int32_t itersolve_generate_steps(struct stepper_kinematics *sk
|
|
||||||
, double flush_time);
|
|
||||||
double itersolve_check_active(struct stepper_kinematics *sk
|
double itersolve_check_active(struct stepper_kinematics *sk
|
||||||
, double flush_time);
|
, double flush_time);
|
||||||
int32_t itersolve_is_active_axis(struct stepper_kinematics *sk, char axis);
|
int32_t itersolve_is_active_axis(struct stepper_kinematics *sk, char axis);
|
||||||
void itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq);
|
void itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq
|
||||||
void itersolve_set_stepcompress(struct stepper_kinematics *sk
|
, double step_dist);
|
||||||
, struct stepcompress *sc, double step_dist);
|
|
||||||
double itersolve_calc_position_from_coord(struct stepper_kinematics *sk
|
double itersolve_calc_position_from_coord(struct stepper_kinematics *sk
|
||||||
, double x, double y, double z);
|
, double x, double y, double z);
|
||||||
void itersolve_set_position(struct stepper_kinematics *sk
|
void itersolve_set_position(struct stepper_kinematics *sk
|
||||||
@@ -106,6 +109,12 @@ defs_trapq = """
|
|||||||
defs_kin_cartesian = """
|
defs_kin_cartesian = """
|
||||||
struct stepper_kinematics *cartesian_stepper_alloc(char axis);
|
struct stepper_kinematics *cartesian_stepper_alloc(char axis);
|
||||||
"""
|
"""
|
||||||
|
defs_kin_generic_cartesian = """
|
||||||
|
struct stepper_kinematics *generic_cartesian_stepper_alloc(double a_x
|
||||||
|
, double a_y, double a_z);
|
||||||
|
void generic_cartesian_stepper_set_coeffs(struct stepper_kinematics *sk
|
||||||
|
, double a_x, double a_y, double a_z);
|
||||||
|
"""
|
||||||
|
|
||||||
defs_kin_corexy = """
|
defs_kin_corexy = """
|
||||||
struct stepper_kinematics *corexy_stepper_alloc(char type);
|
struct stepper_kinematics *corexy_stepper_alloc(char type);
|
||||||
@@ -154,6 +163,7 @@ defs_kin_shaper = """
|
|||||||
, int n, double a[], double t[]);
|
, int n, double a[], double t[]);
|
||||||
int input_shaper_set_sk(struct stepper_kinematics *sk
|
int input_shaper_set_sk(struct stepper_kinematics *sk
|
||||||
, struct stepper_kinematics *orig_sk);
|
, struct stepper_kinematics *orig_sk);
|
||||||
|
void input_shaper_update_sk(struct stepper_kinematics *sk);
|
||||||
struct stepper_kinematics * input_shaper_alloc(void);
|
struct stepper_kinematics * input_shaper_alloc(void);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -175,7 +185,7 @@ defs_serialqueue = """
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct serialqueue *serialqueue_alloc(int serial_fd, char serial_fd_type
|
struct serialqueue *serialqueue_alloc(int serial_fd, char serial_fd_type
|
||||||
, int client_id);
|
, int client_id, char name[16]);
|
||||||
void serialqueue_exit(struct serialqueue *sq);
|
void serialqueue_exit(struct serialqueue *sq);
|
||||||
void serialqueue_free(struct serialqueue *sq);
|
void serialqueue_free(struct serialqueue *sq);
|
||||||
struct command_queue *serialqueue_alloc_commandqueue(void);
|
struct command_queue *serialqueue_alloc_commandqueue(void);
|
||||||
@@ -212,6 +222,7 @@ defs_trdispatch = """
|
|||||||
defs_pyhelper = """
|
defs_pyhelper = """
|
||||||
void set_python_logging_callback(void (*func)(const char *));
|
void set_python_logging_callback(void (*func)(const char *));
|
||||||
double get_monotonic(void);
|
double get_monotonic(void);
|
||||||
|
int set_thread_name(char name[16]);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
defs_std = """
|
defs_std = """
|
||||||
@@ -220,10 +231,11 @@ defs_std = """
|
|||||||
|
|
||||||
defs_all = [
|
defs_all = [
|
||||||
defs_pyhelper, defs_serialqueue, defs_std, defs_stepcompress,
|
defs_pyhelper, defs_serialqueue, defs_std, defs_stepcompress,
|
||||||
defs_itersolve, defs_trapq, defs_trdispatch,
|
defs_steppersync, defs_itersolve, defs_trapq, defs_trdispatch,
|
||||||
defs_kin_cartesian, defs_kin_corexy, defs_kin_corexz, defs_kin_delta,
|
defs_kin_cartesian, defs_kin_corexy, defs_kin_corexz, defs_kin_delta,
|
||||||
defs_kin_deltesian, defs_kin_polar, defs_kin_rotary_delta, defs_kin_winch,
|
defs_kin_deltesian, defs_kin_polar, defs_kin_rotary_delta, defs_kin_winch,
|
||||||
defs_kin_extruder, defs_kin_shaper, defs_kin_idex,
|
defs_kin_extruder, defs_kin_shaper, defs_kin_idex,
|
||||||
|
defs_kin_generic_cartesian,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Update filenames to an absolute path
|
# Update filenames to an absolute path
|
||||||
@@ -262,11 +274,33 @@ def do_build_code(cmd):
|
|||||||
logging.error(msg)
|
logging.error(msg)
|
||||||
raise Exception(msg)
|
raise Exception(msg)
|
||||||
|
|
||||||
|
# Build the main c_helper.so c code library
|
||||||
|
def check_build_c_library():
|
||||||
|
srcdir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
srcfiles = get_abs_files(srcdir, SOURCE_FILES)
|
||||||
|
ofiles = get_abs_files(srcdir, OTHER_FILES)
|
||||||
|
destlib = get_abs_files(srcdir, [DEST_LIB])[0]
|
||||||
|
if not check_build_code(srcfiles+ofiles+[__file__], destlib):
|
||||||
|
# Code already built
|
||||||
|
return destlib
|
||||||
|
# Select command line options
|
||||||
|
if check_gcc_option(SSE_FLAGS):
|
||||||
|
cmd = "%s %s %s" % (GCC_CMD, SSE_FLAGS, COMPILE_ARGS)
|
||||||
|
else:
|
||||||
|
cmd = "%s %s" % (GCC_CMD, COMPILE_ARGS)
|
||||||
|
# Invoke compiler
|
||||||
|
logging.info("Building C code module %s", DEST_LIB)
|
||||||
|
tempdestlib = get_abs_files(srcdir, ["_temp_" + DEST_LIB])[0]
|
||||||
|
do_build_code(cmd % (tempdestlib, ' '.join(srcfiles)))
|
||||||
|
# Rename from temporary file to final file name
|
||||||
|
os.rename(tempdestlib, destlib)
|
||||||
|
return destlib
|
||||||
|
|
||||||
FFI_main = None
|
FFI_main = None
|
||||||
FFI_lib = None
|
FFI_lib = None
|
||||||
pyhelper_logging_callback = None
|
pyhelper_logging_callback = None
|
||||||
|
|
||||||
# Hepler invoked from C errorf() code to log errors
|
# Helper invoked from C errorf() code to log errors
|
||||||
def logging_callback(msg):
|
def logging_callback(msg):
|
||||||
logging.error(FFI_main.string(msg))
|
logging.error(FFI_main.string(msg))
|
||||||
|
|
||||||
@@ -274,17 +308,9 @@ def logging_callback(msg):
|
|||||||
def get_ffi():
|
def get_ffi():
|
||||||
global FFI_main, FFI_lib, pyhelper_logging_callback
|
global FFI_main, FFI_lib, pyhelper_logging_callback
|
||||||
if FFI_lib is None:
|
if FFI_lib is None:
|
||||||
srcdir = os.path.dirname(os.path.realpath(__file__))
|
# Check if library needs to be built, and build if so
|
||||||
srcfiles = get_abs_files(srcdir, SOURCE_FILES)
|
destlib = check_build_c_library()
|
||||||
ofiles = get_abs_files(srcdir, OTHER_FILES)
|
# Open library
|
||||||
destlib = get_abs_files(srcdir, [DEST_LIB])[0]
|
|
||||||
if check_build_code(srcfiles+ofiles+[__file__], destlib):
|
|
||||||
if check_gcc_option(SSE_FLAGS):
|
|
||||||
cmd = "%s %s %s" % (GCC_CMD, SSE_FLAGS, COMPILE_ARGS)
|
|
||||||
else:
|
|
||||||
cmd = "%s %s" % (GCC_CMD, COMPILE_ARGS)
|
|
||||||
logging.info("Building C code module %s", DEST_LIB)
|
|
||||||
do_build_code(cmd % (destlib, ' '.join(srcfiles)))
|
|
||||||
FFI_main = cffi.FFI()
|
FFI_main = cffi.FFI()
|
||||||
for d in defs_all:
|
for d in defs_all:
|
||||||
FFI_main.cdef(d)
|
FFI_main.cdef(d)
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ struct timepos {
|
|||||||
|
|
||||||
// Generate step times for a portion of a move
|
// Generate step times for a portion of a move
|
||||||
static int32_t
|
static int32_t
|
||||||
itersolve_gen_steps_range(struct stepper_kinematics *sk, struct move *m
|
itersolve_gen_steps_range(struct stepper_kinematics *sk, struct stepcompress *sc
|
||||||
, double abs_start, double abs_end)
|
, struct move *m, double abs_start, double abs_end)
|
||||||
{
|
{
|
||||||
sk_calc_callback calc_position_cb = sk->calc_position_cb;
|
sk_calc_callback calc_position_cb = sk->calc_position_cb;
|
||||||
double half_step = .5 * sk->step_dist;
|
double half_step = .5 * sk->step_dist;
|
||||||
@@ -37,7 +37,7 @@ itersolve_gen_steps_range(struct stepper_kinematics *sk, struct move *m
|
|||||||
if (end > m->move_t)
|
if (end > m->move_t)
|
||||||
end = m->move_t;
|
end = m->move_t;
|
||||||
struct timepos old_guess = {start, sk->commanded_pos}, guess = old_guess;
|
struct timepos old_guess = {start, sk->commanded_pos}, guess = old_guess;
|
||||||
int sdir = stepcompress_get_step_dir(sk->sc);
|
int sdir = stepcompress_get_step_dir(sc);
|
||||||
int is_dir_change = 0, have_bracket = 0, check_oscillate = 0;
|
int is_dir_change = 0, have_bracket = 0, check_oscillate = 0;
|
||||||
double target = sk->commanded_pos + (sdir ? half_step : -half_step);
|
double target = sk->commanded_pos + (sdir ? half_step : -half_step);
|
||||||
double last_time=start, low_time=start, high_time=start + SEEK_TIME_RESET;
|
double last_time=start, low_time=start, high_time=start + SEEK_TIME_RESET;
|
||||||
@@ -99,13 +99,13 @@ itersolve_gen_steps_range(struct stepper_kinematics *sk, struct move *m
|
|||||||
if (!have_bracket || high_time - low_time > .000000001) {
|
if (!have_bracket || high_time - low_time > .000000001) {
|
||||||
if (!is_dir_change && rel_dist >= -half_step)
|
if (!is_dir_change && rel_dist >= -half_step)
|
||||||
// Avoid rollback if stepper fully reaches step position
|
// Avoid rollback if stepper fully reaches step position
|
||||||
stepcompress_commit(sk->sc);
|
stepcompress_commit(sc);
|
||||||
// Guess is not close enough - guess again with new time
|
// Guess is not close enough - guess again with new time
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Found next step - submit it
|
// Found next step - submit it
|
||||||
int ret = stepcompress_append(sk->sc, sdir, m->print_time, guess.time);
|
int ret = stepcompress_append(sc, sdir, m->print_time, guess.time);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
target = sdir ? target+half_step+half_step : target-half_step-half_step;
|
target = sdir ? target+half_step+half_step : target-half_step-half_step;
|
||||||
@@ -143,8 +143,9 @@ check_active(struct stepper_kinematics *sk, struct move *m)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate step times for a range of moves on the trapq
|
// Generate step times for a range of moves on the trapq
|
||||||
int32_t __visible
|
int32_t
|
||||||
itersolve_generate_steps(struct stepper_kinematics *sk, double flush_time)
|
itersolve_generate_steps(struct stepper_kinematics *sk, struct stepcompress *sc
|
||||||
|
, double flush_time)
|
||||||
{
|
{
|
||||||
double last_flush_time = sk->last_flush_time;
|
double last_flush_time = sk->last_flush_time;
|
||||||
sk->last_flush_time = flush_time;
|
sk->last_flush_time = flush_time;
|
||||||
@@ -170,15 +171,15 @@ itersolve_generate_steps(struct stepper_kinematics *sk, double flush_time)
|
|||||||
while (--skip_count && pm->print_time > abs_start)
|
while (--skip_count && pm->print_time > abs_start)
|
||||||
pm = list_prev_entry(pm, node);
|
pm = list_prev_entry(pm, node);
|
||||||
do {
|
do {
|
||||||
int32_t ret = itersolve_gen_steps_range(sk, pm, abs_start
|
int32_t ret = itersolve_gen_steps_range(
|
||||||
, flush_time);
|
sk, sc, pm, abs_start, flush_time);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
pm = list_next_entry(pm, node);
|
pm = list_next_entry(pm, node);
|
||||||
} while (pm != m);
|
} while (pm != m);
|
||||||
}
|
}
|
||||||
// Generate steps for this move
|
// Generate steps for this move
|
||||||
int32_t ret = itersolve_gen_steps_range(sk, m, last_flush_time
|
int32_t ret = itersolve_gen_steps_range(sk, sc, m, last_flush_time
|
||||||
, flush_time);
|
, flush_time);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -195,8 +196,8 @@ itersolve_generate_steps(struct stepper_kinematics *sk, double flush_time)
|
|||||||
double abs_end = force_steps_time;
|
double abs_end = force_steps_time;
|
||||||
if (abs_end > flush_time)
|
if (abs_end > flush_time)
|
||||||
abs_end = flush_time;
|
abs_end = flush_time;
|
||||||
int32_t ret = itersolve_gen_steps_range(sk, m, last_flush_time
|
int32_t ret = itersolve_gen_steps_range(
|
||||||
, abs_end);
|
sk, sc, m, last_flush_time, abs_end);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
skip_count = 1;
|
skip_count = 1;
|
||||||
@@ -240,16 +241,10 @@ itersolve_is_active_axis(struct stepper_kinematics *sk, char axis)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void __visible
|
void __visible
|
||||||
itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq)
|
itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq
|
||||||
|
, double step_dist)
|
||||||
{
|
{
|
||||||
sk->tq = tq;
|
sk->tq = tq;
|
||||||
}
|
|
||||||
|
|
||||||
void __visible
|
|
||||||
itersolve_set_stepcompress(struct stepper_kinematics *sk
|
|
||||||
, struct stepcompress *sc, double step_dist)
|
|
||||||
{
|
|
||||||
sk->sc = sc;
|
|
||||||
sk->step_dist = step_dist;
|
sk->step_dist = step_dist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,11 @@ struct stepper_kinematics {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int32_t itersolve_generate_steps(struct stepper_kinematics *sk
|
int32_t itersolve_generate_steps(struct stepper_kinematics *sk
|
||||||
, double flush_time);
|
, struct stepcompress *sc, double flush_time);
|
||||||
double itersolve_check_active(struct stepper_kinematics *sk, double flush_time);
|
double itersolve_check_active(struct stepper_kinematics *sk, double flush_time);
|
||||||
int32_t itersolve_is_active_axis(struct stepper_kinematics *sk, char axis);
|
int32_t itersolve_is_active_axis(struct stepper_kinematics *sk, char axis);
|
||||||
void itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq);
|
void itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq
|
||||||
void itersolve_set_stepcompress(struct stepper_kinematics *sk
|
, double step_dist);
|
||||||
, struct stepcompress *sc, double step_dist);
|
|
||||||
double itersolve_calc_position_from_coord(struct stepper_kinematics *sk
|
double itersolve_calc_position_from_coord(struct stepper_kinematics *sk
|
||||||
, double x, double y, double z);
|
, double x, double y, double z);
|
||||||
void itersolve_set_position(struct stepper_kinematics *sk
|
void itersolve_set_position(struct stepper_kinematics *sk
|
||||||
|
|||||||
52
klippy/chelper/kin_generic.c
Normal file
52
klippy/chelper/kin_generic.c
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Generic cartesian kinematics stepper position calculation
|
||||||
|
//
|
||||||
|
// Copyright (C) 2024 Dmitry Butyugin <dmbutyugin@google.com>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
#include <stddef.h> // offsetof
|
||||||
|
#include <stdlib.h> // malloc
|
||||||
|
#include <string.h> // memset
|
||||||
|
#include "compiler.h" // __visible
|
||||||
|
#include "itersolve.h" // struct stepper_kinematics
|
||||||
|
#include "trapq.h" // move_get_coord
|
||||||
|
|
||||||
|
struct generic_cartesian_stepper {
|
||||||
|
struct stepper_kinematics sk;
|
||||||
|
struct coord a;
|
||||||
|
};
|
||||||
|
|
||||||
|
static double
|
||||||
|
generic_cartesian_stepper_calc_position(struct stepper_kinematics *sk
|
||||||
|
, struct move *m, double move_time)
|
||||||
|
{
|
||||||
|
struct generic_cartesian_stepper *cs = container_of(
|
||||||
|
sk, struct generic_cartesian_stepper, sk);
|
||||||
|
struct coord c = move_get_coord(m, move_time);
|
||||||
|
return cs->a.x * c.x + cs->a.y * c.y + cs->a.z * c.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __visible
|
||||||
|
generic_cartesian_stepper_set_coeffs(struct stepper_kinematics *sk
|
||||||
|
, double a_x, double a_y, double a_z)
|
||||||
|
{
|
||||||
|
struct generic_cartesian_stepper *cs = container_of(
|
||||||
|
sk, struct generic_cartesian_stepper, sk);
|
||||||
|
cs->a.x = a_x;
|
||||||
|
cs->a.y = a_y;
|
||||||
|
cs->a.z = a_z;
|
||||||
|
cs->sk.active_flags = 0;
|
||||||
|
if (a_x) cs->sk.active_flags |= AF_X;
|
||||||
|
if (a_y) cs->sk.active_flags |= AF_Y;
|
||||||
|
if (a_z) cs->sk.active_flags |= AF_Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stepper_kinematics * __visible
|
||||||
|
generic_cartesian_stepper_alloc(double a_x, double a_y, double a_z)
|
||||||
|
{
|
||||||
|
struct generic_cartesian_stepper *cs = malloc(sizeof(*cs));
|
||||||
|
memset(cs, 0, sizeof(*cs));
|
||||||
|
cs->sk.calc_position_cb = generic_cartesian_stepper_calc_position;
|
||||||
|
generic_cartesian_stepper_set_coeffs(&cs->sk, a_x, a_y, a_z);
|
||||||
|
return &cs->sk;
|
||||||
|
}
|
||||||
@@ -77,5 +77,6 @@ dual_carriage_alloc(void)
|
|||||||
struct dual_carriage_stepper *dc = malloc(sizeof(*dc));
|
struct dual_carriage_stepper *dc = malloc(sizeof(*dc));
|
||||||
memset(dc, 0, sizeof(*dc));
|
memset(dc, 0, sizeof(*dc));
|
||||||
dc->m.move_t = 2. * DUMMY_T;
|
dc->m.move_t = 2. * DUMMY_T;
|
||||||
|
dc->x_scale = dc->y_scale = 1.0;
|
||||||
return &dc->sk;
|
return &dc->sk;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,25 +156,14 @@ shaper_xy_calc_position(struct stepper_kinematics *sk, struct move *m
|
|||||||
return is->orig_sk->calc_position_cb(is->orig_sk, &is->m, DUMMY_T);
|
return is->orig_sk->calc_position_cb(is->orig_sk, &is->m, DUMMY_T);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __visible
|
// A callback that forwards post_cb call to the original kinematics
|
||||||
input_shaper_set_sk(struct stepper_kinematics *sk
|
static void
|
||||||
, struct stepper_kinematics *orig_sk)
|
shaper_commanded_pos_post_fixup(struct stepper_kinematics *sk)
|
||||||
{
|
{
|
||||||
struct input_shaper *is = container_of(sk, struct input_shaper, sk);
|
struct input_shaper *is = container_of(sk, struct input_shaper, sk);
|
||||||
if (orig_sk->active_flags == AF_X)
|
is->orig_sk->commanded_pos = sk->commanded_pos;
|
||||||
is->sk.calc_position_cb = shaper_x_calc_position;
|
is->orig_sk->post_cb(is->orig_sk);
|
||||||
else if (orig_sk->active_flags == AF_Y)
|
sk->commanded_pos = is->orig_sk->commanded_pos;
|
||||||
is->sk.calc_position_cb = shaper_y_calc_position;
|
|
||||||
else if (orig_sk->active_flags & (AF_X | AF_Y))
|
|
||||||
is->sk.calc_position_cb = shaper_xy_calc_position;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
is->sk.active_flags = orig_sk->active_flags;
|
|
||||||
is->orig_sk = orig_sk;
|
|
||||||
is->sk.commanded_pos = orig_sk->commanded_pos;
|
|
||||||
is->sk.last_flush_time = orig_sk->last_flush_time;
|
|
||||||
is->sk.last_move_time = orig_sk->last_move_time;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -195,6 +184,44 @@ shaper_note_generation_time(struct input_shaper *is)
|
|||||||
is->sk.gen_steps_post_active = post_active;
|
is->sk.gen_steps_post_active = post_active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __visible
|
||||||
|
input_shaper_update_sk(struct stepper_kinematics *sk)
|
||||||
|
{
|
||||||
|
struct input_shaper *is = container_of(sk, struct input_shaper, sk);
|
||||||
|
if ((is->orig_sk->active_flags & (AF_X | AF_Y)) == (AF_X | AF_Y))
|
||||||
|
is->sk.calc_position_cb = shaper_xy_calc_position;
|
||||||
|
else if (is->orig_sk->active_flags & AF_X)
|
||||||
|
is->sk.calc_position_cb = shaper_x_calc_position;
|
||||||
|
else if (is->orig_sk->active_flags & AF_Y)
|
||||||
|
is->sk.calc_position_cb = shaper_y_calc_position;
|
||||||
|
is->sk.active_flags = is->orig_sk->active_flags;
|
||||||
|
shaper_note_generation_time(is);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __visible
|
||||||
|
input_shaper_set_sk(struct stepper_kinematics *sk
|
||||||
|
, struct stepper_kinematics *orig_sk)
|
||||||
|
{
|
||||||
|
struct input_shaper *is = container_of(sk, struct input_shaper, sk);
|
||||||
|
if (orig_sk->active_flags == AF_X)
|
||||||
|
is->sk.calc_position_cb = shaper_x_calc_position;
|
||||||
|
else if (orig_sk->active_flags == AF_Y)
|
||||||
|
is->sk.calc_position_cb = shaper_y_calc_position;
|
||||||
|
else if (orig_sk->active_flags & (AF_X | AF_Y))
|
||||||
|
is->sk.calc_position_cb = shaper_xy_calc_position;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
is->sk.active_flags = orig_sk->active_flags;
|
||||||
|
is->orig_sk = orig_sk;
|
||||||
|
is->sk.commanded_pos = orig_sk->commanded_pos;
|
||||||
|
is->sk.last_flush_time = orig_sk->last_flush_time;
|
||||||
|
is->sk.last_move_time = orig_sk->last_move_time;
|
||||||
|
if (orig_sk->post_cb) {
|
||||||
|
is->sk.post_cb = shaper_commanded_pos_post_fixup;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int __visible
|
int __visible
|
||||||
input_shaper_set_shaper_params(struct stepper_kinematics *sk, char axis
|
input_shaper_set_shaper_params(struct stepper_kinematics *sk, char axis
|
||||||
, int n, double a[], double t[])
|
, int n, double a[], double t[])
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <stdio.h> // fprintf
|
#include <stdio.h> // fprintf
|
||||||
#include <string.h> // strerror
|
#include <string.h> // strerror
|
||||||
#include <time.h> // struct timespec
|
#include <time.h> // struct timespec
|
||||||
|
#include <linux/prctl.h> // PR_SET_NAME
|
||||||
|
#include <sys/prctl.h> // prctl
|
||||||
#include "compiler.h" // __visible
|
#include "compiler.h" // __visible
|
||||||
#include "pyhelper.h" // get_monotonic
|
#include "pyhelper.h" // get_monotonic
|
||||||
|
|
||||||
@@ -92,3 +94,10 @@ dump_string(char *outbuf, int outbuf_size, char *inbuf, int inbuf_size)
|
|||||||
*o = '\0';
|
*o = '\0';
|
||||||
return outbuf;
|
return outbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set custom thread names
|
||||||
|
int __visible
|
||||||
|
set_thread_name(char name[16])
|
||||||
|
{
|
||||||
|
return prctl(PR_SET_NAME, name);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ void set_python_logging_callback(void (*func)(const char *));
|
|||||||
void errorf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
void errorf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
||||||
void report_errno(char *where, int rc);
|
void report_errno(char *where, int rc);
|
||||||
char *dump_string(char *outbuf, int outbuf_size, char *inbuf, int inbuf_size);
|
char *dump_string(char *outbuf, int outbuf_size, char *inbuf, int inbuf_size);
|
||||||
|
int set_thread_name(char name[16]);
|
||||||
|
|
||||||
#endif // pyhelper.h
|
#endif // pyhelper.h
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ struct serialqueue {
|
|||||||
uint8_t need_sync;
|
uint8_t need_sync;
|
||||||
int input_pos;
|
int input_pos;
|
||||||
// Threading
|
// Threading
|
||||||
|
char name[16];
|
||||||
pthread_t tid;
|
pthread_t tid;
|
||||||
pthread_mutex_t lock; // protects variables below
|
pthread_mutex_t lock; // protects variables below
|
||||||
pthread_cond_t cond;
|
pthread_cond_t cond;
|
||||||
@@ -612,6 +613,7 @@ static void *
|
|||||||
background_thread(void *data)
|
background_thread(void *data)
|
||||||
{
|
{
|
||||||
struct serialqueue *sq = data;
|
struct serialqueue *sq = data;
|
||||||
|
set_thread_name(sq->name);
|
||||||
pollreactor_run(sq->pr);
|
pollreactor_run(sq->pr);
|
||||||
|
|
||||||
pthread_mutex_lock(&sq->lock);
|
pthread_mutex_lock(&sq->lock);
|
||||||
@@ -623,13 +625,16 @@ background_thread(void *data)
|
|||||||
|
|
||||||
// Create a new 'struct serialqueue' object
|
// Create a new 'struct serialqueue' object
|
||||||
struct serialqueue * __visible
|
struct serialqueue * __visible
|
||||||
serialqueue_alloc(int serial_fd, char serial_fd_type, int client_id)
|
serialqueue_alloc(int serial_fd, char serial_fd_type, int client_id
|
||||||
|
, char name[16])
|
||||||
{
|
{
|
||||||
struct serialqueue *sq = malloc(sizeof(*sq));
|
struct serialqueue *sq = malloc(sizeof(*sq));
|
||||||
memset(sq, 0, sizeof(*sq));
|
memset(sq, 0, sizeof(*sq));
|
||||||
sq->serial_fd = serial_fd;
|
sq->serial_fd = serial_fd;
|
||||||
sq->serial_fd_type = serial_fd_type;
|
sq->serial_fd_type = serial_fd_type;
|
||||||
sq->client_id = client_id;
|
sq->client_id = client_id;
|
||||||
|
strncpy(sq->name, name, sizeof(sq->name));
|
||||||
|
sq->name[sizeof(sq->name)-1] = '\0';
|
||||||
|
|
||||||
int ret = pipe(sq->pipe_fds);
|
int ret = pipe(sq->pipe_fds);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ struct pull_queue_message {
|
|||||||
|
|
||||||
struct serialqueue;
|
struct serialqueue;
|
||||||
struct serialqueue *serialqueue_alloc(int serial_fd, char serial_fd_type
|
struct serialqueue *serialqueue_alloc(int serial_fd, char serial_fd_type
|
||||||
, int client_id);
|
, int client_id, char name[16]);
|
||||||
void serialqueue_exit(struct serialqueue *sq);
|
void serialqueue_exit(struct serialqueue *sq);
|
||||||
void serialqueue_free(struct serialqueue *sq);
|
void serialqueue_free(struct serialqueue *sq);
|
||||||
struct command_queue *serialqueue_alloc_commandqueue(void);
|
struct command_queue *serialqueue_alloc_commandqueue(void);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Stepper pulse schedule compression
|
// Stepper pulse schedule compression
|
||||||
//
|
//
|
||||||
// Copyright (C) 2016-2021 Kevin O'Connor <kevin@koconnor.net>
|
// Copyright (C) 2016-2025 Kevin O'Connor <kevin@koconnor.net>
|
||||||
//
|
//
|
||||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <stdlib.h> // malloc
|
#include <stdlib.h> // malloc
|
||||||
#include <string.h> // memset
|
#include <string.h> // memset
|
||||||
#include "compiler.h" // DIV_ROUND_UP
|
#include "compiler.h" // DIV_ROUND_UP
|
||||||
|
#include "itersolve.h" // itersolve_generate_steps
|
||||||
#include "pyhelper.h" // errorf
|
#include "pyhelper.h" // errorf
|
||||||
#include "serialqueue.h" // struct queue_message
|
#include "serialqueue.h" // struct queue_message
|
||||||
#include "stepcompress.h" // stepcompress_alloc
|
#include "stepcompress.h" // stepcompress_alloc
|
||||||
@@ -46,6 +47,8 @@ struct stepcompress {
|
|||||||
// History tracking
|
// History tracking
|
||||||
int64_t last_position;
|
int64_t last_position;
|
||||||
struct list_head history_list;
|
struct list_head history_list;
|
||||||
|
// Itersolve reference
|
||||||
|
struct stepper_kinematics *sk;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct step_move {
|
struct step_move {
|
||||||
@@ -276,9 +279,9 @@ stepcompress_set_invert_sdir(struct stepcompress *sc, uint32_t invert_sdir)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to free items from the history_list
|
// Expire the stepcompress history older than the given clock
|
||||||
static void
|
void
|
||||||
free_history(struct stepcompress *sc, uint64_t end_clock)
|
stepcompress_history_expire(struct stepcompress *sc, uint64_t end_clock)
|
||||||
{
|
{
|
||||||
while (!list_empty(&sc->history_list)) {
|
while (!list_empty(&sc->history_list)) {
|
||||||
struct history_steps *hs = list_last_entry(
|
struct history_steps *hs = list_last_entry(
|
||||||
@@ -290,13 +293,6 @@ 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
|
// Free memory associated with a 'stepcompress' object
|
||||||
void __visible
|
void __visible
|
||||||
stepcompress_free(struct stepcompress *sc)
|
stepcompress_free(struct stepcompress *sc)
|
||||||
@@ -305,7 +301,7 @@ stepcompress_free(struct stepcompress *sc)
|
|||||||
return;
|
return;
|
||||||
free(sc->queue);
|
free(sc->queue);
|
||||||
message_queue_free(&sc->msg_queue);
|
message_queue_free(&sc->msg_queue);
|
||||||
free_history(sc, UINT64_MAX);
|
stepcompress_history_expire(sc, UINT64_MAX);
|
||||||
free(sc);
|
free(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,6 +317,12 @@ stepcompress_get_step_dir(struct stepcompress *sc)
|
|||||||
return sc->next_step_dir;
|
return sc->next_step_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct list_head *
|
||||||
|
stepcompress_get_msg_queue(struct stepcompress *sc)
|
||||||
|
{
|
||||||
|
return &sc->msg_queue;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine the "print time" of the last_step_clock
|
// Determine the "print time" of the last_step_clock
|
||||||
static void
|
static void
|
||||||
calc_last_step_print_time(struct stepcompress *sc)
|
calc_last_step_print_time(struct stepcompress *sc)
|
||||||
@@ -330,7 +332,7 @@ calc_last_step_print_time(struct stepcompress *sc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the conversion rate of 'print_time' to mcu clock
|
// Set the conversion rate of 'print_time' to mcu clock
|
||||||
static void
|
void
|
||||||
stepcompress_set_time(struct stepcompress *sc
|
stepcompress_set_time(struct stepcompress *sc
|
||||||
, double time_offset, double mcu_freq)
|
, double time_offset, double mcu_freq)
|
||||||
{
|
{
|
||||||
@@ -664,164 +666,25 @@ stepcompress_extract_old(struct stepcompress *sc, struct pull_history_steps *p
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store a reference to stepper_kinematics
|
||||||
/****************************************************************
|
|
||||||
* Step compress synchronization
|
|
||||||
****************************************************************/
|
|
||||||
|
|
||||||
// The steppersync object is used to synchronize the output of mcu
|
|
||||||
// step commands. The mcu can only queue a limited number of step
|
|
||||||
// commands - this code tracks when items on the mcu step queue become
|
|
||||||
// free so that new commands can be transmitted. It also ensures the
|
|
||||||
// mcu step queue is ordered between steppers so that no stepper
|
|
||||||
// starves the other steppers of space in the mcu step queue.
|
|
||||||
|
|
||||||
struct steppersync {
|
|
||||||
// Serial port
|
|
||||||
struct serialqueue *sq;
|
|
||||||
struct command_queue *cq;
|
|
||||||
// Storage for associated stepcompress objects
|
|
||||||
struct stepcompress **sc_list;
|
|
||||||
int sc_num;
|
|
||||||
// Storage for list of pending move clocks
|
|
||||||
uint64_t *move_clocks;
|
|
||||||
int num_move_clocks;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allocate a new 'steppersync' object
|
|
||||||
struct steppersync * __visible
|
|
||||||
steppersync_alloc(struct serialqueue *sq, struct stepcompress **sc_list
|
|
||||||
, int sc_num, int move_num)
|
|
||||||
{
|
|
||||||
struct steppersync *ss = malloc(sizeof(*ss));
|
|
||||||
memset(ss, 0, sizeof(*ss));
|
|
||||||
ss->sq = sq;
|
|
||||||
ss->cq = serialqueue_alloc_commandqueue();
|
|
||||||
|
|
||||||
ss->sc_list = malloc(sizeof(*sc_list)*sc_num);
|
|
||||||
memcpy(ss->sc_list, sc_list, sizeof(*sc_list)*sc_num);
|
|
||||||
ss->sc_num = sc_num;
|
|
||||||
|
|
||||||
ss->move_clocks = malloc(sizeof(*ss->move_clocks)*move_num);
|
|
||||||
memset(ss->move_clocks, 0, sizeof(*ss->move_clocks)*move_num);
|
|
||||||
ss->num_move_clocks = move_num;
|
|
||||||
|
|
||||||
return ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free memory associated with a 'steppersync' object
|
|
||||||
void __visible
|
void __visible
|
||||||
steppersync_free(struct steppersync *ss)
|
stepcompress_set_stepper_kinematics(struct stepcompress *sc
|
||||||
|
, struct stepper_kinematics *sk)
|
||||||
{
|
{
|
||||||
if (!ss)
|
sc->sk = sk;
|
||||||
return;
|
|
||||||
free(ss->sc_list);
|
|
||||||
free(ss->move_clocks);
|
|
||||||
serialqueue_free_commandqueue(ss->cq);
|
|
||||||
free(ss);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the conversion rate of 'print_time' to mcu clock
|
// Generate steps (via itersolve) and flush
|
||||||
void __visible
|
int32_t
|
||||||
steppersync_set_time(struct steppersync *ss, double time_offset
|
stepcompress_generate_steps(struct stepcompress *sc, double gen_steps_time
|
||||||
, double mcu_freq)
|
, uint64_t flush_clock)
|
||||||
{
|
{
|
||||||
int i;
|
if (!sc->sk)
|
||||||
for (i=0; i<ss->sc_num; i++) {
|
return 0;
|
||||||
struct stepcompress *sc = ss->sc_list[i];
|
// Generate steps
|
||||||
stepcompress_set_time(sc, time_offset, mcu_freq);
|
int32_t ret = itersolve_generate_steps(sc->sk, sc, gen_steps_time);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
heap_replace(struct steppersync *ss, uint64_t req_clock)
|
|
||||||
{
|
|
||||||
uint64_t *mc = ss->move_clocks;
|
|
||||||
int nmc = ss->num_move_clocks, pos = 0;
|
|
||||||
for (;;) {
|
|
||||||
int child1_pos = 2*pos+1, child2_pos = 2*pos+2;
|
|
||||||
uint64_t child2_clock = child2_pos < nmc ? mc[child2_pos] : UINT64_MAX;
|
|
||||||
uint64_t child1_clock = child1_pos < nmc ? mc[child1_pos] : UINT64_MAX;
|
|
||||||
if (req_clock <= child1_clock && req_clock <= child2_clock) {
|
|
||||||
mc[pos] = req_clock;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (child1_clock < child2_clock) {
|
|
||||||
mc[pos] = child1_clock;
|
|
||||||
pos = child1_pos;
|
|
||||||
} else {
|
|
||||||
mc[pos] = child2_clock;
|
|
||||||
pos = child2_pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find and transmit any scheduled steps prior to the given 'move_clock'
|
|
||||||
int __visible
|
|
||||||
steppersync_flush(struct steppersync *ss, uint64_t move_clock
|
|
||||||
, uint64_t clear_history_clock)
|
|
||||||
{
|
|
||||||
// Flush each stepcompress to the specified move_clock
|
|
||||||
int i;
|
|
||||||
for (i=0; i<ss->sc_num; i++) {
|
|
||||||
int ret = stepcompress_flush(ss->sc_list[i], move_clock);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
// Flush steps
|
||||||
|
return stepcompress_flush(sc, flush_clock);
|
||||||
// Order commands by the reqclock of each pending command
|
|
||||||
struct list_head msgs;
|
|
||||||
list_init(&msgs);
|
|
||||||
for (;;) {
|
|
||||||
// Find message with lowest reqclock
|
|
||||||
uint64_t req_clock = MAX_CLOCK;
|
|
||||||
struct queue_message *qm = NULL;
|
|
||||||
for (i=0; i<ss->sc_num; i++) {
|
|
||||||
struct stepcompress *sc = ss->sc_list[i];
|
|
||||||
if (!list_empty(&sc->msg_queue)) {
|
|
||||||
struct queue_message *m = list_first_entry(
|
|
||||||
&sc->msg_queue, struct queue_message, node);
|
|
||||||
if (m->req_clock < req_clock) {
|
|
||||||
qm = m;
|
|
||||||
req_clock = m->req_clock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!qm || (qm->min_clock && req_clock > move_clock))
|
|
||||||
break;
|
|
||||||
|
|
||||||
uint64_t next_avail = ss->move_clocks[0];
|
|
||||||
if (qm->min_clock)
|
|
||||||
// The qm->min_clock field is overloaded to indicate that
|
|
||||||
// the command uses the 'move queue' and to store the time
|
|
||||||
// that move queue item becomes available.
|
|
||||||
heap_replace(ss, qm->min_clock);
|
|
||||||
// Reset the min_clock to its normal meaning (minimum transmit time)
|
|
||||||
qm->min_clock = next_avail;
|
|
||||||
|
|
||||||
// Batch this command
|
|
||||||
list_del(&qm->node);
|
|
||||||
list_add_tail(&qm->node, &msgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transmit commands
|
|
||||||
if (!list_empty(&msgs))
|
|
||||||
serialqueue_send_batch(ss->sq, ss->cq, &msgs);
|
|
||||||
|
|
||||||
steppersync_history_expire(ss, clear_history_clock);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,13 @@ void stepcompress_fill(struct stepcompress *sc, uint32_t max_error
|
|||||||
, int32_t set_next_step_dir_msgtag);
|
, int32_t set_next_step_dir_msgtag);
|
||||||
void stepcompress_set_invert_sdir(struct stepcompress *sc
|
void stepcompress_set_invert_sdir(struct stepcompress *sc
|
||||||
, uint32_t invert_sdir);
|
, uint32_t invert_sdir);
|
||||||
|
void stepcompress_history_expire(struct stepcompress *sc, uint64_t end_clock);
|
||||||
void stepcompress_free(struct stepcompress *sc);
|
void stepcompress_free(struct stepcompress *sc);
|
||||||
uint32_t stepcompress_get_oid(struct stepcompress *sc);
|
uint32_t stepcompress_get_oid(struct stepcompress *sc);
|
||||||
int stepcompress_get_step_dir(struct stepcompress *sc);
|
int stepcompress_get_step_dir(struct stepcompress *sc);
|
||||||
|
struct list_head *stepcompress_get_msg_queue(struct stepcompress *sc);
|
||||||
|
void stepcompress_set_time(struct stepcompress *sc
|
||||||
|
, double time_offset, double mcu_freq);
|
||||||
int stepcompress_append(struct stepcompress *sc, int sdir
|
int stepcompress_append(struct stepcompress *sc, int sdir
|
||||||
, double print_time, double step_time);
|
, double print_time, double step_time);
|
||||||
int stepcompress_commit(struct stepcompress *sc);
|
int stepcompress_commit(struct stepcompress *sc);
|
||||||
@@ -34,15 +38,11 @@ int stepcompress_queue_mq_msg(struct stepcompress *sc, uint64_t req_clock
|
|||||||
int stepcompress_extract_old(struct stepcompress *sc
|
int stepcompress_extract_old(struct stepcompress *sc
|
||||||
, struct pull_history_steps *p, int max
|
, struct pull_history_steps *p, int max
|
||||||
, uint64_t start_clock, uint64_t end_clock);
|
, uint64_t start_clock, uint64_t end_clock);
|
||||||
|
struct stepper_kinematics;
|
||||||
struct serialqueue;
|
void stepcompress_set_stepper_kinematics(struct stepcompress *sc
|
||||||
struct steppersync *steppersync_alloc(
|
, struct stepper_kinematics *sk);
|
||||||
struct serialqueue *sq, struct stepcompress **sc_list, int sc_num
|
int32_t stepcompress_generate_steps(struct stepcompress *sc
|
||||||
, int move_num);
|
, double gen_steps_time
|
||||||
void steppersync_free(struct steppersync *ss);
|
, uint64_t flush_clock);
|
||||||
void steppersync_set_time(struct steppersync *ss, double time_offset
|
|
||||||
, double mcu_freq);
|
|
||||||
int steppersync_flush(struct steppersync *ss, uint64_t move_clock
|
|
||||||
, uint64_t clear_history_clock);
|
|
||||||
|
|
||||||
#endif // stepcompress.h
|
#endif // stepcompress.h
|
||||||
|
|||||||
177
klippy/chelper/steppersync.c
Normal file
177
klippy/chelper/steppersync.c
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
// Stepper step transmit synchronization
|
||||||
|
//
|
||||||
|
// Copyright (C) 2016-2025 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
// The steppersync object is used to synchronize the output of mcu
|
||||||
|
// step commands. The mcu can only queue a limited number of step
|
||||||
|
// commands - this code tracks when items on the mcu step queue become
|
||||||
|
// free so that new commands can be transmitted. It also ensures the
|
||||||
|
// mcu step queue is ordered between steppers so that no stepper
|
||||||
|
// starves the other steppers of space in the mcu step queue.
|
||||||
|
|
||||||
|
#include <stddef.h> // offsetof
|
||||||
|
#include <stdlib.h> // malloc
|
||||||
|
#include <string.h> // memset
|
||||||
|
#include "compiler.h" // __visible
|
||||||
|
#include "serialqueue.h" // struct queue_message
|
||||||
|
#include "stepcompress.h" // stepcompress_flush
|
||||||
|
#include "steppersync.h" // steppersync_alloc
|
||||||
|
|
||||||
|
struct steppersync {
|
||||||
|
// Serial port
|
||||||
|
struct serialqueue *sq;
|
||||||
|
struct command_queue *cq;
|
||||||
|
// Storage for associated stepcompress objects
|
||||||
|
struct stepcompress **sc_list;
|
||||||
|
int sc_num;
|
||||||
|
// Storage for list of pending move clocks
|
||||||
|
uint64_t *move_clocks;
|
||||||
|
int num_move_clocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate a new 'steppersync' object
|
||||||
|
struct steppersync * __visible
|
||||||
|
steppersync_alloc(struct serialqueue *sq, struct stepcompress **sc_list
|
||||||
|
, int sc_num, int move_num)
|
||||||
|
{
|
||||||
|
struct steppersync *ss = malloc(sizeof(*ss));
|
||||||
|
memset(ss, 0, sizeof(*ss));
|
||||||
|
ss->sq = sq;
|
||||||
|
ss->cq = serialqueue_alloc_commandqueue();
|
||||||
|
|
||||||
|
ss->sc_list = malloc(sizeof(*sc_list)*sc_num);
|
||||||
|
memcpy(ss->sc_list, sc_list, sizeof(*sc_list)*sc_num);
|
||||||
|
ss->sc_num = sc_num;
|
||||||
|
|
||||||
|
ss->move_clocks = malloc(sizeof(*ss->move_clocks)*move_num);
|
||||||
|
memset(ss->move_clocks, 0, sizeof(*ss->move_clocks)*move_num);
|
||||||
|
ss->num_move_clocks = move_num;
|
||||||
|
|
||||||
|
return ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory associated with a 'steppersync' object
|
||||||
|
void __visible
|
||||||
|
steppersync_free(struct steppersync *ss)
|
||||||
|
{
|
||||||
|
if (!ss)
|
||||||
|
return;
|
||||||
|
free(ss->sc_list);
|
||||||
|
free(ss->move_clocks);
|
||||||
|
serialqueue_free_commandqueue(ss->cq);
|
||||||
|
free(ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the conversion rate of 'print_time' to mcu clock
|
||||||
|
void __visible
|
||||||
|
steppersync_set_time(struct steppersync *ss, double time_offset
|
||||||
|
, double mcu_freq)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i<ss->sc_num; i++) {
|
||||||
|
struct stepcompress *sc = ss->sc_list[i];
|
||||||
|
stepcompress_set_time(sc, time_offset, mcu_freq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate steps and flush stepcompress objects
|
||||||
|
int32_t __visible
|
||||||
|
steppersync_generate_steps(struct steppersync *ss, double gen_steps_time
|
||||||
|
, uint64_t flush_clock)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i<ss->sc_num; i++) {
|
||||||
|
struct stepcompress *sc = ss->sc_list[i];
|
||||||
|
int32_t ret = stepcompress_generate_steps(sc, gen_steps_time
|
||||||
|
, flush_clock);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expire the stepcompress history before the given clock time
|
||||||
|
void __visible
|
||||||
|
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
|
||||||
|
heap_replace(struct steppersync *ss, uint64_t req_clock)
|
||||||
|
{
|
||||||
|
uint64_t *mc = ss->move_clocks;
|
||||||
|
int nmc = ss->num_move_clocks, pos = 0;
|
||||||
|
for (;;) {
|
||||||
|
int child1_pos = 2*pos+1, child2_pos = 2*pos+2;
|
||||||
|
uint64_t child2_clock = child2_pos < nmc ? mc[child2_pos] : UINT64_MAX;
|
||||||
|
uint64_t child1_clock = child1_pos < nmc ? mc[child1_pos] : UINT64_MAX;
|
||||||
|
if (req_clock <= child1_clock && req_clock <= child2_clock) {
|
||||||
|
mc[pos] = req_clock;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (child1_clock < child2_clock) {
|
||||||
|
mc[pos] = child1_clock;
|
||||||
|
pos = child1_pos;
|
||||||
|
} else {
|
||||||
|
mc[pos] = child2_clock;
|
||||||
|
pos = child2_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find and transmit any scheduled steps prior to the given 'move_clock'
|
||||||
|
int __visible
|
||||||
|
steppersync_flush(struct steppersync *ss, uint64_t move_clock)
|
||||||
|
{
|
||||||
|
// Order commands by the reqclock of each pending command
|
||||||
|
struct list_head msgs;
|
||||||
|
list_init(&msgs);
|
||||||
|
for (;;) {
|
||||||
|
// Find message with lowest reqclock
|
||||||
|
uint64_t req_clock = MAX_CLOCK;
|
||||||
|
struct queue_message *qm = NULL;
|
||||||
|
int i;
|
||||||
|
for (i=0; i<ss->sc_num; i++) {
|
||||||
|
struct stepcompress *sc = ss->sc_list[i];
|
||||||
|
struct list_head *sc_mq = stepcompress_get_msg_queue(sc);
|
||||||
|
if (!list_empty(sc_mq)) {
|
||||||
|
struct queue_message *m = list_first_entry(
|
||||||
|
sc_mq, struct queue_message, node);
|
||||||
|
if (m->req_clock < req_clock) {
|
||||||
|
qm = m;
|
||||||
|
req_clock = m->req_clock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!qm || (qm->min_clock && req_clock > move_clock))
|
||||||
|
break;
|
||||||
|
|
||||||
|
uint64_t next_avail = ss->move_clocks[0];
|
||||||
|
if (qm->min_clock)
|
||||||
|
// The qm->min_clock field is overloaded to indicate that
|
||||||
|
// the command uses the 'move queue' and to store the time
|
||||||
|
// that move queue item becomes available.
|
||||||
|
heap_replace(ss, qm->min_clock);
|
||||||
|
// Reset the min_clock to its normal meaning (minimum transmit time)
|
||||||
|
qm->min_clock = next_avail;
|
||||||
|
|
||||||
|
// Batch this command
|
||||||
|
list_del(&qm->node);
|
||||||
|
list_add_tail(&qm->node, &msgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transmit commands
|
||||||
|
if (!list_empty(&msgs))
|
||||||
|
serialqueue_send_batch(ss->sq, ss->cq, &msgs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
18
klippy/chelper/steppersync.h
Normal file
18
klippy/chelper/steppersync.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef STEPPERSYNC_H
|
||||||
|
#define STEPPERSYNC_H
|
||||||
|
|
||||||
|
#include <stdint.h> // uint64_t
|
||||||
|
|
||||||
|
struct serialqueue;
|
||||||
|
struct steppersync *steppersync_alloc(
|
||||||
|
struct serialqueue *sq, struct stepcompress **sc_list, int sc_num
|
||||||
|
, int move_num);
|
||||||
|
void steppersync_free(struct steppersync *ss);
|
||||||
|
void steppersync_set_time(struct steppersync *ss, double time_offset
|
||||||
|
, double mcu_freq);
|
||||||
|
int32_t steppersync_generate_steps(struct steppersync *ss, double gen_steps_time
|
||||||
|
, uint64_t flush_clock);
|
||||||
|
void steppersync_history_expire(struct steppersync *ss, uint64_t end_clock);
|
||||||
|
int steppersync_flush(struct steppersync *ss, uint64_t move_clock);
|
||||||
|
|
||||||
|
#endif // steppersync.h
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
# Code for reading and writing the Klipper config file
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import sys, os, glob, re, time, logging, configparser, io
|
import sys, os, glob, re, time, logging, configparser, io
|
||||||
|
|
||||||
error = configparser.Error
|
error = configparser.Error
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Config section parsing helper
|
||||||
|
######################################################################
|
||||||
|
|
||||||
class sentinel:
|
class sentinel:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -134,30 +139,13 @@ class ConfigWrapper:
|
|||||||
pconfig = self.printer.lookup_object("configfile")
|
pconfig = self.printer.lookup_object("configfile")
|
||||||
pconfig.deprecate(self.section, option, value, msg)
|
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):
|
# Config file parsing (with include file support)
|
||||||
self.printer = printer
|
######################################################################
|
||||||
self.autosave = None
|
|
||||||
self.deprecated = {}
|
class ConfigFileReader:
|
||||||
self.runtime_warnings = []
|
def read_config_file(self, filename):
|
||||||
self.deprecate_warnings = []
|
|
||||||
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):
|
|
||||||
try:
|
try:
|
||||||
f = open(filename, 'r')
|
f = open(filename, 'r')
|
||||||
data = f.read()
|
data = f.read()
|
||||||
@@ -167,6 +155,102 @@ class PrinterConfig:
|
|||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
raise error(msg)
|
raise error(msg)
|
||||||
return data.replace('\r\n', '\n')
|
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):
|
def _find_autosave_data(self, data):
|
||||||
regular_data = data
|
regular_data = data
|
||||||
autosave_data = ""
|
autosave_data = ""
|
||||||
@@ -175,7 +259,7 @@ class PrinterConfig:
|
|||||||
regular_data = data[:pos]
|
regular_data = data[:pos]
|
||||||
autosave_data = data[pos + len(AUTOSAVE_HEADER):].strip()
|
autosave_data = data[pos + len(AUTOSAVE_HEADER):].strip()
|
||||||
# Check for errors and strip line prefixes
|
# Check for errors and strip line prefixes
|
||||||
if "\n#*# " in regular_data:
|
if "\n#*# " in regular_data or autosave_data.find(AUTOSAVE_HEADER) >= 0:
|
||||||
logging.warning("Can't read autosave from config file"
|
logging.warning("Can't read autosave from config file"
|
||||||
" - autosave state corrupted")
|
" - autosave state corrupted")
|
||||||
return data, ""
|
return data, ""
|
||||||
@@ -192,7 +276,7 @@ class PrinterConfig:
|
|||||||
return regular_data, "\n".join(out)
|
return regular_data, "\n".join(out)
|
||||||
comment_r = re.compile('[#;].*$')
|
comment_r = re.compile('[#;].*$')
|
||||||
value_r = re.compile('[^A-Za-z0-9_].*$')
|
value_r = re.compile('[^A-Za-z0-9_].*$')
|
||||||
def _strip_duplicates(self, data, config):
|
def _strip_duplicates(self, data, fileconfig):
|
||||||
# Comment out fields in 'data' that are defined in 'config'
|
# Comment out fields in 'data' that are defined in 'config'
|
||||||
lines = data.split('\n')
|
lines = data.split('\n')
|
||||||
section = None
|
section = None
|
||||||
@@ -210,152 +294,31 @@ class PrinterConfig:
|
|||||||
section = pruned_line[1:-1].strip()
|
section = pruned_line[1:-1].strip()
|
||||||
continue
|
continue
|
||||||
field = self.value_r.sub('', pruned_line)
|
field = self.value_r.sub('', pruned_line)
|
||||||
if config.fileconfig.has_option(section, field):
|
if fileconfig.has_option(section, field):
|
||||||
is_dup_field = True
|
is_dup_field = True
|
||||||
lines[lineno] = '#' + lines[lineno]
|
lines[lineno] = '#' + lines[lineno]
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
def _parse_config_buffer(self, buffer, filename, fileconfig):
|
def load_main_config(self):
|
||||||
if not buffer:
|
|
||||||
return
|
|
||||||
data = '\n'.join(buffer)
|
|
||||||
del buffer[:]
|
|
||||||
sbuffer = io.StringIO(data)
|
|
||||||
if sys.version_info.major >= 3:
|
|
||||||
fileconfig.read_file(sbuffer, filename)
|
|
||||||
else:
|
|
||||||
fileconfig.readfp(sbuffer, filename)
|
|
||||||
def _resolve_include(self, source_filename, include_spec, fileconfig,
|
|
||||||
visited):
|
|
||||||
dirname = os.path.dirname(source_filename)
|
|
||||||
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):
|
|
||||||
filename = self.printer.get_start_args()['config_file']
|
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_data, autosave_data = self._find_autosave_data(data)
|
||||||
regular_config = self._build_config_wrapper(regular_data, filename)
|
regular_fileconfig = cfgrdr.build_fileconfig_with_includes(
|
||||||
autosave_data = self._strip_duplicates(autosave_data, regular_config)
|
regular_data, filename)
|
||||||
self.autosave = self._build_config_wrapper(autosave_data, filename)
|
autosave_data = self._strip_duplicates(autosave_data,
|
||||||
cfg = self._build_config_wrapper(regular_data + autosave_data, filename)
|
regular_fileconfig)
|
||||||
return cfg
|
self.fileconfig = cfgrdr.build_fileconfig(autosave_data, filename)
|
||||||
def check_unused_options(self, config):
|
cfgrdr.append_fileconfig(regular_fileconfig,
|
||||||
fileconfig = config.fileconfig
|
autosave_data, '*AUTOSAVE*')
|
||||||
objects = dict(self.printer.lookup_objects())
|
return regular_fileconfig, self.fileconfig
|
||||||
# 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 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):
|
|
||||||
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.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
|
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
return {'config': self.status_raw_config,
|
return {'save_config_pending': self.save_config_pending,
|
||||||
'settings': self.status_settings,
|
|
||||||
'warnings': self.status_warnings,
|
|
||||||
'save_config_pending': self.save_config_pending,
|
|
||||||
'save_config_pending_items': self.status_save_pending}
|
'save_config_pending_items': self.status_save_pending}
|
||||||
# Autosave functions
|
|
||||||
def set(self, section, option, value):
|
def set(self, section, option, value):
|
||||||
if not self.autosave.fileconfig.has_section(section):
|
if not self.fileconfig.has_section(section):
|
||||||
self.autosave.fileconfig.add_section(section)
|
self.fileconfig.add_section(section)
|
||||||
svalue = str(value)
|
svalue = str(value)
|
||||||
self.autosave.fileconfig.set(section, option, svalue)
|
self.fileconfig.set(section, option, svalue)
|
||||||
pending = dict(self.status_save_pending)
|
pending = dict(self.status_save_pending)
|
||||||
if not section in pending or pending[section] is None:
|
if not section in pending or pending[section] is None:
|
||||||
pending[section] = {}
|
pending[section] = {}
|
||||||
@@ -366,8 +329,8 @@ class PrinterConfig:
|
|||||||
self.save_config_pending = True
|
self.save_config_pending = True
|
||||||
logging.info("save_config: set [%s] %s = %s", section, option, svalue)
|
logging.info("save_config: set [%s] %s = %s", section, option, svalue)
|
||||||
def remove_section(self, section):
|
def remove_section(self, section):
|
||||||
if self.autosave.fileconfig.has_section(section):
|
if self.fileconfig.has_section(section):
|
||||||
self.autosave.fileconfig.remove_section(section)
|
self.fileconfig.remove_section(section)
|
||||||
pending = dict(self.status_save_pending)
|
pending = dict(self.status_save_pending)
|
||||||
pending[section] = None
|
pending[section] = None
|
||||||
self.status_save_pending = pending
|
self.status_save_pending = pending
|
||||||
@@ -378,21 +341,20 @@ class PrinterConfig:
|
|||||||
del pending[section]
|
del pending[section]
|
||||||
self.status_save_pending = pending
|
self.status_save_pending = pending
|
||||||
self.save_config_pending = True
|
self.save_config_pending = True
|
||||||
def _disallow_include_conflicts(self, regular_data, cfgname, gcode):
|
def _disallow_include_conflicts(self, regular_fileconfig):
|
||||||
config = self._build_config_wrapper(regular_data, cfgname)
|
for section in self.fileconfig.sections():
|
||||||
for section in self.autosave.fileconfig.sections():
|
for option in self.fileconfig.options(section):
|
||||||
for option in self.autosave.fileconfig.options(section):
|
if regular_fileconfig.has_option(section, option):
|
||||||
if config.fileconfig.has_option(section, option):
|
|
||||||
msg = ("SAVE_CONFIG section '%s' option '%s' conflicts "
|
msg = ("SAVE_CONFIG section '%s' option '%s' conflicts "
|
||||||
"with included value" % (section, option))
|
"with included value" % (section, option))
|
||||||
raise gcode.error(msg)
|
raise self.printer.command_error(msg)
|
||||||
cmd_SAVE_CONFIG_help = "Overwrite config file and restart"
|
cmd_SAVE_CONFIG_help = "Overwrite config file and restart"
|
||||||
def cmd_SAVE_CONFIG(self, gcmd):
|
def cmd_SAVE_CONFIG(self, gcmd):
|
||||||
if not self.autosave.fileconfig.sections():
|
if not self.fileconfig.sections():
|
||||||
return
|
return
|
||||||
gcode = self.printer.lookup_object('gcode')
|
|
||||||
# Create string containing autosave data
|
# 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()
|
lines = [('#*# ' + l).strip()
|
||||||
for l in autosave_data.split('\n')]
|
for l in autosave_data.split('\n')]
|
||||||
lines.insert(0, "\n" + AUTOSAVE_HEADER.rstrip())
|
lines.insert(0, "\n" + AUTOSAVE_HEADER.rstrip())
|
||||||
@@ -401,16 +363,27 @@ class PrinterConfig:
|
|||||||
# Read in and validate current config file
|
# Read in and validate current config file
|
||||||
cfgname = self.printer.get_start_args()['config_file']
|
cfgname = self.printer.get_start_args()['config_file']
|
||||||
try:
|
try:
|
||||||
data = self._read_config_file(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, old_autosave_data = self._find_autosave_data(data)
|
||||||
config = self._build_config_wrapper(regular_data, cfgname)
|
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:
|
except error as e:
|
||||||
msg = "Unable to parse existing config on SAVE_CONFIG"
|
msg = "Unable to parse existing config on SAVE_CONFIG"
|
||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
raise gcode.error(msg)
|
raise gcmd.error(msg)
|
||||||
regular_data = self._strip_duplicates(regular_data, self.autosave)
|
self._disallow_include_conflicts(regular_fileconfig)
|
||||||
self._disallow_include_conflicts(regular_data, cfgname, gcode)
|
|
||||||
data = regular_data.rstrip() + autosave_data
|
|
||||||
# Determine filenames
|
# Determine filenames
|
||||||
datestr = time.strftime("-%Y%m%d_%H%M%S")
|
datestr = time.strftime("-%Y%m%d_%H%M%S")
|
||||||
backup_name = cfgname + datestr
|
backup_name = cfgname + datestr
|
||||||
@@ -430,6 +403,135 @@ class PrinterConfig:
|
|||||||
except:
|
except:
|
||||||
msg = "Unable to write config file during SAVE_CONFIG"
|
msg = "Unable to write config file during SAVE_CONFIG"
|
||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
raise gcode.error(msg)
|
raise gcmd.error(msg)
|
||||||
# Request a restart
|
# Request a restart
|
||||||
|
gcode = self.printer.lookup_object('gcode')
|
||||||
gcode.request_restart('restart')
|
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)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ def hexify(byte_array):
|
|||||||
return "[%s]" % (", ".join([hex(b) for b in byte_array]))
|
return "[%s]" % (", ".join([hex(b) for b in byte_array]))
|
||||||
|
|
||||||
|
|
||||||
class ADS1220():
|
class ADS1220:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.printer = printer = config.get_printer()
|
self.printer = printer = config.get_printer()
|
||||||
self.name = config.get_name().split()[-1]
|
self.name = config.get_name().split()[-1]
|
||||||
@@ -42,8 +42,35 @@ class ADS1220():
|
|||||||
'660': 660, '1200': 1200, '2000': 2000}
|
'660': 660, '1200': 1200, '2000': 2000}
|
||||||
self.sps_options = self.sps_normal.copy()
|
self.sps_options = self.sps_normal.copy()
|
||||||
self.sps_options.update(self.sps_turbo)
|
self.sps_options.update(self.sps_turbo)
|
||||||
self.sps = config.getchoice('sps', self.sps_options, default='660')
|
self.sps = config.getchoice('sample_rate', self.sps_options,
|
||||||
|
default='660')
|
||||||
self.is_turbo = str(self.sps) in self.sps_turbo
|
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 Setup
|
||||||
spi_speed = 512000 if self.is_turbo else 256000
|
spi_speed = 512000 if self.is_turbo else 256000
|
||||||
self.spi = bus.MCU_SPI_from_config(config, 1, default_speed=spi_speed)
|
self.spi = bus.MCU_SPI_from_config(config, 1, default_speed=spi_speed)
|
||||||
@@ -68,11 +95,8 @@ class ADS1220():
|
|||||||
self.batch_bulk = bulk_sensor.BatchBulkHelper(
|
self.batch_bulk = bulk_sensor.BatchBulkHelper(
|
||||||
self.printer, self._process_batch, self._start_measurements,
|
self.printer, self._process_batch, self._start_measurements,
|
||||||
self._finish_measurements, UPDATE_INTERVAL)
|
self._finish_measurements, UPDATE_INTERVAL)
|
||||||
# publish raw samples to the socket
|
|
||||||
self.batch_bulk.add_mux_endpoint("ads1220/dump_ads1220", "sensor",
|
|
||||||
self.name,
|
|
||||||
{'header': ('time', 'counts')})
|
|
||||||
# Command Configuration
|
# Command Configuration
|
||||||
|
self.attach_probe_cmd = None
|
||||||
mcu.add_config_cmd(
|
mcu.add_config_cmd(
|
||||||
"config_ads1220 oid=%d spi_oid=%d data_ready_pin=%s"
|
"config_ads1220 oid=%d spi_oid=%d data_ready_pin=%s"
|
||||||
% (self.oid, self.spi.get_oid(), self.data_ready_pin))
|
% (self.oid, self.spi.get_oid(), self.data_ready_pin))
|
||||||
@@ -85,6 +109,8 @@ class ADS1220():
|
|||||||
cmdqueue = self.spi.get_command_queue()
|
cmdqueue = self.spi.get_command_queue()
|
||||||
self.query_ads1220_cmd = self.mcu.lookup_command(
|
self.query_ads1220_cmd = self.mcu.lookup_command(
|
||||||
"query_ads1220 oid=%c rest_ticks=%u", cq=cmdqueue)
|
"query_ads1220 oid=%c rest_ticks=%u", cq=cmdqueue)
|
||||||
|
self.attach_probe_cmd = self.mcu.lookup_command(
|
||||||
|
"ads1220_attach_load_cell_probe oid=%c load_cell_probe_oid=%c")
|
||||||
self.ffreader.setup_query_command("query_ads1220_status oid=%c",
|
self.ffreader.setup_query_command("query_ads1220_status oid=%c",
|
||||||
oid=self.oid, cq=cmdqueue)
|
oid=self.oid, cq=cmdqueue)
|
||||||
|
|
||||||
@@ -103,6 +129,9 @@ class ADS1220():
|
|||||||
def add_client(self, callback):
|
def add_client(self, callback):
|
||||||
self.batch_bulk.add_client(callback)
|
self.batch_bulk.add_client(callback)
|
||||||
|
|
||||||
|
def attach_load_cell_probe(self, load_cell_probe_oid):
|
||||||
|
self.attach_probe_cmd.send([self.oid, load_cell_probe_oid])
|
||||||
|
|
||||||
# Measurement decoding
|
# Measurement decoding
|
||||||
def _convert_samples(self, samples):
|
def _convert_samples(self, samples):
|
||||||
adc_factor = 1. / (1 << 23)
|
adc_factor = 1. / (1 << 23)
|
||||||
@@ -157,8 +186,10 @@ class ADS1220():
|
|||||||
mode = 0x2 if self.is_turbo else 0x0 # turbo mode
|
mode = 0x2 if self.is_turbo else 0x0 # turbo mode
|
||||||
sps_list = self.sps_turbo if self.is_turbo else self.sps_normal
|
sps_list = self.sps_turbo if self.is_turbo else self.sps_normal
|
||||||
data_rate = list(sps_list.keys()).index(str(self.sps))
|
data_rate = list(sps_list.keys()).index(str(self.sps))
|
||||||
reg_values = [(self.gain << 1),
|
reg_values = [(self.mux << 4) | (self.gain << 1) | int(self.pga_bypass),
|
||||||
(data_rate << 5) | (mode << 3) | (continuous << 2)]
|
(data_rate << 5) | (mode << 3) | (continuous << 2),
|
||||||
|
(self.vref << 6),
|
||||||
|
0x0]
|
||||||
self.write_reg(0x0, reg_values)
|
self.write_reg(0x0, reg_values)
|
||||||
# start measurements immediately
|
# start measurements immediately
|
||||||
self.send_command(START_SYNC_CMD)
|
self.send_command(START_SYNC_CMD)
|
||||||
@@ -177,7 +208,7 @@ class ADS1220():
|
|||||||
write_command.extend(register_bytes)
|
write_command.extend(register_bytes)
|
||||||
self.spi.spi_send(write_command)
|
self.spi.spi_send(write_command)
|
||||||
stored_val = self.read_reg(reg, len(register_bytes))
|
stored_val = self.read_reg(reg, len(register_bytes))
|
||||||
if register_bytes != stored_val:
|
if bytearray(register_bytes) != stored_val:
|
||||||
raise self.printer.command_error(
|
raise self.printer.command_error(
|
||||||
"Failed to set ADS1220 register [0x%x] to %s: got %s. "
|
"Failed to set ADS1220 register [0x%x] to %s: got %s. "
|
||||||
"This may be a connection problem (e.g. faulty wiring)" % (
|
"This may be a connection problem (e.g. faulty wiring)" % (
|
||||||
|
|||||||
393
klippy/extras/ads1x1x.py
Normal file
393
klippy/extras/ads1x1x.py
Normal 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'])
|
||||||
|
|
||||||
|
pcfg = 0
|
||||||
|
pcfg |= (ADS1X1X_OS['OS_SINGLE'] & \
|
||||||
|
ADS1X1X_REG_CONFIG['OS_MASK'])
|
||||||
|
pcfg |= (ADS1X1X_MUX[pin_params['pin']] & \
|
||||||
|
ADS1X1X_REG_CONFIG['MULTIPLEXER_MASK'])
|
||||||
|
pcfg |= (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.
|
||||||
|
pcfg |= (ADS1X1X_MODE['single'] & \
|
||||||
|
ADS1X1X_REG_CONFIG['MODE_MASK'])
|
||||||
|
# lowest sample rate per default, until report time has been set in
|
||||||
|
# setup_adc_sample
|
||||||
|
pcfg |= (self.comp_mode \
|
||||||
|
& ADS1X1X_REG_CONFIG['COMPARATOR_MODE_MASK'])
|
||||||
|
pcfg |= (self.comp_polarity \
|
||||||
|
& ADS1X1X_REG_CONFIG['COMPARATOR_POLARITY_MASK'])
|
||||||
|
pcfg |= (self.comp_latching \
|
||||||
|
& ADS1X1X_REG_CONFIG['COMPARATOR_LATCHING_MASK'])
|
||||||
|
pcfg |= (self.comp_queue \
|
||||||
|
& ADS1X1X_REG_CONFIG['COMPARATOR_QUEUE_MASK'])
|
||||||
|
|
||||||
|
pin_obj = ADS1X1X_pin(self, pcfg)
|
||||||
|
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):
|
||||||
|
cfg = self._read_register(ADS1X1X_REG_POINTER['CONFIG'])
|
||||||
|
return bool((cfg & 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.pcfg = (pin.pcfg & ~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.pcfg)
|
||||||
|
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, pcfg):
|
||||||
|
self.mcu = chip.mcu
|
||||||
|
self.chip = chip
|
||||||
|
self.pcfg = pcfg
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -166,12 +166,12 @@ class AccelCommandHelper:
|
|||||||
% (accel_x, accel_y, accel_z))
|
% (accel_x, accel_y, accel_z))
|
||||||
cmd_ACCELEROMETER_DEBUG_READ_help = "Query register (for debugging)"
|
cmd_ACCELEROMETER_DEBUG_READ_help = "Query register (for debugging)"
|
||||||
def cmd_ACCELEROMETER_DEBUG_READ(self, gcmd):
|
def cmd_ACCELEROMETER_DEBUG_READ(self, gcmd):
|
||||||
reg = gcmd.get("REG", minval=0, maxval=126, parser=lambda x: int(x, 0))
|
reg = gcmd.get("REG", minval=0, maxval=127, parser=lambda x: int(x, 0))
|
||||||
val = self.chip.read_reg(reg)
|
val = self.chip.read_reg(reg)
|
||||||
gcmd.respond_info("Accelerometer REG[0x%x] = 0x%x" % (reg, val))
|
gcmd.respond_info("Accelerometer REG[0x%x] = 0x%x" % (reg, val))
|
||||||
cmd_ACCELEROMETER_DEBUG_WRITE_help = "Set register (for debugging)"
|
cmd_ACCELEROMETER_DEBUG_WRITE_help = "Set register (for debugging)"
|
||||||
def cmd_ACCELEROMETER_DEBUG_WRITE(self, gcmd):
|
def cmd_ACCELEROMETER_DEBUG_WRITE(self, gcmd):
|
||||||
reg = gcmd.get("REG", minval=0, maxval=126, parser=lambda x: int(x, 0))
|
reg = gcmd.get("REG", minval=0, maxval=127, parser=lambda x: int(x, 0))
|
||||||
val = gcmd.get("VAL", minval=0, maxval=255, parser=lambda x: int(x, 0))
|
val = gcmd.get("VAL", minval=0, maxval=255, parser=lambda x: int(x, 0))
|
||||||
self.chip.set_reg(reg, val)
|
self.chip.set_reg(reg, val)
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class AngleCalibration:
|
|||||||
return None
|
return None
|
||||||
return self.mcu_stepper.mcu_to_commanded_position(self.mcu_pos_offset)
|
return self.mcu_stepper.mcu_to_commanded_position(self.mcu_pos_offset)
|
||||||
def load_calibration(self, angles):
|
def load_calibration(self, angles):
|
||||||
# Calculate linear intepolation calibration buckets by solving
|
# Calculate linear interpolation calibration buckets by solving
|
||||||
# linear equations
|
# linear equations
|
||||||
angle_max = 1 << ANGLE_BITS
|
angle_max = 1 << ANGLE_BITS
|
||||||
calibration_count = 1 << CALIBRATION_BITS
|
calibration_count = 1 << CALIBRATION_BITS
|
||||||
@@ -411,6 +411,196 @@ class HelperTLE5012B:
|
|||||||
parser=lambda x: int(x, 0))
|
parser=lambda x: int(x, 0))
|
||||||
self._write_reg(reg, val)
|
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
|
BYTES_PER_SAMPLE = 3
|
||||||
SAMPLES_PER_BLOCK = bulk_sensor.MAX_BULK_MSG_SIZE // BYTES_PER_SAMPLE
|
SAMPLES_PER_BLOCK = bulk_sensor.MAX_BULK_MSG_SIZE // BYTES_PER_SAMPLE
|
||||||
|
|
||||||
@@ -427,8 +617,11 @@ class Angle:
|
|||||||
self.start_clock = self.time_shift = self.sample_ticks = 0
|
self.start_clock = self.time_shift = self.sample_ticks = 0
|
||||||
self.last_sequence = self.last_angle = 0
|
self.last_sequence = self.last_angle = 0
|
||||||
# Sensor type
|
# Sensor type
|
||||||
sensors = { "a1333": HelperA1333, "as5047d": HelperAS5047D,
|
sensors = { "a1333": HelperA1333,
|
||||||
"tle5012b": HelperTLE5012B }
|
"as5047d": HelperAS5047D,
|
||||||
|
"tle5012b": HelperTLE5012B,
|
||||||
|
"mt6816": HelperMT6816,
|
||||||
|
"mt6826s": HelperMT6826S }
|
||||||
sensor_type = config.getchoice('sensor_type', {s: s for s in sensors})
|
sensor_type = config.getchoice('sensor_type', {s: s for s in sensors})
|
||||||
sensor_class = sensors[sensor_type]
|
sensor_class = sensors[sensor_type]
|
||||||
self.spi = bus.MCU_SPI_from_config(config, sensor_class.SPI_MODE,
|
self.spi = bus.MCU_SPI_from_config(config, sensor_class.SPI_MODE,
|
||||||
|
|||||||
@@ -23,18 +23,27 @@ class AxisTwistCompensation:
|
|||||||
self.horizontal_move_z = config.getfloat('horizontal_move_z',
|
self.horizontal_move_z = config.getfloat('horizontal_move_z',
|
||||||
DEFAULT_HORIZONTAL_MOVE_Z)
|
DEFAULT_HORIZONTAL_MOVE_Z)
|
||||||
self.speed = config.getfloat('speed', DEFAULT_SPEED)
|
self.speed = config.getfloat('speed', DEFAULT_SPEED)
|
||||||
self.calibrate_start_x = config.getfloat('calibrate_start_x')
|
self.calibrate_start_x = config.getfloat('calibrate_start_x',
|
||||||
self.calibrate_end_x = config.getfloat('calibrate_end_x')
|
default=None)
|
||||||
self.calibrate_y = config.getfloat('calibrate_y')
|
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',
|
self.z_compensations = config.getlists('z_compensations',
|
||||||
default=[], parser=float)
|
default=[], parser=float)
|
||||||
self.compensation_start_x = config.getfloat('compensation_start_x',
|
self.compensation_start_x = config.getfloat('compensation_start_x',
|
||||||
default=None)
|
default=None)
|
||||||
self.compensation_end_x = config.getfloat('compensation_start_y',
|
self.compensation_end_x = config.getfloat('compensation_end_x',
|
||||||
default=None)
|
default=None)
|
||||||
|
|
||||||
self.m = None
|
self.calibrate_start_y = config.getfloat('calibrate_start_y',
|
||||||
self.b = None
|
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
|
# setup calibrater
|
||||||
self.calibrater = Calibrater(self, config)
|
self.calibrater = Calibrater(self, config)
|
||||||
@@ -43,28 +52,46 @@ class AxisTwistCompensation:
|
|||||||
self._update_z_compensation_value)
|
self._update_z_compensation_value)
|
||||||
|
|
||||||
def _update_z_compensation_value(self, pos):
|
def _update_z_compensation_value(self, pos):
|
||||||
if not self.z_compensations:
|
if self.z_compensations:
|
||||||
return
|
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)
|
sample_count = len(z_compensations)
|
||||||
spacing = ((self.calibrate_end_x - self.calibrate_start_x)
|
spacing = ((comp_end - comp_start)
|
||||||
/ (sample_count - 1))
|
/ (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 = int(math.floor(interpolate_t))
|
||||||
interpolate_i = bed_mesh.constrain(interpolate_i, 0, sample_count - 2)
|
interpolate_i = bed_mesh.constrain(interpolate_i, 0, sample_count - 2)
|
||||||
interpolate_t -= interpolate_i
|
interpolate_t -= interpolate_i
|
||||||
interpolated_z_compensation = bed_mesh.lerp(
|
interpolated_z_compensation = bed_mesh.lerp(
|
||||||
interpolate_t, z_compensations[interpolate_i],
|
interpolate_t, z_compensations[interpolate_i],
|
||||||
z_compensations[interpolate_i + 1])
|
z_compensations[interpolate_i + 1])
|
||||||
pos[2] += interpolated_z_compensation
|
return interpolated_z_compensation
|
||||||
|
|
||||||
def clear_compensations(self):
|
def clear_compensations(self, axis=None):
|
||||||
|
if axis is None:
|
||||||
self.z_compensations = []
|
self.z_compensations = []
|
||||||
self.m = None
|
self.zy_compensations = []
|
||||||
self.b = None
|
elif axis == 'X':
|
||||||
|
self.z_compensations = []
|
||||||
|
elif axis == 'Y':
|
||||||
|
self.zy_compensations = []
|
||||||
|
|
||||||
class Calibrater:
|
class Calibrater:
|
||||||
def __init__(self, compensation, config):
|
def __init__(self, compensation, config):
|
||||||
@@ -80,10 +107,14 @@ class Calibrater:
|
|||||||
self._handle_connect)
|
self._handle_connect)
|
||||||
self.speed = compensation.speed
|
self.speed = compensation.speed
|
||||||
self.horizontal_move_z = compensation.horizontal_move_z
|
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)
|
compensation.calibrate_y)
|
||||||
self.end_point = (compensation.calibrate_end_x,
|
self.x_end_point = (compensation.calibrate_end_x,
|
||||||
compensation.calibrate_y)
|
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.results = None
|
||||||
self.current_point_index = None
|
self.current_point_index = None
|
||||||
self.gcmd = None
|
self.gcmd = None
|
||||||
@@ -94,9 +125,8 @@ class Calibrater:
|
|||||||
|
|
||||||
def _handle_connect(self):
|
def _handle_connect(self):
|
||||||
self.probe = self.printer.lookup_object('probe', None)
|
self.probe = self.printer.lookup_object('probe', None)
|
||||||
if (self.probe is None):
|
if self.probe is None:
|
||||||
config = self.printer.lookup_object('configfile')
|
raise self.printer.config_error(
|
||||||
raise config.error(
|
|
||||||
"AXIS_TWIST_COMPENSATION requires [probe] to be defined")
|
"AXIS_TWIST_COMPENSATION requires [probe] to be defined")
|
||||||
self.lift_speed = self.probe.get_probe_params()['lift_speed']
|
self.lift_speed = self.probe.get_probe_params()['lift_speed']
|
||||||
self.probe_x_offset, self.probe_y_offset, _ = \
|
self.probe_x_offset, self.probe_y_offset, _ = \
|
||||||
@@ -119,20 +149,75 @@ class Calibrater:
|
|||||||
def cmd_AXIS_TWIST_COMPENSATION_CALIBRATE(self, gcmd):
|
def cmd_AXIS_TWIST_COMPENSATION_CALIBRATE(self, gcmd):
|
||||||
self.gcmd = gcmd
|
self.gcmd = gcmd
|
||||||
sample_count = gcmd.get_int('SAMPLE_COUNT', DEFAULT_SAMPLE_COUNT)
|
sample_count = gcmd.get_int('SAMPLE_COUNT', DEFAULT_SAMPLE_COUNT)
|
||||||
|
axis = gcmd.get('AXIS', 'X')
|
||||||
|
|
||||||
# check for valid sample_count
|
# check for valid sample_count
|
||||||
if sample_count is None or sample_count < 2:
|
if sample_count < 2:
|
||||||
raise self.gcmd.error(
|
raise self.gcmd.error(
|
||||||
"SAMPLE_COUNT to probe must be at least 2")
|
"SAMPLE_COUNT to probe must be at least 2")
|
||||||
|
|
||||||
# clear the current config
|
# calculate the points to put the probe at, returned as a list of tuples
|
||||||
self.compensation.clear_compensations()
|
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.")
|
||||||
|
|
||||||
# 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(
|
probe_points = self._calculate_probe_points(
|
||||||
nozzle_points, self.probe_x_offset, self.probe_y_offset)
|
nozzle_points, self.probe_x_offset, self.probe_y_offset)
|
||||||
|
|
||||||
@@ -142,17 +227,9 @@ class Calibrater:
|
|||||||
# begin calibration
|
# begin calibration
|
||||||
self.current_point_index = 0
|
self.current_point_index = 0
|
||||||
self.results = []
|
self.results = []
|
||||||
|
self.current_axis = axis
|
||||||
self._calibration(probe_points, nozzle_points, interval_dist)
|
self._calibration(probe_points, nozzle_points, interval_dist)
|
||||||
|
|
||||||
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 = []
|
|
||||||
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
|
|
||||||
|
|
||||||
def _calculate_probe_points(self, nozzle_points,
|
def _calculate_probe_points(self, nozzle_points,
|
||||||
probe_x_offset, probe_y_offset):
|
probe_x_offset, probe_y_offset):
|
||||||
# calculate the points to put the nozzle at
|
# calculate the points to put the nozzle at
|
||||||
@@ -238,14 +315,31 @@ class Calibrater:
|
|||||||
configfile = self.printer.lookup_object('configfile')
|
configfile = self.printer.lookup_object('configfile')
|
||||||
values_as_str = ', '.join(["{:.6f}".format(x)
|
values_as_str = ', '.join(["{:.6f}".format(x)
|
||||||
for x in self.results])
|
for x in self.results])
|
||||||
|
|
||||||
|
if(self.current_axis == 'X'):
|
||||||
|
|
||||||
configfile.set(self.configname, 'z_compensations', values_as_str)
|
configfile.set(self.configname, 'z_compensations', values_as_str)
|
||||||
configfile.set(self.configname, 'compensation_start_x',
|
configfile.set(self.configname, 'compensation_start_x',
|
||||||
self.start_point[0])
|
self.x_start_point[0])
|
||||||
configfile.set(self.configname, 'compensation_end_x',
|
configfile.set(self.configname, 'compensation_end_x',
|
||||||
self.end_point[0])
|
self.x_end_point[0])
|
||||||
|
|
||||||
self.compensation.z_compensations = self.results
|
self.compensation.z_compensations = self.results
|
||||||
self.compensation.compensation_start_x = self.start_point[0]
|
self.compensation.compensation_start_x = self.x_start_point[0]
|
||||||
self.compensation.compensation_end_x = self.end_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(
|
self.gcode.respond_info(
|
||||||
"AXIS_TWIST_COMPENSATION state has been saved "
|
"AXIS_TWIST_COMPENSATION state has been saved "
|
||||||
"for the current session. The SAVE_CONFIG command will "
|
"for the current session. The SAVE_CONFIG command will "
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ def constrain(val, min_val, max_val):
|
|||||||
def lerp(t, v0, v1):
|
def lerp(t, v0, v1):
|
||||||
return (1. - t) * v0 + t * v1
|
return (1. - t) * v0 + t * v1
|
||||||
|
|
||||||
# retreive commma separated pair from config
|
# retrieve comma separated pair from config
|
||||||
def parse_config_pair(config, option, default, minval=None, maxval=None):
|
def parse_config_pair(config, option, default, minval=None, maxval=None):
|
||||||
pair = config.getintlist(option, (default, default))
|
pair = config.getintlist(option, (default, default))
|
||||||
if len(pair) != 2:
|
if len(pair) != 2:
|
||||||
@@ -54,7 +54,7 @@ def parse_config_pair(config, option, default, minval=None, maxval=None):
|
|||||||
% (option, str(maxval)))
|
% (option, str(maxval)))
|
||||||
return pair
|
return pair
|
||||||
|
|
||||||
# retreive commma separated pair from a g-code command
|
# retrieve comma separated pair from a g-code command
|
||||||
def parse_gcmd_pair(gcmd, name, minval=None, maxval=None):
|
def parse_gcmd_pair(gcmd, name, minval=None, maxval=None):
|
||||||
try:
|
try:
|
||||||
pair = [int(v.strip()) for v in gcmd.get(name).split(',')]
|
pair = [int(v.strip()) for v in gcmd.get(name).split(',')]
|
||||||
@@ -74,7 +74,7 @@ def parse_gcmd_pair(gcmd, name, minval=None, maxval=None):
|
|||||||
% (name, maxval))
|
% (name, maxval))
|
||||||
return pair
|
return pair
|
||||||
|
|
||||||
# retreive commma separated coordinate from a g-code command
|
# retrieve comma separated coordinate from a g-code command
|
||||||
def parse_gcmd_coord(gcmd, name):
|
def parse_gcmd_coord(gcmd, name):
|
||||||
try:
|
try:
|
||||||
v1, v2 = [float(v.strip()) for v in gcmd.get(name).split(',')]
|
v1, v2 = [float(v.strip()) for v in gcmd.get(name).split(',')]
|
||||||
@@ -133,7 +133,7 @@ class BedMesh:
|
|||||||
self.update_status()
|
self.update_status()
|
||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
self.toolhead = self.printer.lookup_object('toolhead')
|
self.toolhead = self.printer.lookup_object('toolhead')
|
||||||
self.bmc.print_generated_points(logging.info)
|
self.bmc.print_generated_points(logging.info, truncate=True)
|
||||||
def set_mesh(self, mesh):
|
def set_mesh(self, mesh):
|
||||||
if mesh is not None and self.fade_end != self.FADE_DISABLE:
|
if mesh is not None and self.fade_end != self.FADE_DISABLE:
|
||||||
self.log_fade_complete = True
|
self.log_fade_complete = True
|
||||||
@@ -186,7 +186,8 @@ class BedMesh:
|
|||||||
self.last_position[2] -= self.fade_target
|
self.last_position[2] -= self.fade_target
|
||||||
else:
|
else:
|
||||||
# return current position minus the current z-adjustment
|
# return current position minus the current z-adjustment
|
||||||
x, y, z, e = self.toolhead.get_position()
|
cur_pos = self.toolhead.get_position()
|
||||||
|
x, y, z = cur_pos[:3]
|
||||||
max_adj = self.z_mesh.calc_z(x, y)
|
max_adj = self.z_mesh.calc_z(x, y)
|
||||||
factor = 1.
|
factor = 1.
|
||||||
z_adj = max_adj - self.fade_target
|
z_adj = max_adj - self.fade_target
|
||||||
@@ -202,19 +203,19 @@ class BedMesh:
|
|||||||
(self.fade_dist - z_adj))
|
(self.fade_dist - z_adj))
|
||||||
factor = constrain(factor, 0., 1.)
|
factor = constrain(factor, 0., 1.)
|
||||||
final_z_adj = factor * z_adj + self.fade_target
|
final_z_adj = factor * z_adj + self.fade_target
|
||||||
self.last_position[:] = [x, y, z - final_z_adj, e]
|
self.last_position[:] = [x, y, z - final_z_adj] + cur_pos[3:]
|
||||||
return list(self.last_position)
|
return list(self.last_position)
|
||||||
def move(self, newpos, speed):
|
def move(self, newpos, speed):
|
||||||
factor = self.get_z_factor(newpos[2])
|
factor = self.get_z_factor(newpos[2])
|
||||||
if self.z_mesh is None or not factor:
|
if self.z_mesh is None or not factor:
|
||||||
# No mesh calibrated, or mesh leveling phased out.
|
# No mesh calibrated, or mesh leveling phased out.
|
||||||
x, y, z, e = newpos
|
x, y, z = newpos[:3]
|
||||||
if self.log_fade_complete:
|
if self.log_fade_complete:
|
||||||
self.log_fade_complete = False
|
self.log_fade_complete = False
|
||||||
logging.info(
|
logging.info(
|
||||||
"bed_mesh fade complete: Current Z: %.4f fade_target: %.4f "
|
"bed_mesh fade complete: Current Z: %.4f fade_target: %.4f "
|
||||||
% (z, self.fade_target))
|
% (z, self.fade_target))
|
||||||
self.toolhead.move([x, y, z + self.fade_target, e], speed)
|
self.toolhead.move([x, y, z + self.fade_target] + newpos[3:], speed)
|
||||||
else:
|
else:
|
||||||
self.splitter.build_move(self.last_position, newpos, factor)
|
self.splitter.build_move(self.last_position, newpos, factor)
|
||||||
while not self.splitter.traverse_complete:
|
while not self.splitter.traverse_complete:
|
||||||
@@ -346,7 +347,7 @@ class BedMeshCalibrate:
|
|||||||
self.gcode.register_command(
|
self.gcode.register_command(
|
||||||
'BED_MESH_CALIBRATE', self.cmd_BED_MESH_CALIBRATE,
|
'BED_MESH_CALIBRATE', self.cmd_BED_MESH_CALIBRATE,
|
||||||
desc=self.cmd_BED_MESH_CALIBRATE_help)
|
desc=self.cmd_BED_MESH_CALIBRATE_help)
|
||||||
def print_generated_points(self, print_func):
|
def print_generated_points(self, print_func, truncate=False):
|
||||||
x_offset = y_offset = 0.
|
x_offset = y_offset = 0.
|
||||||
probe = self.printer.lookup_object('probe', None)
|
probe = self.printer.lookup_object('probe', None)
|
||||||
if probe is not None:
|
if probe is not None:
|
||||||
@@ -355,6 +356,10 @@ class BedMeshCalibrate:
|
|||||||
" | Tool Adjusted | Probe")
|
" | Tool Adjusted | Probe")
|
||||||
points = self.probe_mgr.get_base_points()
|
points = self.probe_mgr.get_base_points()
|
||||||
for i, (x, y) in enumerate(points):
|
for i, (x, y) in enumerate(points):
|
||||||
|
if i >= 50 and truncate:
|
||||||
|
end = len(points) - 1
|
||||||
|
print_func("...points %d through %d truncated" % (i, end))
|
||||||
|
break
|
||||||
adj_pt = "(%.1f, %.1f)" % (x - x_offset, y - y_offset)
|
adj_pt = "(%.1f, %.1f)" % (x - x_offset, y - y_offset)
|
||||||
mesh_pt = "(%.1f, %.1f)" % (x, y)
|
mesh_pt = "(%.1f, %.1f)" % (x, y)
|
||||||
print_func(
|
print_func(
|
||||||
@@ -613,8 +618,6 @@ class BedMeshCalibrate:
|
|||||||
self.mesh_config, self.mesh_min, self.mesh_max,
|
self.mesh_config, self.mesh_min, self.mesh_max,
|
||||||
self.radius, self.origin, probe_method
|
self.radius, self.origin, probe_method
|
||||||
)
|
)
|
||||||
gcmd.respond_info("Generating new points...")
|
|
||||||
self.print_generated_points(gcmd.respond_info)
|
|
||||||
msg = "\n".join(["%s: %s" % (k, v)
|
msg = "\n".join(["%s: %s" % (k, v)
|
||||||
for k, v in self.mesh_config.items()])
|
for k, v in self.mesh_config.items()])
|
||||||
logging.info("Updated Mesh Configuration:\n" + msg)
|
logging.info("Updated Mesh Configuration:\n" + msg)
|
||||||
@@ -911,7 +914,7 @@ class ProbeManager:
|
|||||||
for i in range(y_cnt):
|
for i in range(y_cnt):
|
||||||
for j in range(x_cnt):
|
for j in range(x_cnt):
|
||||||
if not i % 2:
|
if not i % 2:
|
||||||
# move in positive directon
|
# move in positive direction
|
||||||
pos_x = min_x + j * x_dist
|
pos_x = min_x + j * x_dist
|
||||||
else:
|
else:
|
||||||
# move in negative direction
|
# move in negative direction
|
||||||
@@ -1161,7 +1164,7 @@ class ProbeManager:
|
|||||||
|
|
||||||
def _gen_arc(self, origin, radius, start, step, count):
|
def _gen_arc(self, origin, radius, start, step, count):
|
||||||
end = start + step * count
|
end = start + step * count
|
||||||
# create a segent for every 3 degress of travel
|
# create a segent for every 3 degrees of travel
|
||||||
for angle in range(start, end, step):
|
for angle in range(start, end, step):
|
||||||
rad = math.radians(angle % 360)
|
rad = math.radians(angle % 360)
|
||||||
opp = math.sin(rad) * radius
|
opp = math.sin(rad) * radius
|
||||||
@@ -1271,7 +1274,7 @@ class MoveSplitter:
|
|||||||
self.z_offset = self._calc_z_offset(prev_pos)
|
self.z_offset = self._calc_z_offset(prev_pos)
|
||||||
self.traverse_complete = False
|
self.traverse_complete = False
|
||||||
self.distance_checked = 0.
|
self.distance_checked = 0.
|
||||||
axes_d = [self.next_pos[i] - self.prev_pos[i] for i in range(4)]
|
axes_d = [np - pp for np, pp in zip(self.next_pos, self.prev_pos)]
|
||||||
self.total_move_length = math.sqrt(sum([d*d for d in axes_d[:3]]))
|
self.total_move_length = math.sqrt(sum([d*d for d in axes_d[:3]]))
|
||||||
self.axis_move = [not isclose(d, 0., abs_tol=1e-10) for d in axes_d]
|
self.axis_move = [not isclose(d, 0., abs_tol=1e-10) for d in axes_d]
|
||||||
def _calc_z_offset(self, pos):
|
def _calc_z_offset(self, pos):
|
||||||
@@ -1284,7 +1287,7 @@ class MoveSplitter:
|
|||||||
raise self.gcode.error(
|
raise self.gcode.error(
|
||||||
"bed_mesh: Slice distance is negative "
|
"bed_mesh: Slice distance is negative "
|
||||||
"or greater than entire move length")
|
"or greater than entire move length")
|
||||||
for i in range(4):
|
for i in range(len(self.next_pos)):
|
||||||
if self.axis_move[i]:
|
if self.axis_move[i]:
|
||||||
self.current_pos[i] = lerp(
|
self.current_pos[i] = lerp(
|
||||||
t, self.prev_pos[i], self.next_pos[i])
|
t, self.prev_pos[i], self.next_pos[i])
|
||||||
@@ -1299,9 +1302,9 @@ class MoveSplitter:
|
|||||||
next_z = self._calc_z_offset(self.current_pos)
|
next_z = self._calc_z_offset(self.current_pos)
|
||||||
if abs(next_z - self.z_offset) >= self.split_delta_z:
|
if abs(next_z - self.z_offset) >= self.split_delta_z:
|
||||||
self.z_offset = next_z
|
self.z_offset = next_z
|
||||||
return self.current_pos[0], self.current_pos[1], \
|
newpos = list(self.current_pos)
|
||||||
self.current_pos[2] + self.z_offset, \
|
newpos[2] += self.z_offset
|
||||||
self.current_pos[3]
|
return newpos
|
||||||
# end of move reached
|
# end of move reached
|
||||||
self.current_pos[:] = self.next_pos
|
self.current_pos[:] = self.next_pos
|
||||||
self.z_offset = self._calc_z_offset(self.current_pos)
|
self.z_offset = self._calc_z_offset(self.current_pos)
|
||||||
|
|||||||
@@ -24,12 +24,14 @@ class BedTilt:
|
|||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
self.toolhead = self.printer.lookup_object('toolhead')
|
self.toolhead = self.printer.lookup_object('toolhead')
|
||||||
def get_position(self):
|
def get_position(self):
|
||||||
x, y, z, e = self.toolhead.get_position()
|
pos = self.toolhead.get_position()
|
||||||
return [x, y, z - x*self.x_adjust - y*self.y_adjust - self.z_adjust, e]
|
x, y, z = pos[:3]
|
||||||
|
z -= x*self.x_adjust + y*self.y_adjust + self.z_adjust
|
||||||
|
return [x, y, z] + pos[3:]
|
||||||
def move(self, newpos, speed):
|
def move(self, newpos, speed):
|
||||||
x, y, z, e = newpos
|
x, y, z = newpos[:3]
|
||||||
self.toolhead.move([x, y, z + x*self.x_adjust + y*self.y_adjust
|
z += x*self.x_adjust + y*self.y_adjust + self.z_adjust
|
||||||
+ self.z_adjust, e], speed)
|
self.toolhead.move([x, y, z] + newpos[3:], speed)
|
||||||
def update_adjust(self, x_adjust, y_adjust, z_adjust):
|
def update_adjust(self, x_adjust, y_adjust, z_adjust):
|
||||||
self.x_adjust = x_adjust
|
self.x_adjust = x_adjust
|
||||||
self.y_adjust = y_adjust
|
self.y_adjust = y_adjust
|
||||||
|
|||||||
@@ -64,7 +64,11 @@ class BLTouchProbe:
|
|||||||
self.cmd_helper = probe.ProbeCommandHelper(
|
self.cmd_helper = probe.ProbeCommandHelper(
|
||||||
config, self, self.mcu_endstop.query_endstop)
|
config, self, self.mcu_endstop.query_endstop)
|
||||||
self.probe_offsets = probe.ProbeOffsetsHelper(config)
|
self.probe_offsets = probe.ProbeOffsetsHelper(config)
|
||||||
self.probe_session = probe.ProbeSessionHelper(config, self)
|
self.param_helper = probe.ProbeParameterHelper(config)
|
||||||
|
self.homing_helper = probe.HomingViaProbeHelper(config, self,
|
||||||
|
self.param_helper)
|
||||||
|
self.probe_session = probe.ProbeSessionHelper(
|
||||||
|
config, self.param_helper, self.homing_helper.start_probe_session)
|
||||||
# Register BLTOUCH_DEBUG command
|
# Register BLTOUCH_DEBUG command
|
||||||
self.gcode = self.printer.lookup_object('gcode')
|
self.gcode = self.printer.lookup_object('gcode')
|
||||||
self.gcode.register_command("BLTOUCH_DEBUG", self.cmd_BLTOUCH_DEBUG,
|
self.gcode.register_command("BLTOUCH_DEBUG", self.cmd_BLTOUCH_DEBUG,
|
||||||
@@ -75,7 +79,7 @@ class BLTouchProbe:
|
|||||||
self.printer.register_event_handler("klippy:connect",
|
self.printer.register_event_handler("klippy:connect",
|
||||||
self.handle_connect)
|
self.handle_connect)
|
||||||
def get_probe_params(self, gcmd=None):
|
def get_probe_params(self, gcmd=None):
|
||||||
return self.probe_session.get_probe_params(gcmd)
|
return self.param_helper.get_probe_params(gcmd)
|
||||||
def get_offsets(self):
|
def get_offsets(self):
|
||||||
return self.probe_offsets.get_offsets()
|
return self.probe_offsets.get_offsets()
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
@@ -191,9 +195,6 @@ class BLTouchProbe:
|
|||||||
self.verify_raise_probe()
|
self.verify_raise_probe()
|
||||||
self.sync_print_time()
|
self.sync_print_time()
|
||||||
self.multi = 'OFF'
|
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):
|
def probe_prepare(self, hmove):
|
||||||
if self.multi == 'OFF' or self.multi == 'FIRST':
|
if self.multi == 'OFF' or self.multi == 'FIRST':
|
||||||
self.lower_probe()
|
self.lower_probe()
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ BMP180_REGS = {
|
|||||||
STATUS_MEASURING = 1 << 3
|
STATUS_MEASURING = 1 << 3
|
||||||
STATUS_IM_UPDATE = 1
|
STATUS_IM_UPDATE = 1
|
||||||
MODE = 1
|
MODE = 1
|
||||||
|
MODE_PERIODIC = 3
|
||||||
RUN_GAS = 1 << 4
|
RUN_GAS = 1 << 4
|
||||||
NB_CONV_0 = 0
|
NB_CONV_0 = 0
|
||||||
EAS_NEW_DATA = 1 << 7
|
EAS_NEW_DATA = 1 << 7
|
||||||
@@ -143,6 +144,7 @@ class BME280:
|
|||||||
pow(2, self.os_temp - 1), pow(2, self.os_hum - 1),
|
pow(2, self.os_temp - 1), pow(2, self.os_hum - 1),
|
||||||
pow(2, self.os_pres - 1)))
|
pow(2, self.os_pres - 1)))
|
||||||
logging.info("BMxx80: IIR: %dx" % (pow(2, self.iir_filter) - 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.temp = self.pressure = self.humidity = self.gas = self.t_fine = 0.
|
||||||
self.min_temp = self.max_temp = self.range_switching_error = 0.
|
self.min_temp = self.max_temp = self.range_switching_error = 0.
|
||||||
@@ -155,6 +157,7 @@ class BME280:
|
|||||||
return
|
return
|
||||||
self.printer.register_event_handler("klippy:connect",
|
self.printer.register_event_handler("klippy:connect",
|
||||||
self.handle_connect)
|
self.handle_connect)
|
||||||
|
self.last_gas_time = 0
|
||||||
|
|
||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
self._init_bmxx80()
|
self._init_bmxx80()
|
||||||
@@ -293,15 +296,15 @@ class BME280:
|
|||||||
status = self.read_register('STATUS', 1)[0]
|
status = self.read_register('STATUS', 1)[0]
|
||||||
|
|
||||||
if self.chip_type == 'BME680':
|
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.sample_timer = self.reactor.register_timer(self._sample_bme680)
|
||||||
self.chip_registers = BME680_REGS
|
self.chip_registers = BME680_REGS
|
||||||
elif self.chip_type == 'BMP180':
|
elif self.chip_type == 'BMP180':
|
||||||
self.max_sample_time = (1.25 + ((2.3 * self.os_pres) + .575)) / 1000
|
|
||||||
self.sample_timer = self.reactor.register_timer(self._sample_bmp180)
|
self.sample_timer = self.reactor.register_timer(self._sample_bmp180)
|
||||||
self.chip_registers = BMP180_REGS
|
self.chip_registers = BMP180_REGS
|
||||||
elif self.chip_type == 'BMP388':
|
elif self.chip_type == 'BMP388':
|
||||||
self.max_sample_time = 0.5
|
|
||||||
self.chip_registers = BMP388_REGS
|
self.chip_registers = BMP388_REGS
|
||||||
self.write_register(
|
self.write_register(
|
||||||
"PWR_CTRL",
|
"PWR_CTRL",
|
||||||
@@ -318,15 +321,18 @@ class BME280:
|
|||||||
self.write_register("INT_CTRL", [BMP388_REG_VAL_DRDY_EN])
|
self.write_register("INT_CTRL", [BMP388_REG_VAL_DRDY_EN])
|
||||||
|
|
||||||
self.sample_timer = self.reactor.register_timer(self._sample_bmp388)
|
self.sample_timer = self.reactor.register_timer(self._sample_bmp388)
|
||||||
else:
|
elif self.chip_type == 'BME280':
|
||||||
self.max_sample_time = \
|
self.max_sample_time = \
|
||||||
(1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + .575)
|
(1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + .575)
|
||||||
+ ((2.3 * self.os_hum) + .575)) / 1000
|
+ ((2.3 * self.os_hum) + .575)) / 1000
|
||||||
self.sample_timer = self.reactor.register_timer(self._sample_bme280)
|
self.sample_timer = self.reactor.register_timer(self._sample_bme280)
|
||||||
self.chip_registers = BME280_REGS
|
self.chip_registers = BME280_REGS
|
||||||
|
else:
|
||||||
if self.chip_type in ('BME680', 'BME280'):
|
self.max_sample_time = \
|
||||||
self.write_register('CONFIG', (self.iir_filter & 0x07) << 2)
|
(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
|
# Read out and calculate the trimming parameters
|
||||||
if self.chip_type == 'BMP180':
|
if self.chip_type == 'BMP180':
|
||||||
@@ -347,21 +353,64 @@ class BME280:
|
|||||||
elif self.chip_type == 'BMP388':
|
elif self.chip_type == 'BMP388':
|
||||||
self.dig = read_calibration_data_bmp388(cal_1)
|
self.dig = read_calibration_data_bmp388(cal_1)
|
||||||
|
|
||||||
def _sample_bme280(self, eventtime):
|
if self.chip_type in ('BME280', 'BMP280'):
|
||||||
# Enter forced mode
|
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':
|
if self.chip_type == 'BME280':
|
||||||
self.write_register('CTRL_HUM', self.os_hum)
|
self.write_register('CTRL_HUM', self.os_hum)
|
||||||
meas = self.os_temp << 5 | self.os_pres << 2 | MODE
|
# Enter normal (periodic) mode
|
||||||
|
meas = self.os_temp << 5 | self.os_pres << 2 | MODE_PERIODIC
|
||||||
self.write_register('CTRL_MEAS', meas)
|
self.write_register('CTRL_MEAS', meas)
|
||||||
|
|
||||||
try:
|
if self.chip_type == 'BME680':
|
||||||
# wait until results are ready
|
self.write_register('CONFIG', self.iir_filter << 2)
|
||||||
status = self.read_register('STATUS', 1)[0]
|
# Should be set once and reused on every mode register write
|
||||||
while status & STATUS_MEASURING:
|
self.write_register('CTRL_HUM', self.os_hum & 0x07)
|
||||||
self.reactor.pause(
|
gas_wait_0 = self._calc_gas_heater_duration(self.gas_heat_duration)
|
||||||
self.reactor.monotonic() + self.max_sample_time)
|
self.write_register('GAS_WAIT_0', [gas_wait_0])
|
||||||
status = self.read_register('STATUS', 1)[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):
|
||||||
|
# In normal mode data shadowing is performed
|
||||||
|
# So reading can be done while measurements are in process
|
||||||
|
try:
|
||||||
if self.chip_type == 'BME280':
|
if self.chip_type == 'BME280':
|
||||||
data = self.read_register('PRESSURE_MSB', 8)
|
data = self.read_register('PRESSURE_MSB', 8)
|
||||||
elif self.chip_type == 'BMP280':
|
elif self.chip_type == 'BMP280':
|
||||||
@@ -462,35 +511,39 @@ class BME280:
|
|||||||
return comp_press
|
return comp_press
|
||||||
|
|
||||||
def _sample_bme680(self, eventtime):
|
def _sample_bme680(self, eventtime):
|
||||||
self.write_register('CTRL_HUM', self.os_hum & 0x07)
|
def data_ready(stat, run_gas):
|
||||||
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):
|
|
||||||
new_data = (stat & EAS_NEW_DATA)
|
new_data = (stat & EAS_NEW_DATA)
|
||||||
gas_done = not (stat & GAS_DONE)
|
gas_done = not (stat & GAS_DONE)
|
||||||
meas_done = not (stat & MEASURE_DONE)
|
meas_done = not (stat & MEASURE_DONE)
|
||||||
|
if not run_gas:
|
||||||
|
gas_done = True
|
||||||
return new_data and gas_done and meas_done
|
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
|
# Enter forced mode
|
||||||
meas = meas | MODE
|
meas = self.os_temp << 5 | self.os_pres << 2 | MODE
|
||||||
self.write_register('CTRL_MEAS', meas)
|
self.write_register('CTRL_MEAS', meas)
|
||||||
|
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:
|
try:
|
||||||
# wait until results are ready
|
# wait until results are ready
|
||||||
status = self.read_register('EAS_STATUS_0', 1)[0]
|
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.pause(
|
||||||
self.reactor.monotonic() + self.max_sample_time)
|
self.reactor.monotonic() + self.max_sample_time)
|
||||||
status = self.read_register('EAS_STATUS_0', 1)[0]
|
status = self.read_register('EAS_STATUS_0', 1)[0]
|
||||||
|
|
||||||
data = self.read_register('PRESSURE_MSB', 8)
|
data = self.read_register('PRESSURE_MSB', 8)
|
||||||
|
gas_data = [0, 0]
|
||||||
|
if run_gas:
|
||||||
gas_data = self.read_register('GAS_R_MSB', 2)
|
gas_data = self.read_register('GAS_R_MSB', 2)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("BME680: Error reading data")
|
logging.exception("BME680: Error reading data")
|
||||||
@@ -515,6 +568,10 @@ class BME280:
|
|||||||
gas_raw = (gas_data[0] << 2) | ((gas_data[1] & 0xC0) >> 6)
|
gas_raw = (gas_data[0] << 2) | ((gas_data[1] & 0xC0) >> 6)
|
||||||
gas_range = (gas_data[1] & 0x0F)
|
gas_range = (gas_data[1] & 0x0F)
|
||||||
self.gas = self._compensate_gas(gas_raw, gas_range)
|
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:
|
if self.temp < self.min_temp or self.temp > self.max_temp:
|
||||||
self.printer.invoke_shutdown(
|
self.printer.invoke_shutdown(
|
||||||
@@ -643,7 +700,7 @@ class BME280:
|
|||||||
gas_raw - 512. + var1)
|
gas_raw - 512. + var1)
|
||||||
return gas
|
return gas
|
||||||
|
|
||||||
def _calculate_gas_heater_resistance(self, target_temp):
|
def _calc_gas_heater_resistance(self, target_temp):
|
||||||
amb_temp = self.temp
|
amb_temp = self.temp
|
||||||
heater_data = self.read_register('RES_HEAT_VAL', 3)
|
heater_data = self.read_register('RES_HEAT_VAL', 3)
|
||||||
res_heat_val = get_signed_byte(heater_data[0])
|
res_heat_val = get_signed_byte(heater_data[0])
|
||||||
@@ -658,7 +715,7 @@ class BME280:
|
|||||||
* (1. / (1. + (res_heat_val * 0.002)))) - 25))
|
* (1. / (1. + (res_heat_val * 0.002)))) - 25))
|
||||||
return int(res_heat)
|
return int(res_heat)
|
||||||
|
|
||||||
def _calculate_gas_heater_duration(self, duration_ms):
|
def _calc_gas_heater_duration(self, duration_ms):
|
||||||
if duration_ms >= 4032:
|
if duration_ms >= 4032:
|
||||||
duration_reg = 0xff
|
duration_reg = 0xff
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class MCU_SPI:
|
|||||||
cs_active_high=False):
|
cs_active_high=False):
|
||||||
self.mcu = mcu
|
self.mcu = mcu
|
||||||
self.bus = bus
|
self.bus = bus
|
||||||
|
self.speed = speed
|
||||||
# Config SPI object (set all CS pins high before spi_set_bus commands)
|
# Config SPI object (set all CS pins high before spi_set_bus commands)
|
||||||
self.oid = mcu.create_oid()
|
self.oid = mcu.create_oid()
|
||||||
if pin is None:
|
if pin is None:
|
||||||
@@ -51,11 +52,17 @@ class MCU_SPI:
|
|||||||
mcu.add_config_cmd("config_spi oid=%d pin=%s cs_active_high=%d"
|
mcu.add_config_cmd("config_spi oid=%d pin=%s cs_active_high=%d"
|
||||||
% (self.oid, pin, cs_active_high))
|
% (self.oid, pin, cs_active_high))
|
||||||
# Generate SPI bus config message
|
# Generate SPI bus config message
|
||||||
|
self.config_fmt_ticks = None
|
||||||
if sw_pins is not None:
|
if sw_pins is not None:
|
||||||
self.config_fmt = (
|
self.config_fmt = (
|
||||||
"spi_set_software_bus oid=%d"
|
"spi_set_software_bus oid=%d"
|
||||||
" miso_pin=%s mosi_pin=%s sclk_pin=%s mode=%d rate=%d"
|
" miso_pin=%s mosi_pin=%s sclk_pin=%s mode=%d rate=%d"
|
||||||
% (self.oid, sw_pins[0], sw_pins[1], sw_pins[2], mode, speed))
|
% (self.oid, sw_pins[0], sw_pins[1], sw_pins[2], mode, speed))
|
||||||
|
self.config_fmt_ticks = (
|
||||||
|
"spi_set_sw_bus oid=%d"
|
||||||
|
" miso_pin=%s mosi_pin=%s sclk_pin=%s mode=%d pulse_ticks=%%d"
|
||||||
|
% (self.oid, sw_pins[0], sw_pins[1],
|
||||||
|
sw_pins[2], mode))
|
||||||
else:
|
else:
|
||||||
self.config_fmt = (
|
self.config_fmt = (
|
||||||
"spi_set_bus oid=%d spi_bus=%%s mode=%d rate=%d"
|
"spi_set_bus oid=%d spi_bus=%%s mode=%d rate=%d"
|
||||||
@@ -78,6 +85,12 @@ class MCU_SPI:
|
|||||||
if '%' in self.config_fmt:
|
if '%' in self.config_fmt:
|
||||||
bus = resolve_bus_name(self.mcu, "spi_bus", self.bus)
|
bus = resolve_bus_name(self.mcu, "spi_bus", self.bus)
|
||||||
self.config_fmt = self.config_fmt % (bus,)
|
self.config_fmt = self.config_fmt % (bus,)
|
||||||
|
if self.config_fmt_ticks:
|
||||||
|
if self.mcu.try_lookup_command("spi_set_sw_bus oid=%c miso_pin=%u "
|
||||||
|
"mosi_pin=%u sclk_pin=%u "
|
||||||
|
"mode=%u pulse_ticks=%u"):
|
||||||
|
pulse_ticks = self.mcu.seconds_to_clock(1./self.speed)
|
||||||
|
self.config_fmt = self.config_fmt_ticks % (pulse_ticks,)
|
||||||
self.mcu.add_config_cmd(self.config_fmt)
|
self.mcu.add_config_cmd(self.config_fmt)
|
||||||
self.spi_send_cmd = self.mcu.lookup_command(
|
self.spi_send_cmd = self.mcu.lookup_command(
|
||||||
"spi_send oid=%c data=%*s", cq=self.cmd_queue)
|
"spi_send oid=%c data=%*s", cq=self.cmd_queue)
|
||||||
@@ -147,6 +160,8 @@ class MCU_I2C:
|
|||||||
self.bus = bus
|
self.bus = bus
|
||||||
self.i2c_address = addr
|
self.i2c_address = addr
|
||||||
self.oid = self.mcu.create_oid()
|
self.oid = self.mcu.create_oid()
|
||||||
|
self.speed = speed
|
||||||
|
self.config_fmt_ticks = None
|
||||||
mcu.add_config_cmd("config_i2c oid=%d" % (self.oid,))
|
mcu.add_config_cmd("config_i2c oid=%d" % (self.oid,))
|
||||||
# Generate I2C bus config message
|
# Generate I2C bus config message
|
||||||
if sw_pins is not None:
|
if sw_pins is not None:
|
||||||
@@ -154,13 +169,24 @@ class MCU_I2C:
|
|||||||
"i2c_set_software_bus oid=%d"
|
"i2c_set_software_bus oid=%d"
|
||||||
" scl_pin=%s sda_pin=%s rate=%d address=%d"
|
" scl_pin=%s sda_pin=%s rate=%d address=%d"
|
||||||
% (self.oid, sw_pins[0], sw_pins[1], speed, addr))
|
% (self.oid, sw_pins[0], sw_pins[1], speed, addr))
|
||||||
|
self.config_fmt_ticks = (
|
||||||
|
"i2c_set_sw_bus oid=%d"
|
||||||
|
" scl_pin=%s sda_pin=%s pulse_ticks=%%d address=%d"
|
||||||
|
% (self.oid, sw_pins[0], sw_pins[1], addr))
|
||||||
else:
|
else:
|
||||||
self.config_fmt = (
|
self.config_fmt = (
|
||||||
"i2c_set_bus oid=%d i2c_bus=%%s rate=%d address=%d"
|
"i2c_set_bus oid=%d i2c_bus=%%s rate=%d address=%d"
|
||||||
% (self.oid, speed, addr))
|
% (self.oid, speed, addr))
|
||||||
self.cmd_queue = self.mcu.alloc_command_queue()
|
self.cmd_queue = self.mcu.alloc_command_queue()
|
||||||
self.mcu.register_config_callback(self.build_config)
|
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
|
||||||
|
printer = self.mcu.get_printer()
|
||||||
|
printer.register_event_handler("klippy:connect", self._handle_connect)
|
||||||
|
# backward support i2c_write inside the init section
|
||||||
|
self._to_write = []
|
||||||
|
def _handle_connect(self):
|
||||||
|
for data in self._to_write:
|
||||||
|
self.i2c_write(data)
|
||||||
def get_oid(self):
|
def get_oid(self):
|
||||||
return self.oid
|
return self.oid
|
||||||
def get_mcu(self):
|
def get_mcu(self):
|
||||||
@@ -173,6 +199,12 @@ class MCU_I2C:
|
|||||||
if '%' in self.config_fmt:
|
if '%' in self.config_fmt:
|
||||||
bus = resolve_bus_name(self.mcu, "i2c_bus", self.bus)
|
bus = resolve_bus_name(self.mcu, "i2c_bus", self.bus)
|
||||||
self.config_fmt = self.config_fmt % (bus,)
|
self.config_fmt = self.config_fmt % (bus,)
|
||||||
|
if self.config_fmt_ticks:
|
||||||
|
if self.mcu.try_lookup_command("i2c_set_sw_bus oid=%c"
|
||||||
|
" scl_pin=%u sda_pin=%u"
|
||||||
|
" pulse_ticks=%u address=%u"):
|
||||||
|
pulse_ticks = self.mcu.seconds_to_clock(1./self.speed/2)
|
||||||
|
self.config_fmt = self.config_fmt_ticks % (pulse_ticks,)
|
||||||
self.mcu.add_config_cmd(self.config_fmt)
|
self.mcu.add_config_cmd(self.config_fmt)
|
||||||
self.i2c_write_cmd = self.mcu.lookup_command(
|
self.i2c_write_cmd = self.mcu.lookup_command(
|
||||||
"i2c_write oid=%c data=%*s", cq=self.cmd_queue)
|
"i2c_write oid=%c data=%*s", cq=self.cmd_queue)
|
||||||
@@ -180,36 +212,14 @@ class MCU_I2C:
|
|||||||
"i2c_read oid=%c reg=%*s read_len=%u",
|
"i2c_read oid=%c reg=%*s read_len=%u",
|
||||||
"i2c_read_response oid=%c response=%*s", oid=self.oid,
|
"i2c_read_response oid=%c response=%*s", oid=self.oid,
|
||||||
cq=self.cmd_queue)
|
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):
|
def i2c_write(self, data, minclock=0, reqclock=0):
|
||||||
if self.i2c_write_cmd is None:
|
if self.i2c_write_cmd is None:
|
||||||
# Send setup message via mcu initialization
|
self._to_write.append(data)
|
||||||
data_msg = "".join(["%02x" % (x,) for x in data])
|
|
||||||
self.mcu.add_config_cmd("i2c_write oid=%d data=%s" % (
|
|
||||||
self.oid, data_msg), is_init=True)
|
|
||||||
return
|
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],
|
self.i2c_write_cmd.send_wait_ack([self.oid, data],
|
||||||
minclock=minclock, reqclock=reqclock)
|
minclock=minclock, reqclock=reqclock)
|
||||||
def i2c_read(self, write, read_len):
|
def i2c_read(self, write, read_len, retry=True):
|
||||||
return self.i2c_read_cmd.send([self.oid, write, read_len])
|
return self.i2c_read_cmd.send([self.oid, write, read_len], retry)
|
||||||
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):
|
def MCU_I2C_from_config(config, default_addr=None, default_speed=100000):
|
||||||
# Load bus parameters
|
# Load bus parameters
|
||||||
|
|||||||
@@ -244,6 +244,33 @@ class HalfStepRotaryEncoder(BaseRotaryEncoder):
|
|||||||
BaseRotaryEncoder.R_START | BaseRotaryEncoder.R_DIR_CCW),
|
BaseRotaryEncoder.R_START | BaseRotaryEncoder.R_DIR_CCW),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class DebounceButton:
|
||||||
|
def __init__(self, config, button_action):
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
self.reactor = self.printer.get_reactor()
|
||||||
|
self.button_action = button_action
|
||||||
|
self.debounce_delay = config.getfloat('debounce_delay', 0., minval=0.)
|
||||||
|
self.logical_state = None
|
||||||
|
self.physical_state = None
|
||||||
|
self.latest_eventtime = None
|
||||||
|
def button_handler(self, eventtime, state):
|
||||||
|
self.physical_state = state
|
||||||
|
self.latest_eventtime = eventtime
|
||||||
|
# if there would be no state transition, ignore the event:
|
||||||
|
if self.logical_state == self.physical_state:
|
||||||
|
return
|
||||||
|
trigger_time = eventtime + self.debounce_delay
|
||||||
|
self.reactor.register_callback(self._debounce_event, trigger_time)
|
||||||
|
def _debounce_event(self, eventtime):
|
||||||
|
# if there would be no state transition, ignore the event:
|
||||||
|
if self.logical_state == self.physical_state:
|
||||||
|
return
|
||||||
|
# if there were more recent events, they supersede this one:
|
||||||
|
if (eventtime - self.debounce_delay) < self.latest_eventtime:
|
||||||
|
return
|
||||||
|
# enact state transition and trigger action
|
||||||
|
self.logical_state = self.physical_state
|
||||||
|
self.button_action(self.latest_eventtime, self.logical_state)
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Button registration code
|
# Button registration code
|
||||||
@@ -261,6 +288,14 @@ class PrinterButtons:
|
|||||||
self.adc_buttons[pin] = adc_buttons = MCU_ADC_buttons(
|
self.adc_buttons[pin] = adc_buttons = MCU_ADC_buttons(
|
||||||
self.printer, pin, pullup)
|
self.printer, pin, pullup)
|
||||||
adc_buttons.setup_button(min_val, max_val, callback)
|
adc_buttons.setup_button(min_val, max_val, callback)
|
||||||
|
def register_debounce_button(self, pin, callback, config):
|
||||||
|
debounce = DebounceButton(config, callback)
|
||||||
|
return self.register_buttons([pin], debounce.button_handler)
|
||||||
|
def register_debounce_adc_button(self, pin, min_val, max_val, pullup
|
||||||
|
, callback, config):
|
||||||
|
debounce = DebounceButton(config, callback)
|
||||||
|
return self.register_adc_button(pin, min_val, max_val, pullup
|
||||||
|
, debounce.button_handler)
|
||||||
def register_adc_button_push(self, pin, min_val, max_val, pullup, callback):
|
def register_adc_button_push(self, pin, min_val, max_val, pullup, callback):
|
||||||
def helper(eventtime, state, callback=callback):
|
def helper(eventtime, state, callback=callback):
|
||||||
if state:
|
if state:
|
||||||
|
|||||||
80
klippy/extras/canbus_stats.py
Normal file
80
klippy/extras/canbus_stats.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Report canbus connection status
|
||||||
|
#
|
||||||
|
# Copyright (C) 2025 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
import logging
|
||||||
|
|
||||||
|
class PrinterCANBusStats:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
self.reactor = self.printer.get_reactor()
|
||||||
|
self.name = config.get_name().split()[-1]
|
||||||
|
self.mcu = None
|
||||||
|
self.get_canbus_status_cmd = None
|
||||||
|
self.status = {'rx_error': None, 'tx_error': None, 'tx_retries': None,
|
||||||
|
'bus_state': None}
|
||||||
|
self.printer.register_event_handler("klippy:connect",
|
||||||
|
self.handle_connect)
|
||||||
|
self.printer.register_event_handler("klippy:shutdown",
|
||||||
|
self.handle_shutdown)
|
||||||
|
def handle_shutdown(self):
|
||||||
|
status = self.status.copy()
|
||||||
|
if status['bus_state'] is not None:
|
||||||
|
# Clear bus_state on shutdown to note that the values may be stale
|
||||||
|
status['bus_state'] = 'unknown'
|
||||||
|
self.status = status
|
||||||
|
def handle_connect(self):
|
||||||
|
# Lookup mcu
|
||||||
|
mcu_name = self.name
|
||||||
|
if mcu_name != 'mcu':
|
||||||
|
mcu_name = 'mcu ' + mcu_name
|
||||||
|
self.mcu = self.printer.lookup_object(mcu_name)
|
||||||
|
# Lookup status query command
|
||||||
|
if self.mcu.try_lookup_command("get_canbus_status") is None:
|
||||||
|
return
|
||||||
|
self.get_canbus_status_cmd = self.mcu.lookup_query_command(
|
||||||
|
"get_canbus_status",
|
||||||
|
"canbus_status rx_error=%u tx_error=%u tx_retries=%u"
|
||||||
|
" canbus_bus_state=%u")
|
||||||
|
# Register usb_canbus_state message handling (for usb to canbus bridge)
|
||||||
|
self.mcu.register_response(self.handle_usb_canbus_state,
|
||||||
|
"usb_canbus_state")
|
||||||
|
# Register periodic query timer
|
||||||
|
self.reactor.register_timer(self.query_event, self.reactor.NOW)
|
||||||
|
def handle_usb_canbus_state(self, params):
|
||||||
|
discard = params['discard']
|
||||||
|
if discard:
|
||||||
|
logging.warning("USB CANBUS bridge '%s' is discarding!"
|
||||||
|
% (self.name,))
|
||||||
|
else:
|
||||||
|
logging.warning("USB CANBUS bridge '%s' is no longer discarding."
|
||||||
|
% (self.name,))
|
||||||
|
def query_event(self, eventtime):
|
||||||
|
prev_rx = self.status['rx_error']
|
||||||
|
prev_tx = self.status['tx_error']
|
||||||
|
prev_retries = self.status['tx_retries']
|
||||||
|
if prev_rx is None:
|
||||||
|
prev_rx = prev_tx = prev_retries = 0
|
||||||
|
params = self.get_canbus_status_cmd.send()
|
||||||
|
rx = prev_rx + ((params['rx_error'] - prev_rx) & 0xffffffff)
|
||||||
|
tx = prev_tx + ((params['tx_error'] - prev_tx) & 0xffffffff)
|
||||||
|
retries = prev_retries + ((params['tx_retries'] - prev_retries)
|
||||||
|
& 0xffffffff)
|
||||||
|
state = params['canbus_bus_state']
|
||||||
|
self.status = {'rx_error': rx, 'tx_error': tx, 'tx_retries': retries,
|
||||||
|
'bus_state': state}
|
||||||
|
return self.reactor.monotonic() + 1.
|
||||||
|
def stats(self, eventtime):
|
||||||
|
status = self.status
|
||||||
|
if status['rx_error'] is None:
|
||||||
|
return (False, '')
|
||||||
|
return (False, 'canstat_%s: bus_state=%s rx_error=%d'
|
||||||
|
' tx_error=%d tx_retries=%d'
|
||||||
|
% (self.name, status['bus_state'], status['rx_error'],
|
||||||
|
status['tx_error'], status['tx_retries']))
|
||||||
|
def get_status(self, eventtime):
|
||||||
|
return self.status
|
||||||
|
|
||||||
|
def load_config_prefix(config):
|
||||||
|
return PrinterCANBusStats(config)
|
||||||
@@ -62,9 +62,7 @@ class ControllerFan:
|
|||||||
self.last_on += 1
|
self.last_on += 1
|
||||||
if speed != self.last_speed:
|
if speed != self.last_speed:
|
||||||
self.last_speed = speed
|
self.last_speed = speed
|
||||||
curtime = self.printer.get_reactor().monotonic()
|
self.fan.set_speed(speed)
|
||||||
print_time = self.fan.get_mcu().estimated_print_time(curtime)
|
|
||||||
self.fan.set_speed(print_time + PIN_MIN_TIME, speed)
|
|
||||||
return eventtime + 1.
|
return eventtime + 1.
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ def load_config_prefix(config):
|
|||||||
if not config.has_section('display'):
|
if not config.has_section('display'):
|
||||||
raise config.error(
|
raise config.error(
|
||||||
"A primary [display] section must be defined in printer.cfg "
|
"A primary [display] section must be defined in printer.cfg "
|
||||||
"to use auxilary displays")
|
"to use auxiliary displays")
|
||||||
name = config.get_name().split()[-1]
|
name = config.get_name().split()[-1]
|
||||||
if name == "display":
|
if name == "display":
|
||||||
raise config.error(
|
raise config.error(
|
||||||
|
|||||||
209
klippy/extras/display/aip31068_spi.py
Normal file
209
klippy/extras/display/aip31068_spi.py
Normal 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)
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import logging, os, ast
|
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
|
# Normal time between each screen redraw
|
||||||
REDRAW_TIME = 0.500
|
REDRAW_TIME = 0.500
|
||||||
@@ -17,7 +17,8 @@ LCD_chips = {
|
|||||||
'st7920': st7920.ST7920, 'emulated_st7920': st7920.EmulatedST7920,
|
'st7920': st7920.ST7920, 'emulated_st7920': st7920.EmulatedST7920,
|
||||||
'hd44780': hd44780.HD44780, 'uc1701': uc1701.UC1701,
|
'hd44780': hd44780.HD44780, 'uc1701': uc1701.UC1701,
|
||||||
'ssd1306': uc1701.SSD1306, 'sh1106': uc1701.SH1106,
|
'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
|
# Storage of [display_template my_template] config sections
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
# ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
|
# ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
|
||||||
# (c) Joseph Gil
|
# (c) Joseph Gil
|
||||||
#
|
#
|
||||||
# Indivdual fonts are public domain
|
# Individual fonts are public domain
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
VGA_FONT = [
|
VGA_FONT = [
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# Support for "dotstar" leds
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
from . import bus
|
from . import bus, led
|
||||||
|
|
||||||
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
||||||
|
|
||||||
@@ -22,8 +22,7 @@ class PrinterDotstar:
|
|||||||
self.spi = bus.MCU_SPI(mcu, None, None, 0, 500000, sw_spi_pins)
|
self.spi = bus.MCU_SPI(mcu, None, None, 0, 500000, sw_spi_pins)
|
||||||
# Initialize color data
|
# Initialize color data
|
||||||
self.chain_count = config.getint('chain_count', 1, minval=1)
|
self.chain_count = config.getint('chain_count', 1, minval=1)
|
||||||
pled = printer.load_object(config, "led")
|
self.led_helper = led.LEDHelper(config, self.update_leds,
|
||||||
self.led_helper = pled.setup_helper(config, self.update_leds,
|
|
||||||
self.chain_count)
|
self.chain_count)
|
||||||
self.prev_data = None
|
self.prev_data = None
|
||||||
# Register commands
|
# Register commands
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class PhaseCalc:
|
|||||||
class EndstopPhase:
|
class EndstopPhase:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
self.name = config.get_name().split()[1]
|
self.name = " ".join(config.get_name().split()[1:])
|
||||||
# Obtain step_distance and microsteps from stepper config section
|
# Obtain step_distance and microsteps from stepper config section
|
||||||
sconfig = config.getsection(self.name)
|
sconfig = config.getsection(self.name)
|
||||||
rotation_dist, steps_per_rotation = stepper.parse_step_distance(sconfig)
|
rotation_dist, steps_per_rotation = stepper.parse_step_distance(sconfig)
|
||||||
@@ -118,7 +118,7 @@ class EndstopPhase:
|
|||||||
return delta * self.step_dist
|
return delta * self.step_dist
|
||||||
def handle_home_rails_end(self, homing_state, rails):
|
def handle_home_rails_end(self, homing_state, rails):
|
||||||
for rail in rails:
|
for rail in rails:
|
||||||
stepper = rail.get_steppers()[0]
|
stepper = rail.get_endstops()[0][0].get_steppers()[0]
|
||||||
if stepper.get_name() == self.name:
|
if stepper.get_name() == self.name:
|
||||||
trig_mcu_pos = homing_state.get_trigger_position(self.name)
|
trig_mcu_pos = homing_state.get_trigger_position(self.name)
|
||||||
align = self.align_endstop(rail)
|
align = self.align_endstop(rail)
|
||||||
|
|||||||
@@ -82,24 +82,25 @@ class ExcludeObject:
|
|||||||
self._reset_state()
|
self._reset_state()
|
||||||
self._unregister_transform()
|
self._unregister_transform()
|
||||||
|
|
||||||
def _get_extrusion_offsets(self):
|
def _get_extrusion_offsets(self, num_coord):
|
||||||
offset = self.extrusion_offsets.get(
|
ename = self.toolhead.get_extruder().get_name()
|
||||||
self.toolhead.get_extruder().get_name())
|
offset = self.extrusion_offsets.get(ename)
|
||||||
if offset is None:
|
if offset is None:
|
||||||
offset = [0., 0., 0., 0.]
|
offset = [0.] * num_coord
|
||||||
self.extrusion_offsets[self.toolhead.get_extruder().get_name()] = \
|
self.extrusion_offsets[ename] = offset
|
||||||
offset
|
if len(offset) < num_coord:
|
||||||
|
offset.extend([0.] * (len(num_coord) - len(offset)))
|
||||||
return offset
|
return offset
|
||||||
|
|
||||||
def get_position(self):
|
def get_position(self):
|
||||||
offset = self._get_extrusion_offsets()
|
|
||||||
pos = self.next_transform.get_position()
|
pos = self.next_transform.get_position()
|
||||||
for i in range(4):
|
offset = self._get_extrusion_offsets(len(pos))
|
||||||
|
for i in range(len(pos)):
|
||||||
self.last_position[i] = pos[i] + offset[i]
|
self.last_position[i] = pos[i] + offset[i]
|
||||||
return list(self.last_position)
|
return list(self.last_position)
|
||||||
|
|
||||||
def _normal_move(self, newpos, speed):
|
def _normal_move(self, newpos, speed):
|
||||||
offset = self._get_extrusion_offsets()
|
offset = self._get_extrusion_offsets(len(newpos))
|
||||||
|
|
||||||
if self.initial_extrusion_moves > 0 and \
|
if self.initial_extrusion_moves > 0 and \
|
||||||
self.last_position[3] != newpos[3]:
|
self.last_position[3] != newpos[3]:
|
||||||
@@ -122,9 +123,9 @@ class ExcludeObject:
|
|||||||
if (offset[0] != 0 or offset[1] != 0) and \
|
if (offset[0] != 0 or offset[1] != 0) and \
|
||||||
(newpos[0] != self.last_position_excluded[0] or \
|
(newpos[0] != self.last_position_excluded[0] or \
|
||||||
newpos[1] != self.last_position_excluded[1]):
|
newpos[1] != self.last_position_excluded[1]):
|
||||||
offset[0] = 0
|
for i in range(len(newpos)):
|
||||||
offset[1] = 0
|
if i != 3:
|
||||||
offset[2] = 0
|
offset[i] = 0
|
||||||
offset[3] += self.extruder_adj
|
offset[3] += self.extruder_adj
|
||||||
self.extruder_adj = 0
|
self.extruder_adj = 0
|
||||||
|
|
||||||
@@ -137,13 +138,14 @@ class ExcludeObject:
|
|||||||
self.extruder_adj = 0
|
self.extruder_adj = 0
|
||||||
|
|
||||||
tx_pos = newpos[:]
|
tx_pos = newpos[:]
|
||||||
for i in range(4):
|
for i in range(len(newpos)):
|
||||||
tx_pos[i] = newpos[i] - offset[i]
|
tx_pos[i] = newpos[i] - offset[i]
|
||||||
self.next_transform.move(tx_pos, speed)
|
self.next_transform.move(tx_pos, speed)
|
||||||
|
|
||||||
def _ignore_move(self, newpos, speed):
|
def _ignore_move(self, newpos, speed):
|
||||||
offset = self._get_extrusion_offsets()
|
offset = self._get_extrusion_offsets(len(newpos))
|
||||||
for i in range(3):
|
for i in range(len(newpos)):
|
||||||
|
if i != 3:
|
||||||
offset[i] = newpos[i] - self.last_position_extruded[i]
|
offset[i] = newpos[i] - self.last_position_extruded[i]
|
||||||
offset[3] = offset[3] + newpos[3] - self.last_position[3]
|
offset[3] = offset[3] + newpos[3] - self.last_position[3]
|
||||||
self.last_position[:] = newpos
|
self.last_position[:] = newpos
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
# Printer cooling fan
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
from . import pulse_counter
|
from . import pulse_counter, output_pin
|
||||||
|
|
||||||
FAN_MIN_TIME = 0.100
|
|
||||||
|
|
||||||
class Fan:
|
class Fan:
|
||||||
def __init__(self, config, default_shutdown_speed=0.):
|
def __init__(self, config, default_shutdown_speed=0.):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
self.last_fan_value = 0.
|
self.last_fan_value = self.last_req_value = 0.
|
||||||
self.last_fan_time = 0.
|
|
||||||
# Read config
|
# Read config
|
||||||
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
|
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
|
||||||
self.kick_start_time = config.getfloat('kick_start_time', 0.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 = ppins.setup_pin('digital_out', enable_pin)
|
||||||
self.enable_pin.setup_max_duration(0.)
|
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
|
# Setup tachometer
|
||||||
self.tachometer = FanTachometer(config)
|
self.tachometer = FanTachometer(config)
|
||||||
|
|
||||||
@@ -45,37 +46,37 @@ class Fan:
|
|||||||
|
|
||||||
def get_mcu(self):
|
def get_mcu(self):
|
||||||
return self.mcu_fan.get_mcu()
|
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:
|
if value < self.off_below:
|
||||||
value = 0.
|
value = 0.
|
||||||
value = max(0., min(self.max_power, value * self.max_power))
|
value = max(0., min(self.max_power, value * self.max_power))
|
||||||
if value == self.last_fan_value:
|
if value == self.last_fan_value:
|
||||||
return
|
return "discard", 0.
|
||||||
print_time = max(self.last_fan_time + FAN_MIN_TIME, print_time)
|
|
||||||
if self.enable_pin:
|
if self.enable_pin:
|
||||||
if value > 0 and self.last_fan_value == 0:
|
if value > 0 and self.last_fan_value == 0:
|
||||||
self.enable_pin.set_digital(print_time, 1)
|
self.enable_pin.set_digital(print_time, 1)
|
||||||
elif value == 0 and self.last_fan_value > 0:
|
elif value == 0 and self.last_fan_value > 0:
|
||||||
self.enable_pin.set_digital(print_time, 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)):
|
and (not self.last_fan_value or value - self.last_fan_value > .5)):
|
||||||
# Run fan at full speed for specified kick_start_time
|
# 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)
|
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.mcu_fan.set_pwm(print_time, value)
|
||||||
self.last_fan_time = print_time
|
def set_speed(self, value, print_time=None):
|
||||||
self.last_fan_value = value
|
self.gcrq.send_async_request(value, print_time)
|
||||||
def set_speed_from_command(self, value):
|
def set_speed_from_command(self, value):
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
self.gcrq.queue_gcode_request(value)
|
||||||
toolhead.register_lookahead_callback((lambda pt:
|
|
||||||
self.set_speed(pt, value)))
|
|
||||||
def _handle_request_restart(self, print_time):
|
def _handle_request_restart(self, print_time):
|
||||||
self.set_speed(print_time, 0.)
|
self.set_speed(0., print_time)
|
||||||
|
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
tachometer_status = self.tachometer.get_status(eventtime)
|
tachometer_status = self.tachometer.get_status(eventtime)
|
||||||
return {
|
return {
|
||||||
'speed': self.last_fan_value,
|
'speed': self.last_req_value,
|
||||||
'rpm': tachometer_status['rpm'],
|
'rpm': tachometer_status['rpm'],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
# Support fans that are controlled by gcode
|
# 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.
|
# 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:
|
class PrinterFanGeneric:
|
||||||
cmd_SET_FAN_SPEED_help = "Sets the speed of a fan"
|
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 = fan.Fan(config, default_shutdown_speed=0.)
|
||||||
self.fan_name = config.get_name().split()[-1]
|
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 = self.printer.lookup_object("gcode")
|
||||||
gcode.register_mux_command("SET_FAN_SPEED", "FAN",
|
gcode.register_mux_command("SET_FAN_SPEED", "FAN",
|
||||||
self.fan_name,
|
self.fan_name,
|
||||||
@@ -20,8 +24,22 @@ class PrinterFanGeneric:
|
|||||||
|
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
return self.fan.get_status(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")
|
||||||
|
value = 0.
|
||||||
|
self.fan.set_speed(value)
|
||||||
def cmd_SET_FAN_SPEED(self, gcmd):
|
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)
|
self.fan.set_speed_from_command(speed)
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class EncoderSensor:
|
|||||||
def _extruder_pos_update_event(self, eventtime):
|
def _extruder_pos_update_event(self, eventtime):
|
||||||
extruder_pos = self._get_extruder_pos(eventtime)
|
extruder_pos = self._get_extruder_pos(eventtime)
|
||||||
# Check for filament runout
|
# Check for filament runout
|
||||||
self.runout_helper.note_filament_present(
|
self.runout_helper.note_filament_present(eventtime,
|
||||||
extruder_pos < self.filament_runout_pos)
|
extruder_pos < self.filament_runout_pos)
|
||||||
return eventtime + CHECK_RUNOUT_TIMEOUT
|
return eventtime + CHECK_RUNOUT_TIMEOUT
|
||||||
def encoder_event(self, eventtime, state):
|
def encoder_event(self, eventtime, state):
|
||||||
@@ -71,7 +71,7 @@ class EncoderSensor:
|
|||||||
self._update_filament_runout_pos(eventtime)
|
self._update_filament_runout_pos(eventtime)
|
||||||
# Check for filament insertion
|
# Check for filament insertion
|
||||||
# Filament is always assumed to be present on an encoder event
|
# Filament is always assumed to be present on an encoder event
|
||||||
self.runout_helper.note_filament_present(True)
|
self.runout_helper.note_filament_present(eventtime, True)
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
return EncoderSensor(config)
|
return EncoderSensor(config)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
class RunoutHelper:
|
class RunoutHelper:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.name = config.get_name().split()[-1]
|
self.name = config.get_name().split()[-1]
|
||||||
@@ -24,7 +25,7 @@ class RunoutHelper:
|
|||||||
self.insert_gcode = gcode_macro.load_template(
|
self.insert_gcode = gcode_macro.load_template(
|
||||||
config, 'insert_gcode')
|
config, 'insert_gcode')
|
||||||
self.pause_delay = config.getfloat('pause_delay', .5, above=.0)
|
self.pause_delay = config.getfloat('pause_delay', .5, above=.0)
|
||||||
self.event_delay = config.getfloat('event_delay', 3., above=0.)
|
self.event_delay = config.getfloat('event_delay', 3., minval=.0)
|
||||||
# Internal state
|
# Internal state
|
||||||
self.min_event_systime = self.reactor.NEVER
|
self.min_event_systime = self.reactor.NEVER
|
||||||
self.filament_present = False
|
self.filament_present = False
|
||||||
@@ -59,19 +60,20 @@ class RunoutHelper:
|
|||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("Script running error")
|
logging.exception("Script running error")
|
||||||
self.min_event_systime = self.reactor.monotonic() + self.event_delay
|
self.min_event_systime = self.reactor.monotonic() + self.event_delay
|
||||||
def note_filament_present(self, is_filament_present):
|
def note_filament_present(self, eventtime, is_filament_present):
|
||||||
if is_filament_present == self.filament_present:
|
if is_filament_present == self.filament_present:
|
||||||
return
|
return
|
||||||
self.filament_present = is_filament_present
|
self.filament_present = is_filament_present
|
||||||
eventtime = self.reactor.monotonic()
|
|
||||||
if eventtime < self.min_event_systime or not self.sensor_enabled:
|
if eventtime < self.min_event_systime or not self.sensor_enabled:
|
||||||
# do not process during the initialization time, duplicates,
|
# do not process during the initialization time, duplicates,
|
||||||
# during the event delay time, while an event is running, or
|
# during the event delay time, while an event is running, or
|
||||||
# when the sensor is disabled
|
# when the sensor is disabled
|
||||||
return
|
return
|
||||||
# Determine "printing" status
|
# Determine "printing" status
|
||||||
|
now = self.reactor.monotonic()
|
||||||
idle_timeout = self.printer.lookup_object("idle_timeout")
|
idle_timeout = self.printer.lookup_object("idle_timeout")
|
||||||
is_printing = idle_timeout.get_status(eventtime)["state"] == "Printing"
|
is_printing = idle_timeout.get_status(now)["state"] == "Printing"
|
||||||
# Perform filament action associated with status change (if any)
|
# Perform filament action associated with status change (if any)
|
||||||
if is_filament_present:
|
if is_filament_present:
|
||||||
if not is_printing and self.insert_gcode is not None:
|
if not is_printing and self.insert_gcode is not None:
|
||||||
@@ -79,14 +81,14 @@ class RunoutHelper:
|
|||||||
self.min_event_systime = self.reactor.NEVER
|
self.min_event_systime = self.reactor.NEVER
|
||||||
logging.info(
|
logging.info(
|
||||||
"Filament Sensor %s: insert event detected, Time %.2f" %
|
"Filament Sensor %s: insert event detected, Time %.2f" %
|
||||||
(self.name, eventtime))
|
(self.name, now))
|
||||||
self.reactor.register_callback(self._insert_event_handler)
|
self.reactor.register_callback(self._insert_event_handler)
|
||||||
elif is_printing and self.runout_gcode is not None:
|
elif is_printing and self.runout_gcode is not None:
|
||||||
# runout detected
|
# runout detected
|
||||||
self.min_event_systime = self.reactor.NEVER
|
self.min_event_systime = self.reactor.NEVER
|
||||||
logging.info(
|
logging.info(
|
||||||
"Filament Sensor %s: runout event detected, Time %.2f" %
|
"Filament Sensor %s: runout event detected, Time %.2f" %
|
||||||
(self.name, eventtime))
|
(self.name, now))
|
||||||
self.reactor.register_callback(self._runout_event_handler)
|
self.reactor.register_callback(self._runout_event_handler)
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
return {
|
return {
|
||||||
@@ -108,11 +110,12 @@ class SwitchSensor:
|
|||||||
printer = config.get_printer()
|
printer = config.get_printer()
|
||||||
buttons = printer.load_object(config, 'buttons')
|
buttons = printer.load_object(config, 'buttons')
|
||||||
switch_pin = config.get('switch_pin')
|
switch_pin = config.get('switch_pin')
|
||||||
buttons.register_buttons([switch_pin], self._button_handler)
|
buttons.register_debounce_button(switch_pin, self._button_handler
|
||||||
|
, config)
|
||||||
self.runout_helper = RunoutHelper(config)
|
self.runout_helper = RunoutHelper(config)
|
||||||
self.get_status = self.runout_helper.get_status
|
self.get_status = self.runout_helper.get_status
|
||||||
def _button_handler(self, eventtime, state):
|
def _button_handler(self, eventtime, state):
|
||||||
self.runout_helper.note_filament_present(state)
|
self.runout_helper.note_filament_present(eventtime, state)
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
return SwitchSensor(config)
|
return SwitchSensor(config)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class FirmwareRetraction:
|
|||||||
self.unretract_length = (self.retract_length
|
self.unretract_length = (self.retract_length
|
||||||
+ self.unretract_extra_length)
|
+ self.unretract_extra_length)
|
||||||
self.is_retracted = False
|
self.is_retracted = False
|
||||||
cmd_GET_RETRACTION_help = ("Report firmware retraction paramters")
|
cmd_GET_RETRACTION_help = ("Report firmware retraction parameters")
|
||||||
def cmd_GET_RETRACTION(self, gcmd):
|
def cmd_GET_RETRACTION(self, gcmd):
|
||||||
gcmd.respond_info("RETRACT_LENGTH=%.5f RETRACT_SPEED=%.5f"
|
gcmd.respond_info("RETRACT_LENGTH=%.5f RETRACT_SPEED=%.5f"
|
||||||
" UNRETRACT_EXTRA_LENGTH=%.5f UNRETRACT_SPEED=%.5f"
|
" UNRETRACT_EXTRA_LENGTH=%.5f UNRETRACT_SPEED=%.5f"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user