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.

Bike Rack Tray Holder: Stretchy Tiedown Straps

The tray holder on Mary’s bike worked well:

Bike Rack Tray Holder - in use
Bike Rack Tray Holder – in use

Except for having the bungee cord run across the middle of the tray where it blocks access for larger trays and tends to bend the taller leaves.

Well, I can fix that:

Bike Rack Tray Holder - straps - rear
Bike Rack Tray Holder – straps – rear

The front tiedown is similar:

Bike Rack Tray Holder - straps - front
Bike Rack Tray Holder – straps – front

They’re printed from TPU: rectangular blocks and chains, ending in wire hooks bashed from a coat hanger. The M4 button-head screws thread into (uncrushed) rivnuts, which seemed easier to manage than square nuts in this situation.

The chains are just thick circles, with half of the top links sunk into the blocks:

Stretchy Straps - build layout
Stretchy Straps – build layout

You’d (well, I’d) want to build them one at a time, because sometimes this happens:

Bike Rack Tray Holder - bad platform adhesion
Bike Rack Tray Holder – bad platform adhesion

Based on those measurements, I raised the extruder by 0.1 mm, but apparently did a poor job of cleaning / flattening the cold TPU on the nozzle and got it wrong. As a result, the first layer didn’t get squooshed properly onto the BuildTak, came unstuck, and produced art . The track down the middle of the photo shows traces of a previous, badly over-squooshed test chain.

The stretched TPU relaxes enough to leave very little tension after a day, as shown by the unhooked right chain:

Bike Rack Tray Holder - straps - relaxing
Bike Rack Tray Holder – straps – relaxing

However, that make the chains exactly the right length, so they require even more force to get the hooks off the rack. After relaxing for another day, the stretched chains return to roughly their original lengths, so it’s all good.

The OpenSCAD source code as a GitHub Gist:

// TPU Tiedown Straps for bike rack tray holder
// Ed Nisley – KE4ZNU
// 2026-05-14
include <BOSL2/std.scad>
Layout = "Build"; // [Show,Build,Chain,Blocks,Front,Rear]
/* [Hidden] */
HoleWindage = 0.2;
Protrusion = 0.01;
NumSides = 4*3*2*4;
Gap = 5.0;
$fn=NumSides;
LinkID = 7.0;
LinkOD = 10.0;
LinkOC = 14.0;
LinkHeight = 4.0;
JointWidth = 2.0;
FrontChainAngle = 30; // from vertical
FrontChainLength = 80.0; // nominal length
RearChainAngle = 20; // from vertical
RearChainLength = 100.0; // nominal length
BlockOA = [80.0,12.0,15.0];
InsertOC = 30.0;
//—–
// Define things
module Chain(n=2) {
render()
difference() {
union() {
hull() {
cyl(LinkHeight,d=JointWidth,anchor=BOTTOM,rounding=0.0);
back((n – 1)*LinkOC)
cyl(LinkHeight,d=JointWidth,anchor=BOTTOM,rounding=0.0);
}
for (i = [0:n-1])
back(i*LinkOC)
cyl(LinkHeight,d=LinkOD,anchor=BOTTOM,rounding=0.0);
}
for (i = [0:n-1])
back(i*LinkOC)
down(Protrusion)
cyl(LinkHeight + 2*Protrusion,d=(LinkID + HoleWindage),anchor=BOTTOM,rounding=-1.0);
}
}
module FrontBlock() {
difference() {
cuboid(BlockOA,anchor=BOTTOM,chamfer=1.0,except=BACK);
for (i = [-1:1])
right(i*InsertOC) down(Protrusion) {
cyl(BlockOA.z + 2*Protrusion,d=4.0 + HoleWindage,anchor=BOTTOM); // screw clearance
cyl(1.5,d=9.0,anchor=BOTTOM); // insert head
cyl(11.0,d=6.0,anchor=BOTTOM); // insert body
}
}
}
module RearBlock() {
up(BlockOA.z/2) fwd(BlockOA.y/2)
difference() {
cuboid(BlockOA,anchor=FRONT,chamfer=1.0,except=BACK);
for (i = [-1:1])
right(i*InsertOC) fwd(Protrusion) {
ycyl(BlockOA.z + 2*Protrusion,d=4.0 + HoleWindage,anchor=FRONT); // screw clearance
ycyl(1.5,d=9.0,anchor=FRONT); // insert head
ycyl(11.0,d=6.0,anchor=FRONT); // insert body
}
}
}
module FrontAssembly(cl=FrontChainLength,ca=FrontChainAngle) {
Links = ceil(cl / LinkOC);
union() {
up(cl*cos(ca)) {
FrontBlock();
back(BlockOA.y/2)
xrot(90)
for (i = [-1,1])
left(i*InsertOC/2)
zrot(-i*ca + 180)
Chain(Links);
}
}
}
module RearAssembly(cl=RearChainLength,ca=RearChainAngle) {
Links = ceil(cl / LinkOC);
union() {
up(cl*cos(ca)) {
RearBlock();
back(BlockOA.y/2)
xrot(90)
for (i = [-1,1])
left(i*InsertOC/2)
zrot(-i*ca + 180)
Chain(Links);
}
}
}
//—–
// Build things
if (Layout == "Chain")
Chain();
if (Layout == "Blocks") {
fwd(BlockOA.y)
FrontBlock();
back(BlockOA.y)
RearBlock();
}
if (Layout == "Front")
FrontAssembly();
if (Layout == "Rear")
RearAssembly();
if (Layout == "Show") {
fwd(BlockOA.y)
FrontAssembly();
back(BlockOA.y)
zrot(180)
RearAssembly();
}
if (Layout == "Build") {
fwd(BlockOA.z + Gap/2)
up(BlockOA.y/2)
xrot(-90)
down(FrontChainLength*cos(FrontChainAngle))
FrontAssembly();
back(BlockOA.z + Gap/2)
zrot(180)
up(BlockOA.y/2)
xrot(-90)
down(RearChainLength*cos(RearChainAngle))
RearAssembly();
}

Comments

Spam comments get trashed, so don’t bother. Comment moderation may cause a delay.