Designing our first prototype case in Python

Generating a parametric 3D-printable case for our first collar prototype with trimesh and shapely instead of CAD. Since replaced by the production case.

Update, October 2026: this 3D-printed case was our first prototype and has been retired. The production collar uses a sealed 50 × 41 × 15 mm case, and its weight is not confirmed yet. See Rebuilding fudini.co around the collar.

In April we needed a clip-on enclosure for the XIAO nRF52840 Sense and a 200 mAh 402025 LiPo. Like a FitBark, it sits on top of an existing collar strap without replacing it, and it had to print on our Flashforge Adventurer 5M. On 1 September we built a second, openable version for investor demos.

What we tried

We kept a reference OpenSCAD version (fudini_collar_case.scad). An earlier design held the case to the collar with two strap loops.

For the investor demos we generated an openable, branded case from the same component dimensions. The largest internal component is the 402025 cell at 25 × 20 mm; adding 3 mm on every side gives a 31 × 26 mm footprint, which matches what the website and the spec already quote.

What actually happened

The OpenSCAD cask on macOS is deprecated (Intel + Rosetta), and the CLI did not work locally. The two-loop strap design was thin and its mesh was non-manifold.

The first demo revision was recorded as having 0 mm³ interference when mated. Measuring it instead of trusting it found two blockers. The lid skirt wrapped the outside of the base while the base wall stayed full height, so the parts shared 169 mm³ of solid between z 9.1 and 10.9 mm, and the case could not close. The lid was also not a closed volume: its parts were concatenated where they met on the z = 11 plane, leaving 6 non-manifold edges.

The lid carries the real brand mark, the five-ellipse paw over "fudini". Gemini could not do this part: it generates images, not solids. The mark is built from the logo's own ellipse geometry with trimesh and shapely.

The fix

The working path is a parametric Python generator using trimesh and shapely, with manifold3d installed alongside.

/tmp/scadvenv/bin/python device/enclosure/generate_stl.py

In April we replaced the two loops with a single continuous strap tunnel on the bottom shell's underside. It spans the full 28.6 mm with a 3.5 mm ceiling and is boolean-unioned to the shell. An asymmetric shell split, with a bottom fraction of 0.78, confines the USB-C cutout to the bottom wall. The top shell is a thin lid, which preserves antenna clearance near the top face.

For the demo case, a 1.6 mm wall cannot host a lap joint, so the lid now drops a 1 mm spigot into the cavity with 0.25 mm clearance per side. The measured intersection volume is 0.000 mm³. The lid's parts and the engraving cutters are now unioned, and both shells survive an STL round-trip watertight.

We removed the domed top. It rose 0.8 mm over 15.5 mm, which at 0.2 mm layers makes terraces of about 1.9 mm, and it forced the deboss to lose depth toward the edges. A flat top lets the lid print face-down, so the engraving forms against the bed. Engraving stroke width is sized from the nozzle: two extrusion widths, 0.50 mm on a 0.25 mm nozzle.

The April print-canonical case has an outer body of 30.6 × 25.6 × 11.5 mm and weighs about 10-12 g in TPU. TPU 95A is the recommended material because it is flexible and dog-safe; PETG is faster and stiffer, good for first fits. The September demo case body is 15.0 mm tall (base 10.8 + lid 4.2), or 23.4 mm including the strap tunnel.

What's still open

The XIAO PCB antenna needs at least 5 mm of clearance from metal or infill, and we have not checked it yet. We have not done a 1.5 m drop test onto tile.

The April design is IPX4 only, with open holes for USB-C, the reset button and the LED. The path to IPX7 needs wireless charging, a silicone overmold, screw bosses and an O-ring groove. On the demo case, the strap slots cut through into the cavity rather than through a closed loop, so the compartment is not sealed and a strap has nothing to thread. That is fine for a show-and-tell print, but not for a worn prototype.

If you're building something similar

  • Python with trimesh and shapely is a working alternative to OpenSCAD for parametric 3D generation.
  • Measure the intersection volume of your mated meshes, and re-measure rather than trust an earlier check. Our first demo revision was recorded at 0 mm³ and actually shared 169 mm³.
  • Use an asymmetric shell split to keep port cutouts within a single printed part.
  • Print debossed logos face-down on a flat top to avoid terracing.

Have a thought?

We build in public to learn. If you've solved this on nRF52 before, let us know.

Reply by email

Denys Zarubin

Founder, Spain

Investors

Request the deck

Join the
early list.

Prototype phase. Not certified. Not for sale. No payment required.

You’re on the list. We’ll email you when the collar is ready. In the meantime, download the free Fudini app.