The Screen Enclosure

July 2, 2026

Each of the video sphere’s twelve screens lives in a snap-fit case that I mostly didn’t model. I described it. The case is built on YAPPgenerator_v3, a 6,164-line OpenSCAD file you include and then configure: you declare your PCB’s dimensions, its standoffs, and arrays of cutouts per face, and YAPP generates a base and lid with walls, ridges, and snap joins. My entire enclosure (case, lid, a glue-in stem port, and a printable BOOT-button plunger) is one file just shy of 400 lines that reads more like a config than like CAD.

This post is the YAPP reference I wanted when I started: a real enclosure with real constraints, and the gotchas in the order I hit them.

The setup

  1. git clone https://github.com/mrWheel/YAPP_Box
  2. Copy YAPPgenerator_v3.scad next to your enclosure file (or add the YAPP_Box directory to OPENSCADPATH).
  3. include <YAPPgenerator_v3.scad> at the top, set variables, F5 to preview, F6 to render, export.

Describing the board

The screen module is an LCDWIKI ES3C28P, an ESP32-S3 with a 2.8″ ILI9341 display on an 86 × 50 × 1.6 mm PCB. YAPP wants the board described, not drawn:

pcbLength            = 86.0;     // long axis (X)
pcbWidth             = 50.0;     // short axis (Y)
pcbThickness         = 1.6;
standoffHeight       = 13.5;
standoffDiameter     = 5.6;      // matches the 5.60 mm pad on the spec
standoffPinDiameter  = 2.7;      // M3 self-tap

pcbStands = [
    [ 4.0,  4.0, yappBoth, yappPin],
    [82.0,  4.0, yappBoth, yappPin],
    [ 4.0, 46.0, yappBoth, yappPin],
    [82.0, 46.0, yappBoth, yappPin],
];

Four M3 mount holes, inset 4 mm from each PCB edge. All of these numbers came from the module’s datasheet, except the ones that couldn’t be (like where the BOOT button actually sits). Those are flagged in the source with a MEASURE YOUR BOARD comment because they had to be corrected against the physical PCB. They were.

The Z budget

The one genuinely three-dimensional decision in the whole case is vertical. Each module carries a 42 × 25.5 × 11.2 mm Li-Po battery, and it lives under the PCB: the standoffs are 13.5 mm tall specifically so the battery slots beneath the board with ~2.3 mm of clearance for wrappers, kapton, and the lead bulge. Everything else follows from that one choice:

internal Z = standoffHeight (13.5) + pcbThickness (1.6)
           + lcdAboveBoard (4.30) + 1 mm clear above the LCD
           = 20.4 mm

The seam between base and lid lands exactly at the PCB top: baseWallHeight = 15.1, lidWallHeight = 5.3. The battery is held by a pocket of four low walls (5 mm tall, below the SMD clearance band, so the pocket can’t collide with components even if the footprint drifts), drawn in a hookBaseInside, YAPP’s escape hatch for geometry it has no declaration for.

The cutouts

Everything that penetrates a wall is a row in an array. The whole personality of the case fits in one table:

FaceWhatKey numbers
LidLCD window63.2 × 50 mm rectangle
FrontUSB-C11 × 6 mm, centered at Z = −3 (below the PCB plane)
BackPower toggleØ6.2 mm circle at Z = −7.8, for an M6 threaded bushing
BaseStem-port pocketØ25.4 mm circle, 1.2 mm deep, centered at PCB (40, 25)
BaseBOOT plunger holeØ4.5 mm, through the full base plate at PCB (83.5, 14)

Two of these deserve a sentence. The USB-C connector is bottom-mounted (its body hangs below the PCB), so its cutout spans Z = −6 to 0 and stays entirely in the base wall, never notching the lid. And the toggle is a sub-mini SPDT with an M6 bushing (an MTS-102 or NKK M2012; bump the hole to 6.5 mm for a 1/4-40 toggle like the C&K 7101), wired inline with the battery’s positive lead, with comfortable margin over the module’s 560 mA peak draw.

