Phone Charging Dock
A desk block that charges a phone and an earbuds case or a smartwatch in one place. The phone stands in a slot reclined 15 deg and drops onto a USB-C plug held in a pocket below the slot floor; the second cradle either holds an earbuds case upright on its own plug or takes a round watch charger puck in a shallow pocket. Every cable runs in a groove on the underside and leaves at the back, so the top stays clean. The default phone + earbuds version is 161 x 64 x 48 mm (146 x 64 x 48 mm with the watch cradle). Print it flat on its bottom with no supports: the grooves and plug pockets are open to the bed and only short bridges remain. Presets switch between phone + earbuds and phone + watch and between slim, cased and rugged phones, and phone thickness and width, recline angle, earbuds case and puck size, plug body size and cable diameter are all adjustable.
# Charging Dock - desk block with a reclined phone slot, an earbuds or watch cradle and cable channels underneath
# How it is used: thread each charging cable's plug up from the bottom into its pocket, so only the
# metal tip pokes through the slot floor; press the cable into its groove on the underside so it leaves
# at the back; then stand the phone in the slot (it leans back by `recline`) and drop the earbuds case
# onto its plug, or lay the watch charger puck in its round pocket.
# Print orientation: as modeled, flat bottom on the bed. The cable grooves and plug pockets are open to
# the bottom, so they print as open channels; the only bridges are the groove and pocket ceilings (< 10 mm).
# Coordinates: X = width (phone left, accessory right), Y+ = back (cables exit there), Z+ = up.
@profile {
"Phone + earbuds": { accessory: "earbuds", phone_t: 11.0, phone_w: 78 },
"Phone + watch": { accessory: "watch", phone_t: 11.0, phone_w: 78 },
"Slim phone (no case) + earbuds": { accessory: "earbuds", phone_t: 8.5, phone_w: 74 },
"Large phone, rugged case + watch": { accessory: "watch", phone_t: 15.0, phone_w: 84 }
}
@param choices:["earbuds", "watch"] group:"Accessory" desc:"Second cradle: upright earbuds case on a USB-C plug, or a round watch charger puck"
accessory = "earbuds"
@param 6..18 step:0.5 group:"Phone" desc:"Phone thickness including case (mm)"
phone_t = 11.0
@param 60..95 group:"Phone" desc:"Phone width including case (mm)"
phone_w = 78
@param 5..25 group:"Phone" desc:"Phone recline from vertical (deg)"
recline = 15
@param 35..65 step:0.5 group:"Accessory" desc:"Earbuds case width (mm)"
case_w = 50.0
@param 15..35 step:0.5 group:"Accessory" desc:"Earbuds case thickness (mm)"
case_t = 24.0
@param 20..45 step:0.5 group:"Accessory" desc:"Watch charger puck diameter (mm)"
puck_d = 28.0
@param 4..14 step:0.5 group:"Accessory" desc:"Watch charger puck thickness (mm)"
puck_t = 8.0
@param 9..16 step:0.5 group:"Cable" desc:"Plug body (overmold) width (mm)"
plug_w = 12.5
@param 5..9 step:0.5 group:"Cable" desc:"Plug body (overmold) thickness (mm)"
plug_t = 7.0
@param 10..30 group:"Cable" desc:"Plug body length incl. strain relief, without the metal tip (mm)"
plug_len = 18
@param 2.5..6 step:0.5 group:"Cable" desc:"Cable diameter (mm)"
cable_d = 4.0
# Fixed design values
slot_clear = 1.0 # phone slot: total clearance over the phone thickness
side_clear = 2.0 # phone slot: total clearance over the phone width
fit = 0.5 # plug pocket clearance per side (loose fit, cable pass-through class)
tip_w = 10.0 # opening for the USB-C metal tip (9.5 x 3.5 + clearance)
tip_t = 4.0
ceil_t = 1.2 # floor between the slot and the plug pocket (the tip pokes ~5 mm through)
bend_h = 8 # room under the plug body for the cable to bend into its groove
slot_depth = 22 # phone slot depth along the phone axis
margin = 9 # outer wall beside the slots
gap = 12 # wall between the two cradles
depth = 64 # block depth (Y)
phone_y = -12 # Y of the phone slot floor centre (front of centre: the phone leans back)
corner_r = 6 # vertical corner radius
top_r = 2.5 # top edge radius
# Derived sizes
slot_g = phone_t + slot_clear
slot_l = phone_w + side_clear
floor_z = bend_h + plug_len + ceil_t # height of both cradle floors
height = floor_z + slot_depth * cos(recline)
pw = plug_w + 2 * fit
pt = plug_t + 2 * fit
gw = cable_d + 1.5 # cable groove width
gh = cable_d + 2.0 # cable groove height
acc_w = if accessory == "earbuds" then case_w + 1.0 else max(puck_d + 8, pw + 8)
width = margin + slot_l + gap + acc_w + margin
phone_x = -width / 2 + margin + slot_l / 2
acc_x = width / 2 - margin - acc_w / 2
# Block
block = box width depth height
| translate 0 0 (height / 2)
| edges =Z | fillet corner_r
| edges >Z | fillet top_r
# --- Phone slot, built upright at the origin and then tilted back about X ---
# Local frame: z = 0 is the slot floor, +z runs up along the phone.
def tilt(s) = s | rotate (-recline) 0 0 | translate phone_x phone_y floor_z
p_slot = tilt(box slot_l slot_g 60 | translate 0 0 30)
p_tip = tilt(box tip_w tip_t (ceil_t + 2) | translate 0 0 (-ceil_t / 2))
p_pocket = tilt(box pw pt 80 | translate 0 0 (-ceil_t - 40))
# Where the plug pocket reaches the bed: the phone groove starts here
p_y0 = phone_y - floor_z * tan(recline)
p_groove = box gw (depth / 2 - p_y0 + 2) gh
| translate phone_x ((depth / 2 + p_y0) / 2 + 1) (gh / 2 - 0.5)
# --- Accessory cradle ---
acc_y = 0
# Earbuds: upright pocket for the case, plug from below as for the phone
case_r = min(6, case_t / 2 - 0.5)
e_pocket = box (case_w + 1) (case_t + 1) 40
| edges =Z | fillet case_r
| translate acc_x acc_y (floor_z + 20)
e_tip = box tip_w tip_t (ceil_t + 2) | translate acc_x acc_y (floor_z - ceil_t / 2)
e_plug = box pw pt 60 | translate acc_x acc_y (floor_z - ceil_t - 30)
e_cut = e_pocket + e_tip + e_plug
# Watch: round puck pocket (puck stands 1 mm proud) with a cable shaft at its back rim,
# straight through to the groove; the shaft is large enough to pass the far-end plug
w_y = -4
w_r = puck_d / 2 + 0.3
w_pocket = cylinder w_r (puck_t - 1 + 1) | translate acc_x w_y (height - (puck_t - 1) / 2 + 0.5)
w_shaft_y = w_y + w_r
w_shaft = box pw pt (height + 2) | translate acc_x w_shaft_y (height / 2)
w_cut = w_pocket + w_shaft
a_cut = if accessory == "earbuds" then e_cut else w_cut
a_y0 = if accessory == "earbuds" then acc_y else w_shaft_y
a_groove = box gw (depth / 2 - a_y0 + 2) gh
| translate acc_x ((depth / 2 + a_y0) / 2 + 1) (gh / 2 - 0.5)
dock = block - p_slot - p_tip - p_pocket - p_groove - a_cut - a_groove
# --- Checks ---
# Dummy devices sitting in their cradles must not touch the dock
phone = tilt(box phone_w phone_t 150 | translate 0 0 75.05)
case = box case_w case_t 50 | edges =Z | fillet case_r | translate acc_x acc_y (floor_z + 25.05)
puck = cylinder (puck_d / 2) puck_t | translate acc_x w_y (height + 1 - puck_t / 2)
acc = if accessory == "earbuds" then case else puck
assert dock.solids == 1
assert dock.valid
assert dock.bbox.x ~ width
assert dock.bbox.y ~ depth
assert dock.bbox.z ~ height
assert abs(dock.bbox.min[2]) < 0.01
assert clear(dock, phone)
assert clear(dock, acc)
assert floor_z - (ceil_t + plug_len) * cos(recline) >= bend_h - 1 # plug body ends above the bend room
assert pt <= 10 and gw <= 10 # bridges over pockets and grooves stay short
assert p_y0 - pt > -depth / 2 + 3 # tilted plug pocket stays inside the front wall
print "size W x D x H" dock.bbox.x dock.bbox.y dock.bbox.z
print "floor z" floor_z
dock