Reusable Cable Tie
A reusable ratchet cable tie for bundling cables, hoses or garden stems, printed flat in one piece. The strap carries sawtooth teeth with a 90 deg locking flank and a 45 deg ramp (2.5 mm pitch, 1 mm deep); a flexible pawl inside the head lets the strap pull through in one direction and wedges into the teeth if it is pulled back. To reuse it, push the release tab on the side of the head and slide the strap out. The default tie is 163 x 20 x 10.4 mm with an 8 x 1.2 mm strap and holds bundles from 10 to about 37 mm across. Every feature is a straight vertical extrusion, so it prints as laid out, with the strap standing on its 1.2 mm edge, and needs no supports; PETG or nylon is recommended, while PLA is fine for one-off use on larger bundles. Adjustable parameters: strap length, width and thickness, tooth pitch, smallest bundle diameter, and an optional M3 or M4 screw-mount flange.
# Reusable Cable Tie - ratchet strap with a releasable pawl
#
# Print flat as shown, on Z = 0, no supports. Everything is an XY profile extruded
# along Z, so the strap is thin in the print plane and bends easily in that plane.
#
# How it works (top view, the head sits at the origin):
# - The strap leaves the head along +X. Its sawtooth teeth are on the -Y face.
# - Wrap the strap counterclockwise around the bundle (the bundle sits on top of
# the head, to the right of the slot) and push the tip down (-Y) into the slot
# at the top of the head. The strap end rises about 1 mm in Z as it wraps (a
# gentle coil) so it sits between the floor and roof of the entry eyelet.
# - Inside the head a flexible pawl hangs from the eyelet. Its tooth rides over
# the 45 deg ramps as the strap is pulled through and drops behind the 90 deg
# flank of the next tooth, so the strap cannot back out. A backward pull
# pushes the pawl tip into the teeth (self-locking).
# - Release: hold the bundle so the strap is not under tension, then push the
# release tab on the outside of the pawl up toward the strap entry (or hook it
# outward with a fingernail). The pawl tooth lifts clear and the strap slides back
# out, ready to be used again.
# Material: PETG or nylon recommended (springy, survives many cycles). PLA works
# for one-off use on larger bundles but the pawl and a tightly bent strap may
# crack after repeated use. Very soft TPU is too floppy to hold the ratchet.
# --- Parameters ---------------------------------------------------------------
@param 80..240 step:5 desc:"Strap length, from the head to the tip (mm)" group:"Strap"
strap_len = 150
@param 5..12 step:0.5 desc:"Strap width = print height (mm)" group:"Strap"
strap_w = 8
@param 1.0..2.0 step:0.1 desc:"Strap thickness under the teeth (mm)" group:"Strap"
strap_t = 1.2
@param 1.8..4 step:0.1 desc:"Tooth pitch (adjustment step, mm)" group:"Strap"
pitch = 2.5
@param 6..40 step:1 desc:"Smallest bundle diameter (teeth start here, mm)" group:"Bundle"
bundle_min = 10
@param choices:["none", "M3", "M4"] desc:"Screw-mount flange with a hole for this screw" group:"Mount"
mount = "none"
# --- Derived dimensions -------------------------------------------------------
td = 1.0 # tooth depth (strap teeth)
land = pitch - td # flat land on top of each tooth
c_land = 0.3 # gap between tooth lands and the pawl face
sx = strap_t + td + c_land # slot width (strap travels along Y in it)
e_p = sx - strap_t - 0.1 # pawl tooth height (stops 0.1 short of the valley floor)
engage = e_p - c_land # how deep the pawl tooth sits below the tooth lands
e_len = 5 # entry eyelet length (Y), closed by floor and roof
floor_t = 0.8 # eyelet floor (on the bed)
roof_t = 1.0 # eyelet roof (a short bridge over the slot)
cz = 0.3 # strap clearance to floor and to roof
H = floor_t + cz + strap_w + cz + roof_t # head height
pt = 1.4 # pawl beam thickness (X)
pawl_l = 12 # pawl beam length (Y), eyelet to lock face
y_tip = -e_len - pawl_l # lock face of the pawl tooth
tab_l = 6 # release tab, sticking out of the pawl (X)
tab_h = 2.4 # release tab thickness (Y)
pw = 3 # -X post of the eyelet (pawl root)
bw = 3 # +X backing wall the strap rides on
y_bot = y_tip - 3 # bottom of the backing wall (the tail exits here)
x_root = sx + bw # strap root
taper = 10 # tip taper, no teeth
gap = 0.3 # minimum clearance around the free pawl
# Loop geometry: the bundle touches the head top (y = 0) and the strap in the slot
# (x = sx). Strap arc length from the root to the pawl lock face for a bundle D:
# bottom run (D/2 - bw) + three quarter turn on radius (D + t)/2 + down run (D/2 + 17)
def s_lock(d) = d - bw + 0.75 * pi * (d + strap_t) - y_tip
s0 = s_lock(bundle_min) - pitch # first tooth
s1 = strap_len - taper # last tooth ends here
n_teeth = floor((s1 - s0) / pitch)
d_max = (s1 + bw + y_tip - 0.75 * pi * strap_t) / (1 + 0.75 * pi)
# --- Helpers ------------------------------------------------------------------
def r2(x0, y0, x1, y1) = rect (x1 - x0) (y1 - y0) at:((x0 + x1) / 2, (y0 + y1) / 2)
def bx(x0, y0, x1, y1, z0, z1) = box (x1 - x0) (y1 - y0) (z1 - z0) | translate ((x0 + x1) / 2) ((y0 + y1) / 2) ((z0 + z1) / 2)
# --- Head ---------------------------------------------------------------------
post = r2(-pw, -e_len, 0, 0)
wall = r2(sx, y_bot, x_root, 0)
pawl = r2(-pt, y_tip, 0, -e_len + 0.2)
# pawl tooth: 90 deg lock face at the bottom (y_tip), 45 deg ramp facing the entry
ptooth = polyline [(-0.2, y_tip), (e_p, y_tip), (-0.2, y_tip + e_p + 0.2)]
tab = r2(-pt - tab_l, y_tip, -pt + 0.2, y_tip + tab_h)
rootf = polyline [(-pt + 0.2, -e_len + 0.2), (-pt + 0.2, -e_len - 0.8), (-pt - 0.8, -e_len + 0.2)]
gusset = polyline [(x_root - 0.2, -strap_t + 0.2), (x_root + 3, -strap_t + 0.2), (x_root - 0.2, -strap_t - 3)]
head2d = union [post, wall, pawl, ptooth, tab, rootf, gusset]
eyelet = union [bx(-0.2, -e_len, sx + 0.2, 0, 0, floor_t), bx(-0.2, -e_len, sx + 0.2, 0, H - roof_t, H)]
head = (head2d | extrude H) + eyelet
# --- Strap (printed straight along +X, teeth on the -Y face) --------------------
# Each tooth, root to tip: 90 deg flank, flat land, 45 deg ramp toward the tip.
xa = x_root + strap_len - taper
back = r2(x_root - 0.2, -strap_t, xa + 0.01, 0)
tip = polyline [(xa, 0), (xa + taper, -strap_t * 0.25), (xa + taper, -strap_t * 0.75), (xa, -strap_t)]
teeth = [polyline [(x_root + s0 + k * pitch, -strap_t + 0.1),
(x_root + s0 + k * pitch, -strap_t - td),
(x_root + s0 + k * pitch + land, -strap_t - td),
(x_root + s0 + (k + 1) * pitch, -strap_t + 0.1)] for k in range(n_teeth)]
strap = union [back, tip] | union teeth | extrude strap_w
# --- Optional screw-mount flange (screw axis along X, wall on the -X side) --------
# Fit the screw before wrapping the strap: the driver comes in from +X above the head.
hole_d = if mount == "M3" then 3.4 else 4.5
x_w = -pt - tab_l - 3 # wall plane, clear of the tab travel
ft = 3 # flange thickness
fl_top = 11
hz = H / 2
hy = fl_top - 5.5
flange = bx(x_w, -e_len, x_w + ft, fl_top, 0, H) + bx(x_w + ft - 0.2, -e_len, -pw + 0.2, 0, 0, H)
# teardrop hole so it prints without support
bore = (cylinder (hole_d / 2) (ft + 2) | rotate 0 90 0 | translate (x_w + ft / 2) hy hz)
+ (box (ft + 2) (hole_d / 2) (hole_d / 2) | rotate 45 0 0 | translate (x_w + ft / 2) hy (hz + hole_d / 2 * 0.7071))
mounted = (head + strap + flange) - bore
tie = if mount == "none" then head + strap else mounted
# --- Checks -------------------------------------------------------------------
fixed = (union [post, wall, gusset] | extrude H) + eyelet + strap
pawl_free = (union [pawl, ptooth, tab] | extrude H) * bx(-20, y_tip - 1, 10, -e_len - 0.01, -1, H + 1)
# clearance envelope around the free pawl, grown by gap on every free side
pawl_zone = bx(-pt - tab_l - gap, y_tip - gap, e_p + gap, -e_len - gap, 0, H)
assert tie.solids == 1
assert tie.valid
assert clear(pawl_zone, fixed) # pawl is not fused to the wall, floor or roof
assert clear(pawl_free, fixed)
assert sx >= strap_t + td + 0.3 # strap (teeth included) slides through the slot
assert sx - e_p >= strap_t + 0.1 # strap backbone passes under the pawl tooth
assert (H - roof_t - floor_t) >= strap_w + 0.6 # eyelet tunnel height vs strap width
assert engage >= 0.6 # pawl tooth sits well below the tooth lands
assert land >= 0.6 # pitch leaves a real land on each tooth
assert strap_t >= 1.0
assert n_teeth >= 5
assert d_max >= bundle_min + 5
assert tie.bbox.z ~ H
assert strap.bbox.z ~ strap_w
assert strap.bbox.x ~ strap_len + 0.2 tol:0.05
print "slot (mm)" sx "strap section" (strap_t + td) "x" strap_w "eyelet tunnel" (H - roof_t - floor_t)
print "teeth" n_teeth "pitch" pitch "depth" td "flanks 90 / 45 deg, engagement" engage
print "bundle range (mm)" bundle_min "to" d_max
print "head height" H
tie