Macro shot of a chrome SPDT toggle switch mounted through the printed back wall of an enclosure, next to the module's PCB

The two turned parts

Beyond the YAPP shells, the file defines two small rotate_extrude parts.

The stem port is how a case hangs off the sphere. It’s a 25 mm-foot, 18 mm-tall pedestal (the same quarter-ellipse profile as the big pedestal, reused at case scale) with a socket that receives a 10 mm stem. The case base has a circular pocket 1.2 mm deep with 0.2 mm of radial slack, and the port’s foot drops into it. The pocket walls fully constrain the port in XY before any glue is applied, so the stem ends up concentric with the LCD by construction; the CA or epoxy under the foot only has to carry load, not alignment.

A batch of printed black case base shells on a workbench, each with its quarter-elliptical stem port glued into the registration pocket

The BOOT plunger exists because the ES3C28P’s BOOT button is an SMT tactile switch on the underside of the PCB, facing the case floor, and I need it after assembly (it’s also the sleep button). The plunger is a Ø4 mm rod with a Ø6.5 mm flange partway up: the lower stem pokes 1.5 mm out the bottom of the case as a button, the flange retains it from falling out, and the upper stem rests ~0.5 mm below the switch actuator. A 3 mm guide tube printed onto the inside floor extends the bearing length to 5 mm, holding the plunger’s tilt to about 2.9° instead of the ~7° the base plate alone would allow. Total press travel: about 0.8 mm. You drop it in before seating the PCB, and it can only come out the top.

Centering a screen that isn’t centered

The LCD sits asymmetrically on the PCB: the bezel is wider on the USB-C end. Left alone, the visible screen would sit off-center in the case, which would read as an error twelve times over on the finished sphere. The fix is a matched pair of asymmetries. The lid cutout is trimmed 6 mm on the USB-C side to hide the wide bezel, and the case padding on the opposite side is extended by the same 6 mm (paddingBack = 9.0 vs 3.0 everywhere else). Net effect: the visible screen, the case interior, and the stem-port pocket all share the same center at PCB X = 40. It took two commits of nudging to land. The case looks symmetric precisely because the numbers aren’t.

Gotchas I’d want in the manual

  1. YAPP-consumed variables must be literals. YAPP evaluates its known arrays and flags during the include setup pass, before your own assignments resolve. printBaseShell = (part == "case") silently doesn’t work; neither does referencing your own constants inside cutoutsBase. The pattern that survives: literals in YAPP’s arrays, plus a keep-in-sync comment pointing at the named parameters they mirror.
  2. hookBaseInside has its own origin. The hook’s coordinate frame starts at the inside back-left corner of the base: YAPP’s wrapping translate has already consumed wallThickness, and Z = 0 is the inside floor, not the outside bottom. PCB (0,0) is at local (paddingBack, paddingLeft), not (wallThickness + paddingBack, ...). This class of trap is real; the battery pocket in this project sat one basePlaneThickness (2 mm) too high until a commit fixed the hook’s Z origin.
  3. Negative Z in yappCoordPCB is your friend. Z = 0 is the PCB’s bottom face, so features in the base wall live at negative Z: the toggle at −7.8, the USB-C centered at −3. Once you learn “below the board is negative,” side-wall cutouts stop being trial and error.
  4. Snap joins need ridge to bite into. ridgeHeight must be ≥ 1.8 × wallThickness for the snaps to work (mine: 5.0 mm against 2.4 mm walls). And ridgeSlack is a print-tuned number, not a design number: it shipped at 0.2 and got loosened to 0.35 after the first printed pair.
  5. Keep an assembled preview. All four part flags (printBaseShell, printLidShell, printPort, printPlunger) default to true so F5 shows the whole assembly; you flip things off only at export time. Cheap, and it catches interference visually before the slicer ever sees an STL.

YAPP comes down to a declarative DSL with one escape hatch. Learn where each array’s origin sits, keep YAPP’s own inputs literal, put everything custom in the hooks, and an enclosure becomes a table of holes with a PCB in the middle.


Part 2 of the video-sphere series. Previous: The Icosahedron Hub · Next: The Printed Parts.