I’ve been having a heck of a time getting the neopixel LED string working on one of my two Stealthburner toolheads (MissChanger mod). Sometimes it would work, sometimes it would not, and I wasn’t able to correlate the two conditions to anything I was doing. On several occasions it seemed to be correlated with whether or not the front piece (the one containing the LEDs) was securely fastened to the main toolhead body with the two (very) long and two short(er) screws. It seemed it worked when the screws were not in, and didn’t work when they were in and tightened down.
At first I was convinced that one or more LED wires was getting pinched between the two toolhead halves, and when I tightened down the mating screws, the circuit was interrupted. I even found evidence for this and fixed it – several times – but the intermittent behavior continued.
Some research by Grok turned up a LOT of talking about SB LED issues, which were mostly divided into two categories – one thread earlier this year talked about waveform timing issues, and the other thread talked about the fact that the rp2040 outputs 3.3V waveforms while the nominal voltage for the LED string is 5V.
In desperation, I decided to attach a short cable to the LED string connector pads on the Nitehawk PCB on the front half of the SB, thereby allowing me to directly monitor the actual waveform being transmitted to the LED string.
I was able to observe the ‘not working’ waveform once and then my troublesome SB refused to not work again, stubbornly continuing to light up no matter what I did. The waveform in the ‘not working’ condition looked more like triangular waves rather than rectangular indicating an output loading condition of some kind. Unfortunately, I was unable to capture the waveform on my DSO, so I can’t display it here. The ‘working’ waveform is shown below:
Toolhead mounted on the Voron printer, LED string working properly
In an earlier post I described the test jig I had created using an Arduino UNO. I could use it to connect directly to the LED string on the front half of the SB, and my troublesome LED string worked fine on the test jig. As Grok pointed out however, the Arduino output waveform went to 5V, not the 3.3V the actual hardware was providing, so the test jig might not be valid. So, I replaced the Arduino UNO with a Teensy 4.1, which does use 3.3V outputs, as shown here:
I added a potentiometer into the output circuit so I could slowly drop the output waveform down from 3.3V to see where the LED string stopped working. At 3.3V output it was solid, and this particular string still worked fine at 2.7V peak, as shown in following trace-grabs:
Teensy 4.1 3.3V output, LED string workingTeensy 4.1 2.7V output, LED string working
So, it appears that that at least my particular LED string works fine all the way down to 2.7V, so I don’t think the intermittent issues other users have been experiencing are due to the rp2040 having a 3.3V instead of 5V output. My current thinking is that the problems are more likely to be the timing issues discussed in this Klipper Github PR
01 November 2025 Update:
When all was said and done, I could no longer get the LED string on my troublesome toolhead to fail, so I wound up putting it back together and mounting it back on the printer. However, as insurance against a future Murphy attack, I left the external LED data line monitor cable intact, and hot-glued it into the intake cavity for the part cooling fan, as shone in the following photo:
Earlier this month I was able to run a successful docking sequence with one toolhead, as shown below:
Unfortunately, I haven’t been able to replicate this successfully, and after several failures I started looking for the root cause. Eventually I figured out that at least part of the issue was that the middle ‘retainer’ foot that keeps the sliding ‘claw’ from falling off is rotated slightly by the claw when it comes out the ‘docked’ position during an undocking procedure, and this rotation keeps the claw from freely sliding down the ramp and subsequently connecting to the toolhead carrier. See the following two photos for an example of this issue:
Before the dock claw comes off the pinsAfter the dock claw comes off the pins
At first, I thought I just hadn’t tightened the retainer foot down enough, but doing so actually made the problem worse; now when the toolhead is pulled out of the pins by the toolhead carrier, there is enough force to still rotate the foot, but now the additional screw tension makes it even harder for the claw to slide down to the release position – yikes!
As an experiment, I modified the foot by removing 1mm from the bottom to form a ledge to mate with the front of the base, figuring this would stop the foot from rotating. Unfortunately this did not work – there was still enough play in even a very tightly matched ledge to allow the foot to rotate. Next I decided to create a second point of alignment to the foot by way of a post on the underside of the foot that mates to a hole on the upper surface of the base, as shown in the following photos.
The combination of the post/hole and ledge features seemed to do the trick. Now the claw disengages cleanly from the ‘docked’ position and slides freely down to the ‘undocked’ position as shown in the following video:
In an interesting side note, I discovered that I could easily adjust the rubber band tension by making a loop around either (or both) screw/washer combinations. As it turned out though, even the weakest arrangement (no additional loops) with the new foot/claw configuration was plenty to pull the claw all the way down the slide. Hopefully this will result in much better dock/undock performance – we’ll see.
24 October 2025 Update:
I wound up going through a few minor iterations on the design of the dock body/base and the retainer piece. On the dock body, I opened up all four pin docking holes by 0.5mm, and slightly enlarged the holes for the 2mm self-tapping screws, as these were very difficult to get all the way in, especially the two on the bottom. On the retainer piece, I wound up removing 1mm from the side that the claw slides on, as this seemed to provide a better sliding action.
After getting all this done, I remounted the dock assembly on my printer, and again (for the upteenth time) went through section 4.1. ‘Park position calibration’. When I thought I had everything lined up, I ran TEST_DOCKING only to have it fail – again. The claw wasn’t sliding over to the docked position, so I must not have gotten the Y-position correct and the pins didn’t slide into their respective holes. Went through the whole process again, but this time when I got to the part where the claw was ‘all the way up the ramp’ I commanded an X-minus move of 5mm, and made sure the claw moved smoothly into the docked position. Because I was working with the left-most dock , I was able to actually look through the left side transparency of the printer and see whether the pins on that side were lined up with the holes. Turns out that the docking failure was caused by the Y position being off (1mm too positive). I was only 1mm, but that made a huge difference. Here’s a short video of a successful dock/undock sequence.
My wife wants a low-intensity lamp she can turn on/off using any common TV remote from a distance of about 20ft. I found this set of four pairs of IR receiver and transmitter breakout boards on Amazon, and discovered the receiver can indeed detect the signal from a TF remote at well more than 20ft, so the project looks feasible.
Wiring is simplicity itself; just +5V, GND, and output pins.
Here are some representative scope grabs:
20ms/div, so each block of data is about 80-90msec longCloseup of one data block
The idle output voltage is +5V, so I’m thinking that I can probably just low-pass the output and set a threshold.
The low-pass idea didn’t work – there wasn’t enough difference between the HIGH level and the average to comfortably use, so I wound up just counting the number of LOW occurrences in the receiver output, and that worked well (and eliminated two components as well). Here’s the finished prototype:
16 October 2025 Update:
This project is now finished, sort-of. The TV Remote Lamp works according to the wife, but there are some issues with the remote. If she uses the TV remote as we intended, and presses the ON/OFF button at the top – that will also turn the TV itself back on – an unintended consequence, but one that can be resolved by NOT using the ON/OFF button on the remote – any button will do.
Here’s a photo of the finished product and the accompanying enclosure, compliments of my Prusa Core ONE 3D printer:
And here is a photo of the ‘installation’ in our master bathroom:
As can be seen from the above photo, the sensor and LED are actually facing away from the bedroom and toward the mirror, but the sensor is perfectly happy with the reflected signal from the TV remote.
And here is the finished Arduino code for the Teensy 3.2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
Name: TVRemoteLamp.ino
Created: 10/12/2025 8:52:39 AM
Author: FRANK_XPS_9530\Frank
*/
#include <EEPROM.h>
#define INPUT_PIN 23
#define LAMP_PIN 3
#define TV_REMOTE_SIGNAL_COUNT 1000
#define TV_REMOTE_LOW_COUNT_THRESHOLD 100 //20%
#define REPEAT_TRIGGER_DELAY_MSEC 1000
//From NiteLight project
#define LED_MAX 250
#define LED_MIN 0
#define UP_BUTTON_PIN 14
#define DOWN_BUTTON_PIN 22
#define EEPROM_ADDRESS 0
#define PWM_INCREMENT 5
//#define OFFLINE
uint8_t LED_Intensity=0;
elapsedMillis sinceLastOutput;
boolbDone=false;
uint16_t samp=0;
uint16_t samples[1000];
uint32_t Msec[1000];
uint16_t low_count=0;
boolbLampStatus=false;
voidsetup()
{
#ifndef OFFLINE
Serial.begin(115200);
delay(2000);//needed for teensy
#endif
pinMode(INPUT_PIN,INPUT);
//pinMode(LED_BUILTIN, OUTPUT);
pinMode(LAMP_PIN,OUTPUT);
//digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(LAMP_PIN,LOW);
//From NiteLight project
pinMode(UP_BUTTON_PIN,INPUT_PULLUP);
pinMode(DOWN_BUTTON_PIN,INPUT_PULLUP);
LED_Intensity=EEPROM.read(EEPROM_ADDRESS);
Serial.printf("Got = %d from EEPROM address %d\n",LED_Intensity,EEPROM_ADDRESS);
I have decided to make the Klipper-related changes for my MissChanger adventure into its own post, so readers won’t have to wade through all my other non-klipper related blatherings.
The current project status is:
My single SB configuration with single-extruder config file 250830_OrigToolhead_printer.cfg & 250920_Original_stealthburner_leds.cfg working fine.
Currently using my new SB (due to original SB’s neopixel intermittency) with original SB’s NH BCB (due to new SB,s PCB failure). This combo works fine
Next step is to reload 250928_MC_T0_printer.cfg, 250922_T0-Nitehawk-Revo-LDO_Ex.cfg and 250922_T0_stealthburner_leds.cfg. this config file set loaded and started up fine.
Now it is time to go through Vim’s ‘Calibration’ (Section 4 in the TOC) instructions to see if I can get prints to work
02 October 2025 Update:
The first part of the ‘Calibration’ section is intended to shake the toolhead around on the Tap & Change mount to make sure it will slide onto and off the mount during tool changes.
Ran ‘G28’ (Home All) and confirmed the toolhead Z position of +10mm at the center of the bed.
Set the heated bed temp to 60C. According to Vin, this is to get the chamber temp up to 40C or so before starting the ‘shake & bake’ routine.
Tried to run ‘SHAPER_CALIBRATE’ but got a ‘Unknown Command’ error. Researched this a bit, and decided to skip this step for the moment.
So, I moved on to Section 4.1 – Park Position Calibrate.
First, I mounted the docking hardware I had previously constructed. This in itself required a bit of work to get everything assembled and mounted.
Then I ran the Home-All & QGL and STOP_TOOL_PROBE_CRASH_DETECTION.
I temporarily replaced the ‘100’ with ‘0.1’ in the toolhead movement options for X/Y movement. Then I used these movement options to optimize the ‘X’ position of the toolhead in front of the leftmost dock, as shown.
This process turned out to be a LOT more complicated than I thought, due to the irregular shape of the stealthburner toolhead. Not only did the X position have to be correct, but the dock module rotation around the 10mm rod mattered as well. Eventually I got what I think is a good lineup at X = 68.7.
After getting the X value down, and getting a good dock module orientation, I tried some dock/undock movements as shown in the following short video:
Tool 0 Dock/Undock testing
The above test was performed by moving the toolhead laterally to the optimum X position (68.7), and forward to just in front of the dock (Y ~ 110). Then I commanded a 50mm Y travel to fully engage the toolhead in the dock without forcing the docking collar up the locking ramp, followed by backing the toolhead out 50mm & repeating the process.
At this point I have one toolhead docking station calibrated with the leftmost docking station.
As a side note, my replacement NH USB PC and replacement RGB LED string came in today, so I have two working toolheads again. My original (Red) SB is now labelled as ‘T1’ and my second (Gray) SB is now labelled as ‘T0’. the USB serial ID’s are”
1
2
3
usb-Klipper_rp2040_4E363334320B42B6-if00--Orig(Red)toolhead with newUSB PC
usb-Klipper_rp2040_4E363334324B5803-if00--Second(Gray)toolhead with orig USB PC
The next step is to create a second toolhead-specific config with its companion stealthburner_leds.cfg. Since the two stealthburners are identical save for their USB IDSs, this should be relatively easy.
I copied 250922_T0-Nitehawk-Revo-LDO_Ex.cfg to 251002_T1-Nitehawk-Revo-LDO_Ex.cfg, replaced the T0 USB ID with the new T1 USB ID, replaced include 250922_T0_stealthburner_leds.cfg with include 251002_T1_stealthburner_leds.cfg, and changed all 20 instances of ‘nhk0:’ with ‘nhk1:’.
I copied 250922_T0_stealthburner_leds.cfg to 251002_T1_stealthburner_leds.cfg and changed ‘nhk0:gpio7’ to ‘nhk1:gpio7’
Then I saved 250928_MC_T0_printer.cfg to 251002_MC_T1_printer.cfg and (on line 826) changed include ./250922_T0-Nitehawk-Revo-LDO_Ex.cfg to include ./251002_T1-Nitehawk-Revo-LDO_Ex.cfg.
Then using SCP in a Windows command window, I loaded all the above to the raspberry, changing the name of 251002_MC_T1_printer.cfg to just printer.cfg.
I disconnected the USB/Power connector onT0 and restarted restarted the printer. Got ‘mcu nhk0’ Unable to connect.
03 October 2025 Update:
I was a bit ahead of myself with getting the second toolhead up and running. For one thing, I discovered the problem I was having with not being able to successfully do a firmware restart instead having to cycle power was a known problem with the LDO USB adaptor board, and the fix was to physically remove two resistor chips from the PCB. I did this for both the USB adaptor boards.
Then I realized I hadn’t really completed the ‘park position calibrate’ step – I had just done the X position step, so I reverted the firmware to the T0 configuration and started over with this procedure. This time I wound up with X = 69.7 (probably due to some lateral movement of the entire docking bar).
05 October 2025 Update:
Finally got all the way through the dock park calibrate step for T0, and was able to run several successful TEST_DOCK cycles
08 October 2025 Update:
The last couple of days have been marked by one disaster after another. First, I have been unable to get the neopixel string on my new(er) Stealthburner to work, even though I would have sworn on a stack of bibles that it was working fine a few days ago. Second, I tried to ‘unload filament’, forgetting that that particular macro (and a few others I suspect) move the toolhead to the front of the printer, where it promptly collided with the docking assembly, and third, I tried a test print with my new(er) toolhead forgetting that I needed to recalibrate the z-axis offset, so my extruder tip was dragging along the print bed while I frantically banged on the fine-tuning Z+ button. I thought I was doing OK until I watched in horror as the entire conical tip of the extruder fell onto the print bed, along with a huge glop of molten filament! YIKES!!!
Neopixel LED Woes:
By swapping the front shell of my known-good original toolhead with the one from my new(er) toolhead, I was able to determine that the neopixel problem followed the front shell – so not a NH PCB, umbilical or USB adaptor problem. I removed the neopixel strings from both shells and swapped just the neopixel string – and the problem followed the suspect string. Visual inspection of the string was hampered by the fact that whoever assembled the string used all black wires for the intra-pixel connections, and then glopped what looks like black RTV on the connections.
While I did find and repair a broken connection, the string still didn’t work. At this point I decided to get a replacement string on order, so I jumped up on Amazon and ordered this:
When it arrived, I plugged it into my new(er) toolhead (thankfully without going to the trouble of installing all the LEDs) whereupon I discovered that it didn’t work either! I did the same thing with my original shell – no joy there either. Now I feel like I’m in an episode of “The Twilight Zone” (for those of you old enough to have watched them); I have three strings, one of which is brand-new, but only my original toolhead string actually works! After sleeping on this, I decided to set up a test jig to test all the strings independently of the printer. I grabbed an ancient Arduino UNO and a 3-pin 2.0mm male JST plug from my parts bin, downloaded Adafruit’s wonderful NeoPixel library and used their ‘strandtest.ino’ example program to drive the string under test. After getting through all the normal stupid programmer issues, I was able to validate the test jig against my known-good neopixel string – Yay! Then I tested the suspect string from my new(er) toolhead – yep still bad – and then the brand-new string from Amazon – bad. I was really shocked that the new string was bad, as it looked perfect physically, and since the pixels are wired in parallel, I would certainly have expected at least one to work. So, I went looking on Amazon for reviews and found one that said the blue (data) and black (ground) wires were reversed in the connector – yikes! I reversed the pins, and voila` the string lit up – YAY!!
Re-pinned Amazon Neopixel string
Now I have two working strings and one that’s still dead. Maybe it has the same mis-wiring issue? I grabbed my trusty Hanmatek DOS1102 digital O’Scope (a must-have for a well-equipped lab) and used it on my brand-new working string to verify that each pixel chip on the brand-new string had good, solid data (see trace-grab below).
However, when I looked at the ‘bad’ string, I can see the proper waveform on the ‘DO’ solder pad on the ‘input’ side of the first pixel, but not on the ‘output’ side (I didn’t realize until just now that there was an ‘input’ and ‘output’ side – I thought the data line was just like the +V and GND – straight passthrough). So, I bypassed the first pixel by physically connecting the ‘output’ wire to the ‘input’ pad and sure ’nuff, the remaining two pixels lit right up!
‘Bad’ neopixel string test with first pixel bypassed
I installed the new Neopixel string into my ‘T0’ toolhead (with the gray shell). Now that I have my nifty Arduino test program, I was able to test the string at each stage of the installation process to make sure all the required wire contortions didn’t fatally pinch a wire or break a solder connection. Here it is after the string was fully installed (and yes, I did check that the front-facing LED was working too).
Toolhead T0 shell with Neopixel string installed
If you happen to have an Arduino-compatible board (Ardiono, Sparkfun, Teensy, etc) you can easily build the same test jig. I have provided the Arduino code below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
Name: NeopixelTest2.ino
Created: 10/7/2025 5:35:59 PM
Author: FRANK_XPS_9530\Frank
*/
// A basic everyday NeoPixel strip test program.
// NEOPIXEL BEST PRACTICES for most reliable operation:
// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
// connect GROUND (-) first, then +, then data.
// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
// a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
// (Skipping these may work OK on your workbench but can fail in the field)
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN 6
// How many NeoPixels are attached to the Arduino?
strip.setPixelColor(c,color);// Set pixel 'c' to value 'color'
}
strip.show();// Update strip with new contents
delay(wait);// Pause for a moment
firstPixelHue+=65536/90;// One cycle of color wheel over 90 frames
}
}
}
Filament Load/Unload macros – and others?
When I tried to do a filament unload, the toolhead promptly drove itself right into the dock – oops! It was at this point that I realized I really didn’t know what I was doing, and had to get A LOT smarter. The filament unload macro (and surely many others) uses print bed size information from printer.cfg. gcode_macro UNLOAD_FILAMENT calls a macro called ‘FRONT’ to move the toolhead to the front center of the print bed, as shown below:
As can be seen in the above code, the line “G1 X{x_center} Y10 F7800” uses the variable ‘x_center’, but goes to Y = 10, which is why it drives into the dock. This macro should really use something like “Y{printer.position_max – 10}, where ‘printer.position_max’ changes depending on whether or not the dock is present (i.e. on the value of the ‘variable_dock’ variable in ‘gcode_macro _home’)
The above issue ran me down a rabbit hole from which I have just emerged – dirty but victorious. Although this crash problem could have been fixed easily by just changing ‘Y10’ to ‘Y110’, I was worried about other places where ‘magic numbers’ might be lurking. And then this got me thinking about all those places in the code where ‘printer.toolhead.axis_maximum.y’ is used, as this value doesn’t currently get changed when CONFIG_TOGGLE is executed.
So, I posed this problem to Grok as follows:
I have the MissChanger toolchanger mod installed. This mod uses a macro called CONFIG_TOGGLE to modify some printer.cfg parameters to account for the approximately 130mm at the front of the build plate occluded by the toolhead docking assembly, The python function ‘klipper-toolchanger/klipper/extras/config_switch.py’ manages this by replacing a section of printer.cfg with either the contents of ‘printer_data/config/config/config_no_dock.cfg’ or ‘printer_data/config/config/config_wt_dock.cfg’ I have attached copies of ‘printer.cfg’, ‘config_wt_dock.cfg’ and ‘config_no_dock.cfg’. It looks like the switch code should modify the printer object’s minimum_y value (derived from the stepper_y section’s position_max) instead of ( or maybe in addition to) the ‘variable_xh’ value in printer.cfg, as the printer object’s values are used everywhere in the code. What do you think?
Amazingly, Grok not only agreed with the idea in principal, but went on to digest the files I had provided and then produce a complete step-by-step procedure for implementing the idea, including complete ‘config_switch files. Grok even analyzed ‘klipper-toolchanger/klipper/extras/config_switch.py’ and figured out that the [stepper_y] and [tmc5160 stepper_y] sections would have to be moved below the ‘;< # Section Variable marker’ line in printer.cfg so that ‘config_switch.py’ could modify the ‘position_min:’ value appropriately. And Grok did the whole thing in just a few minutes! To say that I was blown away by this would be to hugely understate the impact this had on me – wow!
Armed with Grok’s step-by-step plan, I was able (after more ‘running around in circles’ than I care to admit) come up with a complete working set of files to support ‘with_dock’ and ‘without_dock’ configuration switching.
At this point I can switch from the ‘dock module installed’ to the ‘dock module removed’ condition freely. It remains to be seen whether I can now build on that to get to a fully functional dual-toolhead system.
16 October 2025 Update:
I was away for a while, but I’m back now. I read through my notes above, and starting from there will try to make a bit more progress. After getting the ‘config_toggle’ to work, I’m going to try to get the current toolhead (gray shell) working to the point of doing a test print. DON’T FORGET TO MAKE SURE THE ‘CONFIG_TOGGLE’ state matches the actual physical state (docking assembly present or not)!
Note: If you get the error “failed to detect active tool” after a head-banging episode, it is probably because the power was cycled with the toolhead resting on the print bed with the z_axis sensor in the ‘triggered’ state – apparently this prevents the toolhead from being ‘active’. Manually raise the gantry sufficiently to UNtrigger the sensor (blue LED changes to RED). Don’t worry about keeping the gantry level as this will be done later.
In the ‘Dock Installed’ configuraion, I got the Home_all to work, but the gantry-level failed with an ‘out of bounds’ error. This was due to the config_wt_dock.cfg file having ‘110’ as the min Y, but ‘position_min’ in [stepper_y] was set to 130 – oops. I fixed this by setting both numbers to 120 and did ‘Save&Restart’. Then I did *two* ‘CONFIG_TOGGLES’ and re-checked. OOPS! no change to ‘printer.cfg’ – how can this be?
I went back into config_wt_dock.cfg and saw that the numbers had not changed – so what happenec? Checked config_no_dock.cfg and those numbers are OK (25 for min quad level move in Y, position_min = 0 in [stepper_y]).
Did ONE Config_Toggle, and I see the value changed to the ‘no_dock’ values. Did a second one, and now the values are ‘110’ and ‘130’ again, even though I changed them to 120 & 120 in config_wt_dock.cfg. No, they are still 110 & 130 in config_wt_dock.cfg. I edited them again, verified that they were still correct after a save, then did a Save & Restart and checked again. This time they are correct. Oooh, I have a headache! I did a CONFIG_TOGGLE and verified the ‘no_dock’ numbers were correct, but when I did another CONFIG_TOGGLE, not only were the ‘wt_dock’ numbers wrong in printer.cfg, they were also wrong in /config/config_wt_dock.cfg! Something is editing (or reverting?) the wt_dock file
OK, the problem was that CONFIG_TOGGLE calls CONFIG_SAVE before anything else, and CONFIG_SAVE, writes the dock-related contents of printer.cfg to ‘config_wt_dock’ or ‘config_no_dock’ depending on the value (True or False) of the ‘variable_dock:’ parameter. Now fixed.
So now HOME_ALL and QUAD_GANTRY_LEVEL both worked as expected in the ‘with dock’ configuration. So now doing the ‘Calibrate reference z-offset’ step. *should* use the value ‘130’ (min_y = 120 + 10 = 130). YES! It did – and I’m getting a successful print – YAY!
The first test print required a ‘fine-tune’ adjustment of +0.350 to get the right amount of ‘squish’. When the print completed, I accepted the change, and this modified the z-offset to -2.000 (was -1.650). The second test print was OK without any fine-tuning.
I would like to have the toolhead go to Z = 225 after the print has finished but, if possible, do this only for the ‘with_dock’ configuration. I modified printer.cfg to put the entire PRINT_END macro just below the ‘variables’ marker, and then changed the finish z-axis position to 225. Then I ran a test print and this worked – the toolhead climbed to 225, pulling the dock assembly with it so I could easily access the finished print. Then I did a ‘SAVE_CONFIG_MODE’ from the console to write the new printer.cfg contents to the ‘config_wt_dock.cfg’ file. I checked this file and verified that the PRINT_END macro had indeed been saved to it. Then I switched to the ‘no-dock’ configuration, again moved the PRINT_END macro below the variable marker, changed the ending z position to Z25, and ran SAVE_CONFIG_MODE to write it out to ‘config_no_dock’.
Then I did a ‘test print, to verify that the PRINT_END macro works as intended in this config.
18 October 2025 Update:
I couldn’t get the TEST_DOCK operation to work. I kept getting ‘move out of bounds’ errors even though I had executed the STOP_TOOL_PROBE_CRASH_DETECTION macro. This worked before, so I was more than a little bit confused. When I asked Grok about this, he maintained that the STOP_TOOL_PROBE_CRASH_DETECTION has only a ‘local’ (whatever that means) effect – the hardware bounds listed in the config file are still checked and honored. Just now it came to me that I caused this problem when I modified the configuration toggle mechanism to dynamically modify the ‘position_min’ variable in [stepper_y]
1
2
3
4
5
6
7
8
9
[stepper_y]
step_pin:PF15
dir_pin:!PF14
enable_pin:!PE9
rotation_distance:40
microsteps:32
full_steps_per_rotation:400#set to 200 for 1.8 degree stepper
endstop_pin:PC2
position_min:120#Dock installed
I thought I was being very clever, but apparently not clever enough for klipper!
19 October 2025 Update:
When I asked Grok about this issue, it gave me a pretty detailed explanation why the previous ‘clever idea’ didn’t (and won’t) work. Here is Grok’s response:
To address your issue with position_min blocking docking operations while still leveraging printer.toolhead.axis_minimum.y (and similar) for general macro consistency, the core challenge is that position_min enforces a hard, system-wide limit on all moves—it’s static after startup and can’t be dynamically toggled or selectively bypassed for specific macros without workarounds. Based on Klipper’s documentation and community discussions, there are no built-in ways to change it at runtime or ignore it per-macro safely. However, here’s a viable strategy that balances your goals, with options from safe (recommended) to more advanced/risky.Recommended Approach: Use Soft Limits with Full Hardware RangeRevert position_min to 0 in both toggle files (config_no_dock.cfg and config_wt_dock.cfg) to allow full Y-axis access at the hardware level—this ensures docking macros can always succeed without errors, even mid-print. Then, enforce an “effective” minimum Y (your occlusion value, e.g., 130) as a soft limit via macro variables and conditionals. This propagates dynamically without magic numbers by referencing a single variable, while still using the printer object’s actual axis_minimum.y (now always 0) as a base.
Step 1: Update Toggle Files for Full Range
In both config_no_dock.cfg and config_wt_dock.cfg, set [stepper_y] position_min: 0 (keep other values like position_max: 310).
This makes printer.toolhead.axis_minimum.y always 0 after restart, avoiding any hard bounds errors during docking.
Step 2: Define an Effective Min Y Variable. Add or update a global variable in your toggle files (e.g., in [gcode_macro _static_variable] or a new [gcode_macro _limits]). Use variable_dock to set an “effective_min_y”:
[gcode_macro _limits]
variable_effective_min_y: {% if printer["gcode_macro _home"].dock %} 130.0 {% else %} printer.toolhead.axis_minimum.y {% endif %} # Your occlusion value; use 110 if that's exact
gcode: # Empty, just for variables
Place this in the swappable section of both toggle files (after the marker).
Now, printer[“_limits”].effective_min_y acts as your dynamic, dock-aware min Y—it’s 130 when docked (soft-enforcing occlusion for print ops) and 0 otherwise.
After toggling/restarting, this propagates to all macros without hardcoding 130 everywhere.
Step 3: Update Macros to Use the Effective MinReplace direct references to printer.toolhead.axis_minimum.y or hard Y values with {printer[“_limits”].effective_min_y} (or add offsets as needed). Examples:
Prime Line (e.g., in PRINT_START or a prime macro):
{% set prime_y = printer["_limits"].effective_min_y + 5 %} # Safe offset from effective min
G1 X{printer.toolhead.axis_maximum.x / 2} Y{prime_y} F7800 # Prime at front if no dock, or just above occlusion if docked
Parking (e.g., in PAUSE or M600):G1 X{printer.toolhead.axis_maximum.x / 2} Y{printer["_limits"].effective_min_y + 5} F6000 # Parks safely above effective min
Other Macros: For any that position near front (e.g., nozzle clean, unload), use similar: Y{printer[“_limits”].effective_min_y + offset}.
Keep [bed_mesh] and [quad_gantry_level] toggling as-is—they already use high min Y (e.g., 110) when docked to avoid probing occluded areas.
Benefits and Trade-offs
Keeps Using Printer Object: You still reference printer.toolhead.axis_minimum.y as a base (it’s now reliably 0), but layer the effective value on top for dock awareness—no magic numbers scattered around.
Docking Succeeds: Full range allows low-Y moves without errors.
Safety: Soft limits prevent accidental print/probe in occluded areas via macros, but won’t block invalid custom G-code (e.g., manual G0 Y10 would still work—add warnings if needed).
No Restart Mid-Print: Toggle only when switching dock install state (e.g., hardware changes).
Update your slicer profiles to match effective build volume (Y min 130 when docked) to avoid sliced violations.
This is the safest, most maintainable way without risking hardware.
It occurred to me that rather than modifying the dock & no-dock toggle files per Step1, I could simply move the [stepper_y] & [tmc5160 stepper_y] sections back above the variable marker where they started and remove the sections from both the dock & no-dock config files. I checked this with Grok, and he was OK with that. So, Step 1 – complete.
Step2: Grok said the [gcode_macro _limits] section could go anywhere ABOVE the toggle marker, so I put it JUST above the marker, with a comment. I also changed ‘130’ to ‘120’ as I know it works.
Other macros that needed to be modified:
Prime line code in [gcode_macro PRINT_START]
Front position in [gcode_macro FRONT]: I discovered that past-Frank had shifted this macro into the ‘Session Variables’ section (presumably so the y-value could be changed to reflect the ‘dock/no-dock’ configurations), but the corresponding macros in ‘config_wt_dock’ and ‘config_no_dock’ weren’t any different than the original version – oops!. Moved the CHOME & FRONT macros back to their original positions above the ‘Session Variables’ marker and changed the y value in ‘FRONT’ from {% set y_center = printer.toolhead.axis_maximum.y|float / 2.0 %} to {% set y_front = printer[“_limits”].effective_min_y + 5 %} and ‘G1 X{x_center} Y{y_center} F7800’ to ‘G1 X{x_center} Y{y_front} F7800’. Also added a statement to send the FRONT x,y pos to the display.
Removed the CHOME & FRONT macros from ‘dock’ & ‘no-dock config files (and moved the ‘#include'[include 251003_T0-Nitehawk-Revo-LDO.cfg]’ to the top)
Now I will just have to play with the printer for a while and see what happens. Fortunately by this time I have a pretty good understanding of how all the bells and whistles work. As one more safety measure, I have included printer.cfg, config_wt_dock, and config_no_dock here:
config_wt_dock.cfg:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#;< # Section Variable marker
[gcode_macro _limits]
variable_effective_min_y:120.0#this value gets changed to '0.0' in 'config_no_dock'
gcode:# Empty, just for variables
#10/17/25 gfp: PRINT_END moved here to see if I can modify end Z position for only the 'with_dock' configuration
[gcode_macro PRINT_END]
# Use PRINT_END for the slicer ending script - please customise for your slicer of choice
Back to testing after yesterday’s marathon sessions with Grok. Started a test print with the dock in place (“variable_dock: True” in printer.cfg). Hopefully the prime line will get laid down just in front of the dock exclusion area. Hurrah – it worked! The prime line was laid down at Y=125, as expected, and the print itself turned out as well as any in the past.
28 October 2025 Update:
I spent the entire day yesterday trying to get the ‘LOAD_FILAMENT’ macro to work with toolhead T1, with no success. Every time I tried the macro, it would heat the extruder to 230C and then hang forever ‘waiting for temperature’, even though I could plainly see that extruder 1 was at 230C. For a long time I thought maybe this was happening because I hadn’t yet calibrated the PID for the extruder heater, so the temp was varying too much. After I finally figured out how (with a lot of help from Grok) to calibrate the PID, I *still* couldn’t get LOAD_FILAMENT to work. Eventually, with more help from Grok, I figured out the problem. It turns out that the LOAD_FILAMENT macro definition in printer.cfg only works for a single extruder setup, and it was blocking the execution of the multiple-extruder version of LOAD_FILAMENT in the ‘macro_general.cfg’ file that came with the MissChanger installation – oops!
Once I figured this out, I deleted the LOAD_FILAMENT (and UNLOAD_FILAMENT) versions in printer.cfg, and voila! everything started working again!
So now I understand why the example printer.cfg that came with the MissChanger installation is so simple compared to the one I had developed over time since I first got my Voron 2.4 going; it’s because many of the standard operations in my printer.cfg assume a single extruder (named ‘extruder’). This still works OK with my dual extruder setup when extruder T0 is active, as it is also named ‘extruder’. However, when T1 is active, its extruder is named ‘extruder1’, which isn’t recognized at all by the single-extruder macro definitions. The corresponding macros in ‘macro_general.cfg’ have been generalized to deal with any number of extruders, named ‘extruder’ (for T0) or ‘extruder[x]’ (where ‘x’ ranges from 0-N).
After finally figuring this out, I went back through all the macros in printer.cfg to see what other single-toolhead limited code has to be removed to avoid overriding multiple-toolhead versions in macro_general.cfg, but didn’t find any more. Apparently, the LOAD/UNLOAD_FILAMENT were the only ones.
OK, now I’m trying to figure out why my SB LEDs aren’t working, and my assumption is it has something to do with the T0/T1 issue above. I instrumented 251002_T1_stealthburner_leds.cfg to display on the status screen in the ‘Dashboard’ display, and from that I can tell that when I run the ‘status_ready’ (and others), that the correct macro is called (in this case, the one in 251002_T1_stealthburner_leds.cfg). However, nothing happens on the toolhead itself.
251029 Update:
Well, I am truly down the rabbit-hole without a crumb trail on this one. After a full day of working on the issue with Grok, I’m more lost now than when I started. Grok seems completely convinced that the issue is a hardware problem caused by trying to run the nominally 5V neopixel string from the nominally 3.3V RP2040 MCU on the NH PCB. Grok’s cure was to install a WS2812B 3.3-5V Shifter Board. I’m more than a little suspicious of this result, as I remember having the LED strings on both toolheads working not long ago. Here’s a short movie of a dock/undock cycle from 24 October and the neopixel string was working just fine then.
So, my plan is to revert my Klipper config setup to that previous one and see if the LEDs work again. If so, then it obviously is a software, not hardware, problem.
Looking through my saved config files, I found this complete set of config files:
I replaced the contents of printer.cfg with the contents of 251025_wt_dock_printer.cfg (this file uses the 251003_T0-Nitehawk-Revo-LDO.cfg tool-specific config file, which is already available on the printer), and voila! the LEDs on the T0 (gray) toolhead are back, and all the ‘STATUS_xxx’ macros work as well! So, my old “Hardware never fails – it is ALWAYS a software issue” mantra wins again.
When I told this to Grok, he(it?) congratulated me on eliminating hardware as the problem, and then decided the problem was caused when I moved the T0/T1 include files to the top of printer.cfg. This caused the T0/T1 code to be inserted in the wrong place, thereby causing the LED problems I experienced. Grok then guided me through fixing the issues with the dual toolhead config files.
30 October 2025 Update:
Well, Grok and I have been back and forth a lot over the last few days, and I’m sure by now Grok (he/she/it?) is getting frustrated with the inability of this old human to ‘grok’ (and yes, I *am* old enough to have read “Stranger in a Strange Land” when it first came out) the finer points of Klipper configuration file construction.
The basic problem is that whenever I try to implement a dual-toolhead configuration, the stealthburner LEDs are disabled. Grok has explained this is due to ‘included’ files overwriting needed LED control information (like pin numbers?), and has provided several ideas for fixing the problem via different config file edits. Unfortunately my Klipper ignorance is showing (and Grok has some real blind spots as well), so I haven’t been able to get anything to work.
While researching why this LED thing is giving me so much trouble, I came to realize that the real problem is that I started with the single-toolhead config file developed over time since I first got the Voron going, and part of that was the use of the ‘stealthburner_leds.cfg’ add-on that contains all the cool ‘STATUSxxx’ macros. However, vin’s MISSChanger project starts with just a very plain-vanilla printer.cfg file without any of the bells/whistles and adds the multiple-toolhead stuff from there. This was a situation ripe for problems and boy did I have them. I’m going to give Grok’s ideas a good try, but if I can’t make them work, I’m going to go back to Vin’s basic printer.cfg and build things up from there.
31 October 2025:
Last night I was able to verify that ‘klippy.log’ contains the entire in-lined configuration, after all includes and before parsing at the top of each entry. This should go a long way toward effective troubleshooting of the ‘No LEDs’ problem. However, when I started the process of implementing the Grok-suggested changes, I discovered that my printer will no longer connect to nhk0 or nhk1. I get the dreaded “mcu ‘nhk1’: Unable to connect” and/or “mcu ‘nhk0’: Unable to connect” error(s). Since I haven’t made any hardware changes this is kinda hard to believe, and at least so far I’m convinced this is yet another software-induced issue.
So, I plan to go back to my 251025 configuration (251025_wt_dock_printer.cfg & 251003_T0-Nitehawk-Revo-LDO.cfg). Yep! this configuration setup worked perfectly – no errors, and the restart was very rapid (maybe 2-5 seconds).
I’m going to start a new post – “MissChanger Klipper Configuration – Part 2” – as this one is getting a bit cumbersome.