This is probably the strongest case of “sometimes projects choose you” that I’ve ever experienced.

Basically, it is a box where you can put things in it and somebody else can control whether it’s locked or unlocked, remotely over the internet. All code and 3D models required to make your own is located here: https://forge.snepi.space/nepi/lockbox


I don’t really engage in kink myself - I just have a bunch of friends who do. One of them brought this idea to my attention because they were lamenting the unavailability of a similar, more commercial product. I really felt strongly about the fact that something like this should be fully printable and assemble-able from commodity parts and controllers.

I had to complete 3 things in order to make the idea work:

  • A 3D-printable box
  • Firmware to power the microcontroller in the box which controls the lock.
  • A server component that administers and tracks state of the boxes.

The Box

Obviously, the most important component, as it’s physical.

The box consists of four main parts:

  • The shell (“holder”)
  • A faceplate/backplate
  • The drawer

I settled on the locking mechanism pretty early on at my friend’s suggestion - an RC servo. Simple enough to control with a single PWM output, functional, and inexpensive. It uses one of the attachments that comes with it as the locking cam.

I had thought about using some kind of solenoid, but everything I could find required far too much current for what you could push through a microcontroller development board.

Similarly early was the idea that the thing being locked should go inside a drawer. I didn’t want to pigeon-hole myself too much - there’s actually a lot of non-kinky uses for this kind of thing! For example, you could put a vape or a phone in a large enough box. To that end, the model is parametric. At least to the extent that I could get it to play nice within FreeCAD. The drawer size is the most important bit.

Putting this model together also taught me something very valuable about FreeCAD: Do as much work in the sketch as possible! Fillet and chamfer features are the death of documents, especially with the (currently unsolved as of 0.21.2) topological naming problem. Even with trying that, resizing doesn’t work perfectly - changing the parameters requires some manual fixup, reattachment, and re-selection of edges.

If you open the source file you’ll notice that the shell part is named “Holder3” - That’s because I started the same design 3 times from scratch in order to get it to a state where it was manageable. The majority of the design exists as one big extrusion, and there are a few extra sketches mostly attached to datum planes in order to add holes and mounts. I think the final design only has two or three chamfer/fillet features, and they’re all the last item(s) in the tree.

Firmware

For the firmware, I made the decision to use MicroPython very early on. This is my first MP project, though I’ve used CircuitPython (a fork of MP) in the past. This decision was entirely driven by the fact that I hate, hate, hate dealing with embedded toolchains. I would rather do anything else other than waste my time wrangling with ARM’s custom GCC.

To that end, I also wrote as minimal of firmware as possible. Instead of anything fancy like Server-Sent Events or Websockets, the microcontroller simply polls a server every 5 seconds to figure out if it should be open or not. This is, of course, not as responsive, but not in a way that really matters. 5 seconds (or 10 at most) is barely enough to be noticeable unless you’re clicking away on lock/unlock with the thing on your desk.

Arriving at this decision took a while - I was kicking around the idea of something peer-to-peer using STUN/TURN but ended up writing that idea off completely when I realized how complicated it would be. Something for the future.

In the end, this design is so dead simple that it lets me implement all of the firmware required to make the box functional in less than 50 lines of Python.

Loading the firmware is a sore spot, currently. It involves having the micropython CLI on your computer, filling out a config file, and then uploading the config file and the code. It should be improved, but it’s functional.

Server

Since the firmware is polling a server to figure out its state, the server needs some software to manage and track that state. The solution I came up with is a pretty basic web app written in Golang. It’s very minimal, powered by sqlite, and has no configuration at all currently. It’s meant to be very easy to run your own instance of - this kind of thing is very personal and people (rightly) might feel odd about a third party potentially having control over their stuff.

To facilitate polling and access control, each box is assigned a “friend code” which is randomly generated. The person who wants ultimate control over the box creates a friend code in the web user interface, and that friend code is added to the config file and uploaded to the box using the micropython CLI. Any user in possession of friend code can associate it with their account in the web UI, but the ultimate owner has the ability to disassociate it from all accounts.

The web UI uses htmx to facilitate a single-page-app-like experience. I really liked working with this framework - my brain basically still thinks in PHP templates so this was right up my alley. React has never gelled with me and frankly, seems like complete overkill for something of this scale.

After spending some time working with it, I think that htmx-like functionality should be part of the HTML spec. It suits a lot of use cases where people just need basic interactivity but are instead using the sledgehammer of React to create that experience. The pendulum of frontend is already swinging back towards “server-side rendering” (lol) anyways, and this would be a welcome step back from the current reality of “browsing to a webpage means arbitrary code execution by the person in control of the website”.

Where to next?

Dunno. I haven’t even given the first prototype to the friend who suggested the idea yet, so I’ve gathered no feedback. See if people are interested and go from there, I guess? The experience of programming your own is going to be pretty difficult for somebody not familiar with embedded development using MicroPython. That’s a clear avenue of improvement.

The biggest takeaway is that FreeCAD is still not ready for prime-time. Working with it is an exercise in frustration, mostly because there’s the core of a great CAD program underneath all of the sharp edges and questionable UI decisions. Solving the TNP will go a long way towards making it more enjoyable to use.