Carbide Create Pro Post Processor for Onefinity (M6 Tool Change)

Folks, I don’t know if anyone has written this down before; if they have, I can’t find it, or what I did find was way complicated.

I did use AI to help me write the JavaScript. I had two developer friends review the code, and they said it was safe and good enough for our level of work. I am not a developer, I am a Network Architect and aspiring wood butcher. I hope this post helps someone. The code has made my workflow so much easier and the machine more enjoyable to use.

I am speaking and working from what I have learned using my machine for the last 3 years. I am by no means an expert or know everything about the device, but I do know I am making better workpieces each time I run the machine.

The Problem

Carbide Create’s built-in GRBL post processor outputs M0 ;T102 for tool changes. This is just a pause - it doesn’t trigger Onefinity’s tool-change macro.

The Onefinity controller only runs the code in the tool-change field when it sees an M6 command.

The Solution

A custom post-processor that outputs M6 T102 instead of M0 ;T102, plus a simple tool-change macro.

What Changes in the G-Code

Before (GRBL post-processor):

M05
M0 ;T102
M03S18000

After (Onefinity post-processor):

M05
(Move to safe Z to avoid workholding)
G53G0Z-5.000
(TOOL/MILL,3.17, 0.00, 0.00, 0.00)
M6T102
M03S18000

The key differences:

  • M6T102 replaces M0 ;T102 - This triggers the tool-change macro

  • Adds a safe Z move before tool change using machine coordinates

  • Adds tool dimension comments for visualization software


Requirements

  • Carbide Create Pro (subscription or perpetual license)

  • Onefinity with Buildbotics controller (firmware 1.4.0+)


Installing the Post Processor

  1. Download onefinity.post (link to your file)

  2. Open Carbide Create Pro

  3. Go to About → Open Data Directory

  4. Open the Posts folder

  5. Copy onefinity.post into the Posts folder

  6. Restart Carbide Create Pro

  7. Go to Edit → Select Post Processor

  8. Select “Onefinity (M6 Tool Change)”


Tool Change Macro

On your Onefinity controller:

  1. Tap the hamburger menu (☰) → Settings

  2. Scroll to the GCODE section

  3. Find the tool-change field

  4. Paste this code:

(Runs on M6, tool change)
M5
G4 P2
G53 G0 Z-1
M0 (MSG, Change Tool - Press Continue when ready)
  1. Tap Save

What this does:

  • M5 - Stops the spindle

  • G4 P2 - Waits 2 seconds for the spindle to stop

  • G53 G0 Z-1 - Moves Z to 1mm from the machine top (maximum clearance)

  • M0 - Pauses and displays a message to change the tool

After you press Continue, the G-code takes over - it starts the spindle and moves to the first cut position.


The Post-Processor Code

javascript

description = "Onefinity (M6 Tool Change)"
notes = "Onefinity Buildbotics controller with M6 tool changes for proper tool change macro support"
vendor = "Onefinity"
vendorUrl = "https://www.onefinitycnc.com"

outputMetric = true
extension = "nc"

function createFormatter(name, numDigits, useUnits) {
    var lastOutput = ""
    return {
        format: function(val) {
            if(val == undefined) {
                this.reset();
                return "";
            }
            if(!outputMetric && useUnits) {
                val = val / 25.4
            }
            var fmt = val.toFixed(numDigits);
            var ret = ""
            if(fmt != lastOutput) {
                ret += name;
                ret += fmt;
                lastOutput = fmt;
            }
            return ret;
        },
        reset: function() {
            lastOutput = "--"
        }
    }
}

function formatUnits(val) {
    var un = "mm"
    var digits = 2;
    if(!outputMetric) {
        val /= 25.4;
        un = "in"
        digits += 1;
    }
    return "" + val.toFixed(digits) + un
}

function formatDimension(val) {
    var digits = 2;
    if(!outputMetric) {
        val /= 25.4;
        digits += 1;
    }
    return "" + val.toFixed(digits)
}

