Tool Change Functionality (Question About Ground Control)

When I used to use ground control, my recollection (which is what might be faulty) was that during a tool change I could use the Z-Axis popup to raise and lower the router bit and define zero and then I could just resume the cut and it would adjust the Z-Axis height accordingly. Is my recollection faulty? I ask because now it seems I can access the popup, but none of the commands work. I can’t raise and lower the router bit, or define zero, in ground control during a tool change.

1 Like

I recall being able to use all of the commands…I believe that the tool change behavior should be identical to when you press the “pause” button. Basically everything should still work, it just won’t execute more lines of gcode.

But there was a report a few days ago of someone describing the same behavior so it’s possible that we have broken something.


I’m sorry this is long… and edited multiple times after posting…

What I am seeing is that after sending a “T1 M6” gcode, the controller does not respond back with an ‘ok’, rather it just responds back with the “Tool Change: Please insert tool 1” followed by a “Maslow Paused” from the executeMcodeLine() function. After sending those messages, the controller enters a pause() loop where it seems to only process serialCommands and not actually execute gcode.

Because the controller doesn’t respond back with an ‘ok’ after processing the ‘T1 M6’ line, neither the bufferSpace nor machineIsReadyForData get updated:

if lineFromMachine == "ok\r\n" or (len(lineFromMachine) >= 6 and lineFromMachine[0:6] == "error:"):
    self.machineIsReadyForData = True
    if bool(self.lengthOfLastLineStack) is True:  #if we've sent lines to the machine
         bufferSpace = self.bufferSpace + self.lengthOfLastLineStack.pop()    #free up that space in the buffer

So, even though the user issues move commands via the Z-Axis popup, which “puts” them in the gcode_queue, they never get executed because bufferSpace != bufferSize and machineIsReadyForData is False:

if self.bufferSpace == self.bufferSize and self.machineIsReadyForData:
   if != True:
      command = + " "

By sending the ‘~’ to the controller, the controller responds back with two ‘ok’ messages. The first ‘ok’ is for the “~” command that gets processed by readSerialCommands (which is called during the pause() loop), and after the pause() loop returns (because it received the ‘~’) to executeMcodeLine(), another “ok” gets sent for the original “T1 M6” command. These two “ok” messages makes bufferSpace = bufferSize (and makes machineIsReadyForData true).

However, even if the ‘ok’ message thing is cleared up, the controller still enters the pause() loop waiting to receive a ‘~’ before continuing. I think the controller is still capable of processing quick commands and such because the pause routine calls execSystemRealtime() which calls readSerialCommands(); however, I think gcode only gets executed during the main loop() which is the only function calling gcodeExecuteLoop(). I think the controller is stuck in the pause() loop in this state until it receives the ‘~’ and won’t execute gcode. This is what I think, not know.

If I’m correct, then to execute gcode you have to get the controller out of the pause loop and the only way to do that is to send the ‘~’ to the controller. This in itself isn’t necessarily an issue and is what webcontrol does when it receives a controller-initiated pause (i.e., “Maslow Paused” sent to webcontrol). It just makes the controller hop out of the pause() loop and re-enter the main loop() so gcode can be processed. Webcontrol is still, itself, paused (uploadFlag is 0) so it doesn’t send any more gcode file lines. The problem with this is that if buffer gcode is enabled, then there could be multiple commands already sent to the controller that is in the controller ring buffer and those will get executed. This is why I disabled buffer gcode in webcontrol last night.

From what I can tell, the only way for ground control to send a “~” is when the resume button (i.e., the pause button) is pressed. This would move the controller out of the pause loop but that button also sets uploadFlag to 1 and therefore ground control resumes sending gcode file lines… but obviously at this point the user never got a chance to move the Z-Axis. I’m pretty sure I moved the Z-Axis at one point so it must have worked at one time. Did, at one time, the controller not enter the pause() loop?

What I am worried about is 1) I’m misunderstanding ground control / controller source code and misinterpretting something or 2) If we broke something along the way, what was broken? I would think part of this could be resolved by the controller issuing an “ok” after sending the “Maslow Paused”. This would clear-up the bufferSize/bufferSpace and machineIsReadyForData issue and allow groundcontrol to send commands from the gcode_queue (which Z-Axis popup uses). But the controller is still in pause() loop and I don’t think it would execute any gcode commands until it receives the ‘~’.

1 Like

In brief testing, the “pause” button takes effect after the gcode lines in the firmware buffer have completed. When Settings/buffering is ‘ON’ this can be several lines of code after the button is clicked. As this can cause confusion with execution sequence, I tested with buffering ‘OFF’.

While running a job, clicking the ‘pause’ button in GC allows the current line of gcode to finish and the Z-axis commands are operational. Clicking ‘Resume’ sends the next line of gcode and processing proceeds. log.txt doesn’t indicate that the firmware is aware of the pause, the firmware reports ‘<Idle,MPos:…’ as if waiting for gcodes, and it executes the Z-axis gcodes as GC sends them. This form of pause is managed by GC, which suspends sending lines from the gcode file but will send other lines as requested, to which the firmware responds immediately because it is waiting for the next gcode to execute.

In a gcode file, the M0 gcode command pauses processing when the firmware evaluates it, setting a flag ‘sys.pause’ and looping in System::pause() until the flag is cleared. While the firmware is paused by the M0 command, any Z-axis commands are inserted into the GC serial buffer but are not executed until the ‘resume’ has been clicked in GC and the firmware begins accepting lines again. While paused the firmware reports ‘<Pause,MPos…’ but GC doesn’t appear to look for that. Instead it looks for ‘Maslow Paused’ sent by the firmware System::pause() to set its uploadFlag to 0. To exit fro this state, clicking the ‘Resume’ button in GC sends the ‘~’ character to the firmware which clears the firmware sys.pause flag and resumes firmware gcode evaluation.

