42
forty-two watts
Don't Panic
v0.4.0-alpha · running in production · mostly harmless

The answer is forty-two watts.

42W is a single Go binary that runs on a Raspberry Pi and coordinates your solar, batteries, grid, and EV charger in real time — so your house runs on its own power. Local-first. No cloud required. Deep Thought took 7½ million years to compute the Answer. Our MPC solves 200,000 states in ~0.6 ms.

Born from frustration.

We spent too many evenings trying to get a Ferroamp talk to a Sungrow, a Sungrow talk to an Easee, and none of them talking to a sane optimiser. Every vendor has a cloud. Every cloud has a latency floor. The grid balances every second — a 2-second round trip to someone's data centre in Frankfurt is not a control loop. It's a rumor.

So we wrote 42W. A single binary you can scp to a Raspberry Pi. Drivers in plain Lua so anyone who can read a register map can add an inverter in an afternoon. Physics at the edge, priceshaving at the planner, and the cloud is a nice-to-have — not a dependency.

We know this stuff at Sourceful. Much of the design came from hard-won experience coordinating distributed energy for real customers. 42W is what happens when that experience meets a homeowner who wants it on their terms.

Three loops. One binary.

42W is three control layers compiled into one static executable. Each has its own job and its own clock. Together they keep the lights on even when the forecasts lie.

Control loop 5 s

PI controller with anti-windup (3 kW integral limit), Kalman-smoothed meter signal, seven independent safety clamps — fuse guard, slew rate, SoC floor/ceiling, saturation envelope, dispatch holdoff. The part that keeps the lights on.

MPC planner 15 min · 48 h

Dynamic programming over 51 SoC levels × 21 actions × 193 slots. Solves in ~0.6 ms. Three strategies: self-consumption, cheap charging, full arbitrage. Reactive replan on forecast divergence > 500 Wh.

Digital twins 60 s

Three online models: a PV twin (RLS, learns your roof), a load twin (168 hour-of-week buckets + heating coefficient), and a price twin (fills the day-ahead gap). No training phase — they learn while they work.

Lua drivers 20 shipped

Sungrow, Ferroamp, Huawei, Deye, Solis, SMA, Fronius, Kostal, GoodWe, Growatt, Sofar, SolarEdge, Victron, Pixii, Easee, Eastron SDM630. One .lua file per device. Hot-reloadable. No compile step.

Watchdog

Any driver silent > 60 s flips offline and receives DefaultMode. Each driver implements an autonomous fallback — Sungrow reverts to self-consumption, Ferroamp publishes auto. Safe 02:00 crashes.

EV-aware dispatch

When the car is charging, home batteries stop discharging into it. No round-tripping through two battery systems. Easee cloud driver + OCPP 1.6J central system on port 8887.

Home Assistant

MQTT autodiscovery. Point it at your Mosquitto, sensors and controls appear in HA automatically. Also optional federation to Sourceful Nova Core (ES256 identity, JWT MQTT auth).

Long-term history

SQLite hot store keeps 14 days at 5 s resolution. Daily rolloff to zstd-compressed Parquet, dictionary-encoded per metric. A full year fits in a few GB. Query your house.

Self-tuning batteries

The selftune package injects known step commands, observes the response, and fits ARX(1) parameters. Bootstraps the cascade controller from first principles. No calibration wizard required.

Physics before code.

Every rule exists because ignoring it cost us something. These are the ones we write down.

01Local over cloud

Cloud APIs answer in 2–5 seconds. Grid frequency balances every one. The control path lives on the device; the cloud is opt-in.

02One binary, no CGo

Pure Go, statically linked for linux/arm64. Embeds Lua 5.1 (gopher-lua) so drivers need no toolchain. scp and run.

03The site sign convention is law

Positive W = energy into site. Sign flips happen only at the driver boundary. Any module above drivers violating this is always a bug.

04Robust before feature-rich

Works 100% of the time before it works 110% of the time. Seven clamps, each guarding a named failure mode. Watchdogs. Safe-state on staleness.

05Hardware-stable identity

Devices keyed by make:serial, else mac:<arp>, else endpoint. Renames don't orphan trained model state. Your battery remembers itself.

06Confidence-weighted optimism

The MPC blends forecasts toward horizon mean: eff = conf·real + (1−conf)·mean. Won't lock in expensive trades on a single ML prediction.

07Simple over clever

If you're proud of how clever it is, simplify it. The DP solves in 0.6 ms because it's a DP, not a MILP with a branch-and-cut rodeo.

08Share and enjoy

MIT licensed. Drivers are Lua. Issues are on GitHub. Design is open. Network effects, not lock-in.

Three layers. Goroutines all the way down.

      config.yaml
          │
          ▼
┌─────────────────────────────────────────────────┐
│  Lua drivers — one goroutine per device       │
│  ferroamp.lua · sungrow.lua · easee.lua · …     │
└───────────────────────┬─────────────────────────┘
                        │ host.emit("meter"|"pv"|"battery"|"ev")
                        ▼
┌─────────────────────────────────────────────────┐
│  Telemetry store — Kalman smoothed             │
│  (process 100 W · measurement 50 W)             │
└───────────────────────┬─────────────────────────┘
                        │
                        ▼
┌─────────────────────────────────────────────────┐
│  Control loop  (5 s)    PI + clamps + dispatch │
│    ↑   MPC planner   (15 min)  DP · 48 h horizon│
│    ↑   Digital twins (60 s)    PV · load · price│
└───────────────────────┬─────────────────────────┘
                        │ driver_command(action, power_w)
                        ▼
┌─────────────────────────────────────────────────┐
│  Lua drivers → Modbus / MQTT / HTTP / OCPP     │
└─────────────────────────────────────────────────┘

Numbers, because physics.

control_loop.period5 seconds
mpc.cadence15 minutes
mpc.horizon48 hours
mpc.state_space51 · 21 · 193 ≈ 207k
mpc.solve_time~0.6 ms
drivers.shipped20 lua files
pi.integral_limit3000 W
dispatch.slew_rate500 W / interval
soc.default_floor10%
soc.default_ceiling95%
watchdog.timeout60 seconds
twin.pvRLS · 7 features · λ=0.995
twin.load168 hour-of-week buckets · α=0.1
history.hot14 days @ 5 s (SQLite)
history.coldParquet · zstd · dictionary encoded
codebase.go~30,700 lines
codebase.tests~22,500 lines · 56 files
licenseMIT

Built by two people, and some towels.

42W is not a product of a company. It's a codebase maintained by hobbyists who happen to do this professionally.

Fredrik Ahlgren @frahlg

Birthed the project. Writes most of the Go. CEO at Sourceful Energy by day. Cranky about cloud latency by night.

Erik Arenhill @erikarenhill

Co-developer. Joined days after creation and has been shipping with Fredrik ever since. Makes the web front-end not look like 1998.

Three commands. One binary.

$ git clone https://github.com/frahlg/forty-two-watts
$ cd forty-two-watts
$ make dev       # starts sim-ferroamp + sim-sungrow + the app
$ open http://localhost:8080

When you're ready for hardware: make build-arm64, scp to your Pi, edit the config, systemctl start. That's the whole deployment story.