function outputSafeZ() {
    if(state.wasHomeZ) {
        return
    }
    var distFromTop = -5
    if(!outputMetric) {
        distFromTop /= 25.4
    }
    onComment("Move to safe Z to avoid workholding")
    writeLn("G53G0Z"+distFromTop.toFixed(3))
    state.motionMode.reset()
    state.zval.reset()
    state.wasHomeZ = true
}

function writeBlock(){
    var ln = ""
    for (var i = 0; i < arguments.length; i++) {
        ln += arguments[i];
    }
    writeLn(ln);
}

var state = {}

function onOpen() {
    var coordDigits = outputMetric?3:4
    state.xval = createFormatter("X", coordDigits, true)
    state.yval = createFormatter("Y", coordDigits, true)
    state.zval = createFormatter("Z", coordDigits, true)
    state.fval = createFormatter("F", 1, true)
    state.sval = createFormatter("S", 0)
    state.tval = createFormatter("T", 0)
    state.motionMode = createFormatter("G", 0)
    state.rpm = 0
    state.wasHomeZ = false
    state.lastTool = -1
    state.spindle = false

    onComment("Design File: " + sourceFilename);
    onComment("stockMin:"+formatUnits(stockMinX) + ", " + formatUnits(stockMinY) + ", " + formatUnits(stockMinZ))
    onComment("stockMax:"+formatUnits(stockMaxX) + ", " + formatUnits(stockMaxY) + ", " + formatUnits(stockMaxZ))

    var length = stockMaxX - stockMinX;
    var width = stockMaxY - stockMinY;
    var height = stockMaxZ - stockMinZ;
    onComment("STOCK/BLOCK,"+formatDimension(length) + ", " + formatDimension(width) + ", " + formatDimension(height) + "," + formatDimension(-stockMinX) + ", " + formatDimension(-stockMinY) + ", " + formatDimension(-stockMinZ))

    writeLn("G90")
    if(outputMetric) {
        writeLn("G21")
    } else {
        writeLn("G20")
    }
    outputSafeZ();
}

function onClose() {
    if(state.spindle) {
        writeLn("M05");
    }
    writeLn("M02");
}

function onTerminate() {
}

function onComment(text) {
    writeLn("(" + text + ")");
}

function onLinear(x, y, z, feed) {
    if(state.wasHomeZ) {
        writeBlock(state.motionMode.format(1), state.xval.format(x), state.yval.format(y), state.fval.format(feed));
        state.wasHomeZ = false
    }
    var xv = state.xval.format(x);
    var yv = state.yval.format(y);
    var zv = state.zval.format(z);
    if(xv != "" || yv != "" || zv != "") {
        writeBlock(state.motionMode.format(1), xv, yv, zv, state.fval.format(feed));
    }
}

function onRapid(x, y, z) {
    if(state.wasHomeZ) {
        writeBlock(state.motionMode.format(0), state.xval.format(x), state.yval.format(y));
        state.wasHomeZ = false
    }
    writeBlock(state.motionMode.format(0), state.xval.format(x), state.yval.format(y), state.zval.format(z));
}

function onSpindleSpeed(s) {
    if(s <= 0) {
        state.spindle = false
        writeBlock("M05")
    } else {
        state.spindle = true
        state.sval.reset()
        writeBlock("M03", state.sval.format(s))
    }
    state.rpm = s
}

function onSection() {
    var tnum = toolNumber
    var force = forceToolChange

    if(tnum != state.lastTool || force == true) {
        state.lastTool = tnum
        onSpindleSpeed(0)
        outputSafeZ()
        onComment("TOOL/MILL,"+formatDimension(toolDiameter) + ", "+ formatDimension(toolCornerRadius) + ", " + formatDimension(toolFluteLength) + ", " + Number(toolAngle).toFixed(2))
        state.tval.reset()
        writeBlock("M6", state.tval.format(tnum));
        state.xval.reset()
        state.yval.reset()
        state.zval.reset()
        state.fval.reset()
        state.wasHomeZ = true
    }
}

Notes

  • I use collared bits, so all tools are the same length - no re-probing needed after tool change

  • Output is always metric (G21)

  • Tested with 1/8" and 1/4" endmills, 10° to 90° vee bits, and engraving

1 Like