Geneva Drive
A desk-top Geneva drive (Maltese cross) that shows intermittent motion: turn the D-shaped knob once and the slotted wheel advances exactly one slot, then stays locked by the disc while the knob keeps turning. Every dimension is derived from the slot count n, the center distance a and the print clearance: the pin radius a sin(180/n), the wheel radius a cos(180/n), the slot length, the locking arcs and the locking disc whose horns sit exactly on the pin entry and exit directions, and the wheel's angle as a function of the drive angle is checked at nine positions of the stroke and in the dwell. Three parts on a base plate with two axle pins: the drive (crank plate, pin, locking disc and knob in one piece), the six-slot wheel and the plate; assembled it is 97 x 69 x 23 mm, the print layout 120 x 95 x 19 mm. Print all parts flat as laid out, no supports: the drive stands on its crank plate, the wheel and the plate lie flat with the pins pointing up. Parameters: number of slots (4 to 8), center distance, pin radius, clearance, wheel thickness, knob height and the drive angle shown in the assembled view.
# Geneva Drive - Maltese cross indexing mechanism, every size derived from the slot count
#
# Turn the D-shaped knob and the slotted wheel advances one slot per turn, then
# stays locked while the knob keeps turning. Three printed parts on a base plate:
# the drive (crank plate + pin + locking disc + knob in one piece), the driven
# wheel with n slots, and the base plate with the two axle pins.
#
# Geometry (standard external Geneva, all from n, the center distance a and the
# print clearance c):
# pin crank radius R = a sin(180/n) the pin enters a slot tangentially
# wheel radius rw = a cos(180/n) R^2 + rw^2 = a^2
# slot from radius a - R (pin at mid-stroke) out through the rim,
# width 2 (pin_r + c)
# locking disc rl = R - sqrt(2 rw c + c^2): the disc is a circle rl cut by
# a circle rw + c about the wheel center at mid-stroke. With
# this rl the two horns of the "D" lie exactly on the pin
# entry/exit directions +-(90 - 180/n), so the lock lets go
# at the instant the pin enters a slot and grips again when
# it leaves. If that would leave the wheel tips thinner
# than 1.2 mm (big pin, small clearance) rl is reduced and
# the lock lets go a fraction of a degree early instead.
# locking arcs radius rl + c, centered a from the wheel center, halfway
# between slots
# kinematics theta(phi) = -atan2(sin phi, a/R - cos phi) while the pin
# is in a slot (|phi| <= 90 - 180/n), constant in the dwell.
#
# Stack (from the base plate up): spacer ring, drive crank plate, gap, driven
# wheel at the same level as the locking disc and the pin, knob above.
# The driven wheel rests on a boss around its axle pin; the drive sits on a short
# axle pin in a blind hole under its crank plate.
#
# Print orientation: all parts flat as laid out (part = "print"), no supports:
# the drive stands on its crank plate (pin and knob point up), the wheel and the
# plate lie flat with the axle pins up.
# Use: push the wheel onto its pin, drop the drive onto its pin, turn the knob.
@param choices:["assembled", "print", "drive", "driven", "base"] desc:"Output: the assembled mechanism, the print layout, or one part"
part = "assembled"
@param 4..8 desc:"Number of slots n (stops per revolution of the wheel)" group:"Geneva"
n = 6
@param 30..50 step:1 desc:"Center distance a between the two axles (mm)" group:"Geneva"
a = 40
@param 1.5..3 step:0.25 desc:"Drive pin radius (mm)" group:"Geneva"
pin_r = 2.0
@param 0.15..0.5 step:0.05 desc:"Print clearance c of every moving fit, per side (mm)" group:"Geneva"
c = 0.3
@param -180..180 step:5 desc:"Drive angle in the assembled view (deg); the pin is in a slot while |angle| <= 90 - 180/n" group:"View"
angle = 20
@param 3..6 step:0.5 desc:"Thickness of the driven wheel and the locking disc (mm)" group:"Build"
t = 4.0
@param 6..14 step:1 desc:"Knob height above the wheel (mm)" group:"Build"
knob_h = 8
# ---------------------------------------------------------------- derived geometry
half = 180 / n # half slot pitch (deg)
R = a * sin(half) # pin crank radius
rw = a * cos(half) # driven wheel radius
phie = 90 - half # pin enters at -phie, leaves at +phie
w = pin_r + c # slot half width
rl_lock = R - sqrt(2 * rw * c + c * c) # disc radius with the horns exactly on the entry/exit directions
rl = min(rl_lock, R - w - c - 1.2) # locking disc radius; smaller if the wheel tips would get thinner than 1.2
tip = R - w - (rl + c) # wheel tip width between slot edge and locking arc
horn = acos((rl ** 2 + a ** 2 - (rw + c) ** 2) / (2 * a * rl)) # horn direction of the D (deg from the pin)
rp = R + pin_r + 1.2 # crank plate radius
tp = 6 # crank plate thickness
gz = 0.6 # gap between crank plate and wheel
sp = 0.6 # spacer ring height on the base plate
pt = 4 # base plate thickness
pw = 12 # base plate half width
ax1 = 4 # drive axle radius
ax2 = 3 # driven axle radius
hole_d = tp - 1 # blind hole depth under the crank plate
z_drive = pt + sp # bottom of the drive
z_driven = z_drive + tp + gz # bottom of the driven wheel
assert tip >= 1.19 # the wheel tips keep some meat (1.2 by design)
assert horn <= phie + 0.01 # the lock never holds past the pin entry direction
assert a - rw - c > 1.5 # the D keeps material in front of the axle
assert rp + c < a - 6.5 # crank plate clears the wheel's boss
assert a - R - w > ax2 + c + 2 # slot bottom clears the wheel's axle hole
# Driven wheel angle (deg, CCW about its own axle) as a function of the drive
# angle (deg, CCW). Slot 0 points at the drive axle when phi = 0.
def theta(phi) = if phi < -phie then half
else if phi > phie then -half
else -atan2(sin(phi), 1 / sin(half) - cos(phi))
# ---------------------------------------------------------------- driven wheel (local: axle at origin, slot 0 along -X)
slot = wire [(-(a - R), 0), (-(rw + 2), 0)] | offset w
slots = union [slot | rotate (360 * k / n) for k in range(n)]
arcs = union [circle (rl + c) at:(a * cos(180 + half + 360 * k / n), a * sin(180 + half + 360 * k / n)) for k in range(n)]
driven2d = (circle rw) - slots - arcs - (circle (ax2 + c))
driven = driven2d | extrude t
assert driven.solids == 1
# The rim is the circle rw: the wheel stays inside rw + 0.01 and reaches rw - 0.05.
def ring_out(r) = (cylinder (r + 5) (3 * t)) - (cylinder r (4 * t))
assert clear(driven, ring_out(rw + 0.01)) and clear(driven, ring_out(rw - 0.05)) == false
assert driven.bbox.z ~ t
# ---------------------------------------------------------------- drive (local: axle at origin, pin on +X, bottom at z 0)
d2d = (circle rl) - (circle (rw + c) at:(a, 0)) # locking disc / knob profile
crank = circle rp | extrude tp
disc = d2d | extrude (gz + t + knob_h) | edges >Z | chamfer 1 | translate 0 0 tp
pin = cylinder pin_r (gz + t) | floor | translate R 0 tp
$hole = cylinder (ax1 + c) (hole_d + 1) | floor | translate 0 0 -1
drive = crank + disc + pin - $hole
assert drive.solids == 1
assert drive.bbox.z ~ (tp + gz + t + knob_h)
assert drive.bbox.x ~ (2 * rp) tol:0.1
# ---------------------------------------------------------------- base plate (drive axle at origin, wheel axle at (a, 0))
bar = (rect a (2 * pw) at:(a / 2, 0)) + (circle pw) + (circle pw at:(a, 0)) | extrude pt
ring1 = cylinder 8 sp | floor | translate 0 0 pt
boss2 = cylinder 6.5 (sp + tp + gz) | floor | translate a 0 pt
pin1 = cylinder ax1 (sp + hole_d - 0.3) | floor | edges >Z | chamfer 0.8 | translate 0 0 pt
pin2 = cylinder ax2 (sp + tp + gz + t + 0.5) | floor | edges >Z | chamfer 0.8 | translate a 0 pt
base = bar + ring1 + boss2 + pin1 + pin2
assert base.solids == 1
assert base.bbox.x ~ (a + 2 * pw)
# ---------------------------------------------------------------- placement
def drive_at(phi) = drive | rotate 0 0 phi | translate 0 0 z_drive
def driven_at(phi) = driven | rotate 0 0 theta(phi) | translate a 0 z_driven
def driven_off(phi, d) = driven | rotate 0 0 (theta(phi) + d) | translate a 0 z_driven
def pin_at(phi) = cylinder pin_r t | floor | translate R 0 z_driven | rotate 0 0 phi
def ok(phi) = clear(drive_at(phi), driven_at(phi))
# ---------------------------------------------------------------- checks
# 1. Indexing stroke: at entry, exit and seven angles between, the drive
# (pin, locking disc, crank plate) shares no volume with the wheel.
assert ok(-phie)
assert ok(-0.75 * phie)
assert ok(-0.5 * phie)
assert ok(-0.25 * phie)
assert ok(0)
assert ok(0.25 * phie)
assert ok(0.5 * phie)
assert ok(0.75 * phie)
assert ok(phie)
# 2. The pin is really inside a slot, not merely outside the wheel: at mid-stroke
# the whole pin lies inside the wheel's rim circle, at entry and exit half of it.
rim = cylinder rw t | floor | translate a 0 z_driven
pin_vol = pi * pin_r ** 2 * t
assert (pin_at(0) * rim).volume ~ pin_vol tol:0.5%
assert (pin_at(0.5 * phie) * rim).volume ~ pin_vol tol:0.5%
assert (pin_at(-phie) * rim).volume ~ (pin_vol / 2) tol:5%
assert (pin_at(phie) * rim).volume ~ (pin_vol / 2) tol:5%
# 3. Dwell: with the pin out of the slots the locking disc sits in a locking arc
# with clearance c, and the wheel cannot turn: 2 deg either way collides.
assert ok(90) and ok(180) and ok(-90) and ok(135)
assert clear(drive_at(180), driven_off(180, 2)) == false
assert clear(drive_at(180), driven_off(180, -2)) == false
assert clear(drive_at(90), driven_off(90, 2)) == false
assert clear(drive_at(90), driven_off(90, -2)) == false
# 4. The kinematic function is continuous at entry/exit and antisymmetric.
assert theta(-phie) ~ half tol:0.01
assert theta(phie) ~ (-half) tol:0.01
assert theta(0) ~ 0 tol:0.01
assert theta(0.5 * phie) ~ (-theta(-0.5 * phie)) tol:0.01
# 5. Both moving parts clear the base plate (they rest on it: touching is clear).
assert clear(base, drive_at(angle))
assert clear(base, driven_at(angle))
assert clear(base, drive_at(180))
assert clear(base, driven_at(180))
print "pin radius R" R "wheel radius rw" rw "locking disc rl" rl "tip width" tip
print "pin in slot for |angle| <=" phie "wheel turns per cycle (deg)" (360 / n) "lock releases (deg) before entry" (phie - horn)
# ---------------------------------------------------------------- layouts
asm = base + drive_at(angle) + driven_at(angle)
drive_prt = drive | translate (rw + 4 + rp) 0 0
base_prt = base | translate (-a / 2) (-(rw + 4 + pw)) 0
assert clear(driven, drive_prt) and clear(driven, base_prt) and clear(drive_prt, base_prt)
$print = driven + drive_prt + base_prt
if part == "assembled" then asm
else if part == "print" then $print
else if part == "drive" then drive
else if part == "driven" then driven
else base