1 Like

Yes, that is what I find. The ‘pause’ button on ground control simply pauses sending the gcode file. I think it works as intended. If you really need the machine to stop, you have to hit the ‘stop’ button.

Your description of M0 seems correct as well. M6 results in the same thing as you describe (enters pause() loop), just after sending a message asking for tool change.

My question is what to do with the tool change? What I plan to do on webcontrol is the following (unless someone has a better answer) because it doesn’t affect the firmware.

  • If buffering gcode is DISABLED, upon parsing the ‘Maslow Paused’ message, set uploadFlag to 0 and send a ‘~’. This will kick the controller out of the pause loop allowing it to accept move commands.

  • If buffering gcode is ENABLED, upon parsing the ‘Maslow Paused’ message, set uploadFlag to 0 and DO NOT send the ‘~’… therefore the controller will stay in the pause loop and not execute buffered gcode. All commands to move will be ignored by webcontrol… they won’t be sent to the controller.

If the user is wanting to enable buffer gcode AND do tool changes, they will have to use collets to set the bits to the exact correct height. There will be no ability to raise or lower the z-axis to set zero. If the user doesn’t enable buffer gcode, then there is no harm in kicking the controller out of the pause loop since the uploadFlag is 0 until the user hits resume.

This would prevent machine errors, but will seem wrong to the user who has forgotten the buffering setting or doesn’t understand the interaction between buffering and tool change. How about scanning the file on first loading, and posting an alert warning if it contains tool changes and buffering is on?
Another possibility would be for GC/WC to temporarily stop buffering if sending a gcode line that contains a tool change, and handle the ‘Maslow Pause’ as unbuffered until the ‘Resume’ is clicked.

1 Like

See, that’s why I said ‘unless someone has a better answer’. That’s easy enough to do in webcontrol since I already process the gcode line by line in real-time to filter comments. I think then:

  • if buffering gcode is ENABLED, upon seeing a M-command that results in a pause being sent, send the command, set uploadFlag = 0, and set the Pause button on front page to Resume. When webcontrol receives the ‘Maslow Paused’ message, send the ‘~’ to clear it so the ok’s can come back and allow gcode to be processed.
1 Like

Turned out easier said then done.

TLDR; I had to make it so that gcode file doesn’t send anything if there are lines in gcode_queue still… i.e., gcode_queue has priority. I don’t think ground control would have this issue.

Long version:

Webcontrol keeps track of where everything was when the pause occurred and makes sure its back to the same position upon resume. For instance, a user manually pauses a run during a cut with the z-axis at -0.25 inches because they think the bit is dull. The user changes the bit, zeroes it out, and presses resume. I think ground control would just continue sending the gcode and wouldn’t adjust the z-axis until it comes across a z-axis change in the gcode. That is, the bit would scrap along the surface for a while until there’s a z-axis change. Webcontrol, however, keeps track of the paused z-axis height and when the user hits resume, it moves there prior to restarting the gcode file cutting. Without getting into too much detail, depending upon where the serialPortThread was when it got the go ahead to start sending gcode (i.e., uploadFlag = 1) then that z-axis height that was put in the gcode_queue might never get sent until the controller pauses again or finishes its run … if gcode buffering was enabled…

1 Like

Might be why grbl and many gcode senders don’t handle tool change. It’s really better to treat each tool as a separate machine job.

1 Like

I also have this problem in GroundControl v1.2.7 where z-axis controls will not work when tool change is requested. Was cutting the Meticulous z-Axis sled and the gcode would set height to 7mm before tool change which made it difficult to set the height of the new bit. Of course I did not realize it was 7mm at the time and assumed .25" which threw the depth more each time the tool change occurred.

Would it be easier/better to automatically set the tool height to a preset whenever a tool change is requested. Then set height back to what it was before gcode resumes?

Slick! Love it @madgrizzle

That makes perfect sense to me.

I think the question in my mind is should we be focusing on maintaining two separate control programs? It seems to me like Web Control took all the best parts of Ground Control and made them better without bringing a lot of the baggage. When Web Control was raspberry pi only I think having both made sense still, but now that there is a Web Control bundled version it seems like the better option in a lot of ways. Is there still value in Ground Control which doesn’t exist in Web Control?

There’s many, many people that are happy using ground control with their existing kits. I suggest considering updating ground control as needed for bug fixes, at a minimum. I tend to consider this issue as a bug that really should be fixed.

1 Like

I agree, it has come up several times

I think we need to have confirmation of people testing webcontrol on all the
different OS options (not just on a Pi, but on other Linux, OS/X and Windows

we also need step-by-step installation instructions for each (ideally with
screenshots) so that we can update the wiki.

Then I think we need to release a GC version that has a pop-up at a startup time
that says it’s being depriciated and replaced with WC, with a link to where to
get WC and a link to the installation instructions.

This is in addition to anything that pops up in testing.

David Lang

1 Like

My 2 cents. It is safer to run separate toolpaths. Both physically and mechanically. Nice big industrial CNCs have a tool changing robot, this is who this function was really designed for. By developing a good workflow each operation can be run by the numbers. For me on the Maslow that is using the center of your work space as the starting point. There will always be exceptions, and my way takes extra time. In the end it’s repeatable and reliable.

Thank you