"""6 x 6 mm through-hole tactile switch, Omron's B3F-1000 series (top-operated, flat or projected plunger, with or without a ground terminal). The body is centred on the origin, its underside on the board's top face (the XY plane); terminals 1/2 at -X and 3/4 at +X, 6.5 apart along X and 4.5 along Y; the ground terminal, when there is one, at -Y. Dimensions from Omron's data sheet, in the README.""" from steve import Model kind = "1000" # Type (the B3F-1xx0 drawing) force = "0.98N" # Operating force (sets the plunger colour) # Each entry: (height to the plunger's top, plunger, terminal pitch along X, overall width over the terminal bends, ground terminal). # Omron "Tactile Switch B3F", Cat. No. A070-E1-08 (omronfs.omron.com/en_US/ecb/products/pdf/en-b3f.pdf), Dimensions, pages 4 and 5: # every 6 x 6 model: body 6 ±0.2 x 6 ±0.2, 3.4 to the body's top, plunger 3.5 dia., terminals 0.7 wide and 0.3 thick, 3.5 below # the board, 4.5 ±0.2 apart along Y. B3F-1000/1002/1005/1006: 4.3 ±0.2; B3F-1020/1022/1025/1026: 5 ±0.2 (note); B3F-1060/1062: 7 ±0.2; # B3F-1070/1072/1075: 9.5, the plunger's top 3 dia.; B3F-1050/1052/1055/1056: 7.3 ±0.2, a 2.4 x 2.4 ±0.1 stem 1.8 ±0.1 high above a # plunger 4.3 ±0.2 high; terminals 6.5 ±0.5 apart and 7.7 ±0.5 over the bends. With ground terminal (B3F-1100/1102/1105, # B3F-1120/1122/1125, B3F-1150/1152/1155): the same, plus terminal 5, its hole 4.1 ±0.1 from the centre line, its tab 1.5 wide. # B3F-1110: 5 ±0.2, terminals 7.5 ±0.5 apart and 9 ±0.5 over the bends, with ground terminal. TYPES = { "1000": (4.3, "flat", 6.5, 7.7, False), "1020": (5.0, "flat", 6.5, 7.7, False), "1060": (7.0, "flat", 6.5, 7.7, False), "1070": (9.5, "tall", 6.5, 7.7, False), "1050": (7.3, "projected", 6.5, 7.7, False), "1100": (4.3, "flat", 6.5, 7.7, True), "1120": (5.0, "flat", 6.5, 7.7, True), "1150": (7.3, "projected", 6.5, 7.7, True), "1110": (5.0, "flat", 7.5, 9.0, True), } BODY = 6.0 BODY_TOP = 3.4 PLUNGER_D = 3.5 TALL_TOP_D = 3.0 STEM = 2.4 STEM_H = 1.8 PROJECTED_BASE = 4.3 ROW = 4.5 GROUND = 4.1 GROUND_TAB = 1.5 LEAD_W = 0.7 LEAD_T = 0.3 TAIL = 3.5 # Plunger colours by operating force (List of Models, pages 1 and 2): (kind, force) -> (part number, colour name). COLORS = { ("1000", "0.98N"): ("B3F-1000", "ivory"), ("1000", "1.47N"): ("B3F-1002", "yellow"), ("1000", "2.55N"): ("B3F-1005", "orange"), ("1000", "4.9N"): ("B3F-1006", "red"), ("1020", "0.98N"): ("B3F-1020", "black"), ("1020", "1.47N"): ("B3F-1022", "gray"), ("1020", "2.55N"): ("B3F-1025", "pink"), ("1020", "4.9N"): ("B3F-1026", "blue"), ("1060", "0.98N"): ("B3F-1060", "black"), ("1060", "1.47N"): ("B3F-1062", "yellow"), ("1070", "0.98N"): ("B3F-1070", "black"), ("1070", "1.47N"): ("B3F-1072", "yellow"), ("1070", "2.55N"): ("B3F-1075", "orange"), ("1050", "0.98N"): ("B3F-1050", "ivory"), ("1050", "1.47N"): ("B3F-1052", "yellow"), ("1050", "2.55N"): ("B3F-1055", "orange"), ("1050", "4.9N"): ("B3F-1056", "red"), ("1100", "0.98N"): ("B3F-1100", "ivory"), ("1100", "1.47N"): ("B3F-1102", "yellow"), ("1100", "2.55N"): ("B3F-1105", "orange"), ("1120", "0.98N"): ("B3F-1120", "black"), ("1120", "1.47N"): ("B3F-1122", "gray"), ("1120", "2.55N"): ("B3F-1125", "pink"), ("1150", "0.98N"): ("B3F-1150", "ivory"), ("1150", "1.47N"): ("B3F-1152", "yellow"), ("1150", "2.55N"): ("B3F-1155", "orange"), ("1110", "0.98N"): ("B3F-1110", "black"), } SHADES = {"ivory": "#ece4cf", "yellow": "#e8c21c", "orange": "#e8761c", "red": "#c8202a", "black": "#222325", "gray": "#8c8f93", "pink": "#e79ab4", "blue": "#2f62b8"} # Proportions, for looks only (the drawings don't dimension them): the metal cover's thickness, the four corner rivets, the projected # plunger's neck under its stem, where the 9.5 mm plunger narrows to its 3 dia. top, and the terminals' bends. COVER_T = 0.3 RIVET_D = 1.4 RIVET_AT = 2.1 RIVET_H = 0.2 NECK_D = 1.4 TALL_STEP = 6.5 TAB_Z = 0.9 if kind not in TYPES: raise ValueError(f"no type {kind!r}: {', '.join(TYPES)}") if (kind, force) not in COLORS: raise ValueError(f"B3F-{kind} doesn't come with a {force} operating force") height, plunger, pitch, over, ground = TYPES[kind] color = SHADES[COLORS[(kind, force)][1]] h = BODY / 2 # model.box takes the box's centre; the XZ plane's normal is -Y, so a sketch on it extrudes towards -Y. model = Model() model.box("housing", BODY, BODY, BODY_TOP - COVER_T, at=(0, 0, (BODY_TOP - COVER_T) / 2), op="new", body="housing_body", name="Housing") for i, (sx, sy) in enumerate(((1, 1), (-1, 1), (-1, -1), (1, -1))): model.cylinder(f"rivet_{i + 1}", RIVET_D / 2, COVER_T + RIVET_H, at=(sx * RIVET_AT, sy * RIVET_AT, BODY_TOP - COVER_T), op="join", body="housing_body", name="Corner rivet") model.box("cover", BODY, BODY, COVER_T, at=(0, 0, BODY_TOP - COVER_T / 2), op="new", body="cover_body", name="Cover") model.cylinder("cover_hole", PLUNGER_D / 2 + 0.1, COVER_T + 0.2, at=(0, 0, BODY_TOP - COVER_T - 0.1), op="cut", body="cover_body", name="Plunger opening") # Plunger: from the housing's top up to its own. base = BODY_TOP - COVER_T # the plunger stands on the housing, through the cover's opening top = PROJECTED_BASE if plunger == "projected" else height model.cylinder("plunger", PLUNGER_D / 2, (TALL_STEP if plunger == "tall" else top) - base, at=(0, 0, base), op="new", body="plunger_body", name="Plunger") if plunger == "tall": model.cylinder("plunger_top", TALL_TOP_D / 2, height - TALL_STEP + 0.01, at=(0, 0, TALL_STEP - 0.01), op="join", body="plunger_body", name="Plunger top") if plunger == "projected": model.cylinder("neck", NECK_D / 2, height - STEM_H - top + 0.02, at=(0, 0, top - 0.01), op="join", body="plunger_body", name="Neck") model.box("stem", STEM, STEM, STEM_H, at=(0, 0, height - STEM_H / 2), op="join", body="plunger_body", name="Stem") # Terminals 1-4: a strip 0.7 wide and 0.3 thick, from the housing's side out at TAB_Z, down the outside, in to the pitch and down to the # tail; drawn as an outline in the XZ plane (the strip's centre line offset by half the thickness) and extruded along Y. x_out = over / 2 - LEAD_T / 2 x_pin = pitch / 2 path = [(h, TAB_Z), (x_out, TAB_Z), (x_out, -0.4), (x_pin, -1.2), (x_pin, -TAIL)] def outline(points, t): """A closed outline around a polyline of straight segments, t thick (mitred corners).""" import math def offset(sign): out = [] for i, (x, y) in enumerate(points): normals = [] segments = ([(points[i - 1], points[i])] if i else []) + ([(points[i], points[i + 1])] if i + 1 < len(points) else []) for a, b in segments: dx, dy = b[0] - a[0], b[1] - a[1] n = math.hypot(dx, dy) normals.append((-dy / n, dx / n)) if len(normals) == 1: nx, ny = normals[0] k = 1.0 else: nx, ny = normals[0][0] + normals[1][0], normals[0][1] + normals[1][1] n = math.hypot(nx, ny) nx, ny = nx / n, ny / n k = 1 / (nx * normals[0][0] + ny * normals[0][1]) out.append((x + sign * nx * k * t / 2, y + sign * ny * k * t / 2)) return out return offset(1) + offset(-1)[::-1] leads = [] for i, (sx, sy) in enumerate(((1, -1), (-1, -1), (1, 1), (-1, 1))): pts = [(sx * x, z) for x, z in path] model.plane(f"lead_{i + 1}_plane", on="XZ", offset=-(sy * ROW / 2 + LEAD_W / 2), name=f"Terminal {i + 1}") with model.sketch(f"lead_{i + 1}_sketch", on=f"lead_{i + 1}_plane", name=f"Terminal {i + 1}") as sk: sk.polyline(f"lead_{i + 1}_outline", outline(pts, LEAD_T), closed=True) model.extrude(f"lead_{i + 1}", f"lead_{i + 1}_sketch", distance=LEAD_W, op="new", body=f"lead_{i + 1}_body", name=f"Terminal {i + 1}") leads.append(f"lead_{i + 1}_body") if ground: # Terminal 5: a tab 1.5 wide out of the -Y face at TAB_Z, then down at 4.1 from the centre line to the tail. gpts = [(-h, TAB_Z), (-GROUND, TAB_Z), (-GROUND, -TAIL)] model.plane("ground_plane", on="YZ", offset=-LEAD_W / 2, name="Terminal 5") with model.sketch("ground_sketch", on="ground_plane", name="Terminal 5") as sk: sk.polyline("ground_outline", outline(gpts, LEAD_T), closed=True) model.extrude("ground", "ground_sketch", distance=LEAD_W, op="new", body="ground_body", name="Terminal 5") model.box("ground_tab", GROUND_TAB, GROUND - h, LEAD_T, at=(0, -(GROUND + h) / 2, TAB_Z), op="join", body="ground_body", name="Terminal 5 tab") leads.append("ground_body") # Materials, for looks and a rough mass (the data sheet names none): the housing a black polyamide (pa6), the cover sheet steel # (steel-c10), the plunger polyamide in its colour, the terminals copper alloy, silver plated (copper). model.material("pa6", bodies=["housing_body"], color="#26272a") model.material("steel-c10", bodies=["cover_body"], color="#c9ccd0") model.material("pa6", bodies=["plunger_body"], color=color) model.material("copper", bodies=leads, color="#d6d8db") result = model.result()