The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

CNC-3018XL Setup: Table Riser Blocks

After fixing the X axis drive, the CNC-3018XL table moved properly again, so I measured its overall alignment:

3018CNC - table height measurement
3018CNC – table height measurement

The +Y side (on the left in the photo, keeping in mind I’ve rotated the axes) turned out to be 0.7 mm too low, so I made a set of riser blocks to level the tabletop:

Table Riser - solid model
Table Riser – solid model

The 10 mm height would ram the tip of a Pilot pen about 10 mm below the tabletop surface, were it not for the spring-loaded pen holder:

Pilot V5RT holder - installed
Pilot V5RT holder – installed

The 0.7 mm difference in height levels the tabletop:

CNC3018XL - table riser positions
CNC3018XL – table riser positions

The OpenSCAD code produces an SVG outline I intended to use for a foam pad, but then I found a quartet of springs that worked even better:

CNC3018XL - table spring mount
CNC3018XL – table spring mount

So it’s now aligned within ±0.3-ish mm across the surface, with the unflatness of a slab cut from a 1955-era Formica kitchen countertop accounting for most of the difference in a swale from Quadrant III across the origin to Quadrant I.

Which a check plot using an old file shows will be Flat Enough for my simple needs:

CNC3018XL - test plot
CNC3018XL – test plot

Having the camera alignment remain exactly spot on came as a pleasant surprise:

Camera Alignment check
Camera Alignment check

The faded cross to the left came from the table’s previous position; there’s no positive index between the countertop slab and the underlying T-slots.

Part of the motivation for these blocks was to verify PrusaSlicer automagically handles filament / color changes between two objects, as long as OpenSCAD hasn’t unioned them as part of a common transformation. Not having to cut out the socket around the text simplifies the code from what I’d been doing with previous objects.

The OpenSCAD source code as a GitHub Gist:

// CNC 3018 table riser blocks
// Ed Nisley – KE4ZNU
// 2025-06-29
include <BOSL2/std.scad>
Layout = "Show"; // [Show,Build,Outlines]
/* [Hidden] */
HoleWindage = 0.2;
Protrusion = 0.1;
BlockOA = [40.0,30.0,10.0]; // riser block size
SlotBlock = [8.0,BlockOA.y,3.0]; // alignment in slot
BoltOD = 6.0 + HoleWindage; // central bolt
LogoFont = "Fira Sans Condensed:style=SemiBold";
LogoSize = 7.5;
LogoColor = "Red";
LogoThick = 0.4;
//———-
// Define Shapes
module Riser(thick=1,matl="Block") {
LogoText = format_fixed(thick,1);
if (matl == "Text" || matl == "All")
right(BlockOA.x/4) zrot(90)
color(LogoColor)
up(thick + SlotBlock.z + ((matl == "All") ? 0.01 : 0))
text3d(LogoText,LogoThick + ((matl == "All") ? 0.01 : 0),LogoSize,LogoFont,
anchor=TOP,atype="ycenter");
if (matl == "Block" || matl == "All")
difference() {
cuboid(SlotBlock,$fn=8*3,anchor=BOTTOM,rounding=2.0,except=[BOTTOM,TOP]) position(TOP)
cuboid(BlockOA,$fn=8*3,anchor=BOTTOM,rounding=2.0,except=[BOTTOM,TOP]);
down(Protrusion)
zrot(180/6)
cyl(2*BlockOA.z,d=BoltOD,$fn=6,anchor=BOTTOM,circum=true);
}
}
//———-
// Build things
if (Layout == "Show")
down(SlotBlock.z)
Riser(BlockOA.z,matl="All");
if (Layout == "Outlines") {
projection(cut=false)
Riser(BlockOA.z,matl="Block");
}
if (Layout == "Build") {
up(BlockOA.z + SlotBlock.z) xrot(180)
Riser(BlockOA.z,matl="Block");
up(BlockOA.z + SlotBlock.z) xrot(180)
Riser(BlockOA.z,matl="Text");
}