Outlet
Outlet is the thing that pulls the plug.
It is a deadman switch for mains power. The relay closes only while something is
actively telling it to. The host says SOMNUS-ON every 100 milliseconds. Go
quiet for a second and it opens on its own. Crash the host, pull the cable, kill
the process, and whatever is plugged into it stops.
Outlet is the contract, not the box. One firmware exists today and more are coming. What follows the hardware around is the protocol and the rules below.
Source on git.somnus.engineering
Off is the resting state
Most smart plugs have this backwards. They default to on and stay there. If the thing driving them falls over, the load keeps running and nobody is watching it.
Outlet inverts that. Power flows while the host keeps talking, and stops when it does not. There is no timeout to configure and no failure to detect. Silence is the signal.
It will forgive ten missed keepalives. At a full second of nothing, it drops. Closing has a 500ms floor so the relay cannot chatter itself to death, but opening is never delayed by anything. Off always wins the race.
The rules any target has to hold
Whatever the hardware, the same things stay true.
A physical override that outranks the host. A button on the box latches the relay open on the spot, ahead of the host, ahead of whatever the program thinks it wants. Clearing it takes a deliberate two second hold, and the press that engaged it cannot be the press that clears it. That is a hardware path, not a request the firmware gets to consider.
No radios. Wi-Fi and Bluetooth are compiled out and the build checks they stayed out. No network stack, no pairing, no app, no cloud, nothing sitting there listening.
One wire in. 115200 8N1 over USB-C, newline-delimited ASCII, case-sensitive, and any line over 64 bytes gets thrown away rather than parsed. The client binds to the USB serial number, so it drives that physical socket and not whatever else happened to enumerate that morning.
First target: M5Stack ATOM Socket
An ATOM Socket kit, off the shelf. ESP32-PICO-D4, dual core, 4MB flash. The relay sits on GPIO23, rated 10A at 100 to 120VAC. An HLW8032 meters the load and reports what it is actually pulling. An SK6812 gives you the state at a glance without plugging anything in to ask.
Sized for 30 to 50W. Sleep gear, not shop tools.
It is the first one built, not the only shape this takes. Different sockets, different current ratings, different mains standards: the protocol does not change, so the client does not either.
The code
Firmware is C++20 on ESP-IDF with the hardware watchdog wired in. The client is Go, with a CLI if you want to drive it by hand.
31 native unit tests across the state machine, the protocol parser, and the HLW8032 decoder, including the millisecond counter rolling over. Six hardware-in-the-loop tests against real timing rather than a mock of it.
Welded contact detection is deliberately not in there. Under half an amp the signal is not clean enough to call it, and a test that cannot honestly fail is worse than no test at all.