Grid Edge Gateway

A site controller in Rust that sits between a grid operator and a commercial prosumer site. It speaks IEC 60870-5-104 to the operator's control centre and SunSpec Modbus to the inverters, chargers, heat pump and battery. A model-predictive planner schedules the site against real day-ahead prices; a real-time safety layer applies each country's rules: §14a EnWG in Germany, the ElWG feed-in cap in Austria, the 3% curtailment budget and flexibility contracts in Switzerland. What runs below is that code, compiled to WebAssembly.

What is simulated here

1

A delivery depot in Germany

One commercial site connected to the grid: 120 kWp of solar panels on the roof, four 22 kW chargers for its electric delivery vans (they come back in the late afternoon, charge overnight and leave in the morning), a 14 kW heat pump that heats the building and a 100 kWh battery.

2

A rule that lets the grid operator slow it down

On winter evenings everyone draws power at once and the neighbourhood transformer can overload. The German §14a EnWG lets the grid operator reduce chargers, heat pumps and batteries for a while, as long as a guaranteed minimum is left.

3

The gateway in between

The Grid Edge Gateway is the software between the grid operator and the depot. It receives the operator's commands (IEC 104), drives the devices (Modbus), keeps to the rules and, if you choose, plans the day by the electricity price. In the live demo you are the grid operator: press "Dim" and watch the site obey.

4

What happens when the reduction ends?

During the reduction the vans fall behind. When it ends, every depot in the neighbourhood catches up at the same moment, and the peak can end up higher than with no reduction at all. This is the rebound. The feeder calculator simulates many depots together to find the least harmful way to bring them back.

DSO control centreYou are the grid operator. Every button sends a real IEC 104 command to the site; the frames appear in the log below.
Rules the site runs under
Day the real extremes of 2025: 583 €/MWh on 20 Jan at 17:00, −250 €/MWh on 11 May at 13:00, −4.2 °C on 22 Nov, 33.5 °C and 476 €/MWh on 1 Jul (SMARD prices, measured weather)
or any day:
Controller switch any time; a rules-only copy runs alongside
Reduce consumption C_SC_NA_1 · IOA 5001
Feed-in limit C_SE_NC_1 · IOA 5002 · % of 120 kWp
Emergency C_SC_NA_1 · IOA 5003 · overrides day limits, budgets, opt-outs
Simulated time a logistics depot, 06:00 to 06:00 next day
--:--
Jump to a scene

Power at the depot, kW

Grid connection (+ import, − export) PV output Chargers + heat pump Battery (+ charging) DSO limit in force

Day-ahead price, €/MWh 2025: highest 583 on 20 Jan at 17:00, lowest −250 on 11 May at 13:00

Battery state of charge, % actual plan

Indoor temperature, °C actual plan comfort floor

This site vs the same site on rules alone

Consumption limit: controllable devices' grid draw

––

Fault injection

The device stops answering Modbus. Watch the fallbacks: a charger drops to its own 6 A failsafe and the battery goes idle after 30 s; without the meter the budget shrinks to the floor.

Field devices (Modbus TCP): setpoint written by the gateway → measured

IEC 60870-5-104 frames on the DSO link

Flexible connection: the site side of the capacity map

Since 2025 a German grid operator may connect a plant beyond the grid's firm capacity if the plant accepts a limit (§ 17 (2b) EnWG). The capacity map found, for each HV/MV district of the Saarland, the day of 2025 on which flexible plants lose the most, and the share of their output allowed in each hour. Play that day here: the grid operator sends the limit over IEC 104 every five minutes, and the gateway keeps the depot's export under it, using the energy on site and in the battery before it curtails the PV.

District
Curtailment rule

See the districts on the capacity map

Share of a flexible plant's output the substation allows, hour by hour

Feeder calculator: the best way to end a reduction

When a §14a reduction ends at the same time for many sites, their chargers and heat pumps catch up at once. The research behind this project (20 sites × 10 days × 33 cases, docs/study/rebound) found that a slow restart spreads the rebound over more minutes while its peak stays about as high, and that a site planning by price avoids most of it. Test any combination yourself: the page simulates every site with the gateway's own code and compares it with the same evening without a reduction.

1. Describe the neighbourhoodHow many depots share the transformer, how much it carries per depot, when the vans arrive, which day and how long the reduction lasts.
2. Choose how the depots come backAll at once, slowly (ramp), after a random wait or in groups. Each depot can also plan by price. Press Calculate, or Compare all options to test them all.
3. Read the evening
  • blue: the neighbourhood's load with your option
  • dashed: the same evening with no reduction
  • red: the transformer's limit (blue above it is an overload)
  • purple band: the reduction
Sites on the feeder each is the demo depot with its own weather
20
Transformer capacity per site
90 kW
When the electric vans arrive all at the same time = worst case for the transformer
Day
or any day of 2025:
Reduction from 17:30
Grid operator releases groups 15 min apart
How each site comes back
Site controller the planner schedules against day-ahead prices; slower

Pick a feeder on the left and press Calculate. Each site is simulated from 06:00 with the gateway's code; the evening then plays back here, minute by minute.

2025, evening by evening

Twenty depots on one feeder, simulated on every evening of 2025 with that day's real electricity prices and measured weather. A grid operator only reduces consumption on days the transformer would otherwise overload. Each square is one day: pick a transformer size and a site controller, then click a day to see its evening.

Transformer per depot
Depots run on
no overload: nothing to do would overload: the reduction solves it the rebound overloads it again after 19:30 overloads even with the reduction

Loading the year…

Data sources: what is measured and what is modelled

DataWhat is usedSourceKind
Electricity pricesHourly day-ahead prices of the bidding zone DE-LU for every hour of 2025; where 15-minute products exist (DE-LU and AT from 1 October) the hourly meanSMARD.de (Bundesnetzagentur), CC BY 4.0, through the Energy-Charts API, stored in year2025_data.rsreal, Germany
Sun and temperature, any day of 2025Hourly air temperature and global irradiance measured in Stuttgart; PV output = irradiance × 0.85Open-Meteo historical weather API (ERA5 reanalysis, Copernicus/ECMWF), CC BY 4.0, stored in year2025_data.rsreal, Germany
Reduction rules (Germany)Minimum power Pmin,14a = 4.2 kW per device, simultaneity factor, 5-minute gradual release, proof of complianceBNetzA decision BK6-22-300, Anlage 1; §14a EnWGreal, Germany
Feed-in rules (Germany)60% cap for new systems without a smart meter; no payment in negative hoursSolarspitzengesetz (BGBl. 2025 I Nr. 51); EEG §51real, Germany
Rules in Austria and Switzerland70% feed-in cap; 3% free curtailment budget; flexibility contractsElWG (BGBl. I 91/2025); StromVG Art. 17c; StromVV Art. 19b–19dreal, AT and CH
Sun and temperature, the two study daysClear-sky solar curve and daily temperature for south-west Germany, with a random cloudiness per day (sunny, mixed, overcast), used by the Monte Carlo and rebound studiesModel in climate.rs, parameters set by handmodelled
VansArrival time, energy wanted and hours plugged in for each van; "varied times" shifts each depot by up to ±1 h and ±30% energyModel in sim.rs and feeder.rsmodelled
Building and other loadsFirst-order thermal model of the building; 10–40 kW of other consumptionModel in sim.rsmodelled
TariffDay-ahead price + 12 ct/kWh of grid fees and levies; demand charge of 100 €/kW a yearIllustrative values of the order of German commercial tariffs, in climate.rsassumed

The feeder calculator and the year study follow the German §14a rule, so they always use the German bidding zone and the weather of Stuttgart.

Four extreme days, and every day of 2025

The buttons are the extreme days of 2025 in Germany. 20 January was a Dunkelflaute, with little wind or sun and 583 €/MWh at 17:00, the highest price of the year. On days like this, German grid operators are most likely to reduce heat pumps and chargers. 11 May, a sunny Sunday, had the lowest price of the year, −250 €/MWh at 13:00, the situation feed-in limits exist for. 22 November was the coldest day and 1 July the start of a heatwave with an evening price spike.

Any other day of 2025 can be picked as well: it runs on that day's real prices and measured weather. The section 2025, evening by evening runs all 365 evenings for a feeder of 20 depots. This replays 2025 as it happened. Day-ahead prices are published one day in advance, so a whole year of them cannot be forecast, and the planner only needs the next 24 hours.

How it works

Towards the DSO

The gateway is an IEC 60870-5-104 controlled station, the protocol German DSOs require for telecontrol of plants from 100 kW. Commands are confirmed (or refused with a negative confirmation), measurements go out spontaneously with time tags, and the link runs over TLS with client certificates in the spirit of IEC 62351-3. The protocol stack is written from scratch and tested against lib60870.

Two layers of control

A planner (MPC) looks 24 hours ahead every 15 minutes and schedules battery, cars and heat pump against prices, the demand charge, departures, comfort and battery ageing. Underneath, a real-time safety layer runs every second and follows the plan only as far as the rules allow: the consumption floor, feed-in limits, the 6 A car minimum and departure deadlines never depend on the plan being right.

Towards the devices

Inverters and meter are found by walking the SunSpec model chain. Inverter limits are written without a revert timeout, so they stay in force if the gateway dies; chargers get a failsafe current of 6 A, which alone is below 4.2 kW. DSO commands are persisted, so a dimming survives a restart.

Mathematical models and why each one is there

Every number on this page comes from one of these models. Each card gives the equation, the values used and the reason for the choice, with a link to the rule or to the code.

1Minimum power during a §14a reduction

Pmin,14a = max(0.4·ΣPHP, 0.4·ΣPAC) + (n − 1)·GZF(n)·4.2 kW
depot: n = 6 → 0.4·14 + 5·0.6·4.2 = 18.2 kW

n is the number of controllable devices behind the energy manager (4 chargers, the battery and the 14 kW heat pump). GZF is the simultaneity factor of the ruling: 0.8 for 2 devices, falling to 0.45 from 9.

Why. The ruling fixes this floor. The gateway may never grant less, so it is computed from the rule itself and tested against the ruling's own examples.

BK6-22-300, Anlage 1, 4.5.2 · rules.rs

2Power budget while reduced

B = Pmin,14a + SPV − 0.3 kW
controllable devices: Σ PEV + PHP + Pbat,charge ≤ B

SPV is the smallest PV surplus measured over the last 30 s, recomputed every second. The 0.3 kW margin absorbs meter noise and the delay between a setpoint and the device reaching it.

Why. The ruling lets a site use its own PV on top of the floor. Taking the smallest recent value means a passing cloud cannot push the grid draw over the limit, and without the margin measurement noise alone would.

controller.rs

3Gradual release after a reduction

B(t) = B0 + (Prated − B0)·(t − t0) / Tramp, Tramp = 300 s
t0 = tend + U·D, U ~ uniform(0, 1)

B0 is the budget when the reduction ends, Prated the sum of the devices' rated power, D the longest random wait (0, 10 or 30 min in the calculator).

Why. A step back to full power makes every site on a feeder jump at the same second. The ramp is the German rule; the random wait is the idea of the British smart-charging regulation (SI 2021/1467), tested here to see whether it helps.

BK6-22-300, Anlage 1, 4.3 · controller.rs

4Charging current and power

PEV = 3 · 230 V · I
I ∈ {0} ∪ [6 A, 32 A] → P ∈ {0} ∪ [4.14 kW, 22.1 kW]

Three phases at 230 V. The car stops charging below 6 A, so the power is either zero or at least 4.14 kW.

Why. Chargers are controlled by current, as in IEC 61851. The 6 A step is why a floor of 4.2 kW per device makes sense and why the planner leaves minimum powers to the real-time layer.

rules.rs

5Battery

Ek+1 = Ek + η·Pc·Δt − Pd·Δt / η
η = 0.95, E ∈ [0, 100] kWh, |P| ≤ 50 kW

E is the stored energy, Pc and Pd charge and discharge power, η the one-way efficiency.

Why. The simplest model that loses energy in both directions. That is what makes charging and discharging at once never pay, so the planner needs no binary variables.

sim.rs

6Building and heat pump

C·dT/dt = COP(Tout)·PHP + Qgains − UA·(T − Tout)
COP = clamp(3 + 0.08·Tout, 1.8, 5), UA = 1.2 kW/K, C = 18 kWh/K

One thermal mass (first-order RC model). Time constant C/UA = 15 h. Internal gains 3 kW in working hours, 1 kW otherwise. Comfort floor 20 °C from 06:00 to 22:00, 17 °C at night, ceiling 23 °C.

Why. A 15-hour time constant lets the building store heat for a few hours. That is the flexibility the planner uses to heat before a price peak or a reduction.

sim.rs

7Sun and PV output

real day: PPV = 120 kWp · min(1, 0.85·GHI / 1000 W/m²)
study days: PPV = 120 kWp · pclear·sin(π·x)1.4·c, x = (h − hrise)/(hset − hrise)

GHI is the measured global horizontal irradiance of the hour; 0.85 is the performance ratio of a flat rooftop system. On the two study days, c is a random cloudiness per day and the sun's path is a clear-sky curve.

Why. Measured irradiance gives real days, real clouds included. The clear-sky model is kept for the forecaster, which, like a real energy manager, does not know the day's weather in advance.

climate.rs

8Prices and tariff

cimport = pDA/1000 + 0.12 €/kWh
cexport = max(0, pDA)/1000
demand charge = 100 €/kW·a · P̂ · h/8760

pDA is the day-ahead price in €/MWh. P̂ is the highest quarter-hour of import in the billing period; the plan carries h/8760 of the yearly charge.

Why. A dynamic tariff indexed to the day-ahead market, as German suppliers must offer. Exported PV earns nothing in negative hours (EEG §51). The demand charge is what makes a smart plan avoid new peaks.

EEG §51 · climate.rs

9Planner (MPC)

min Σk=1..96 [cimport,k·g⁺k − cexport,k·g⁻k + ageing + discomfort + unmet] + demand(P̂) + 10⁻⁴·Σ(ΔPbat)² − V·E96
s.t. g⁺ − g⁻ + PV − ΣEV − HP − c + d = base, battery and building dynamics, g⁺k ≤ P̂

96 steps of 15 minutes, about 1,550 variables with four cars. Ageing 0.03 €/kWh plus a penalty outside 20–80% charge; discomfort 20 €/K·h; energy a car leaves without 1 €/kWh. V values the energy left in the battery at the end.

Why. A convex quadratic program has one optimum, a certificate when there is none, and a predictable solve time of about 16 ms. A mixed-integer model would add exactness only where the real-time layer already handles it.

Clarabel · mpc.md

10Reserve against forecast errors

PVlow = PVexpected − z·σ, z = 1.645 (ε = 5%)
Emin,k = Emin + z·Σj σj (errors summed linearly)

σ is the standard deviation of the PV forecast. The reserve is kept only during and just before an expected reduction, the one time a shortfall cannot be bought from the grid.

Why. Cloudy hours come together, so errors are added linearly. Summing them over the whole day asked for more reserve than the battery holds.

mpc.md

11Feeder and rebound

L(t) = Σi=1..N Li(t)
rebound R = maxt ≥ trel [Lred(t) − Lbase(t)]
pushed energy = Σ max(0, Lred − Lbase)·Δt, minutes over = #{t : L(t) > Ctrafo}

Li is the grid exchange of depot i in one-minute means, Lred the evening with the reduction and Lbase the same evening without it, with the same weather and fleet.

Why. Comparing each evening with itself without a reduction isolates the effect of the reduction. The difference is the rebound, measured, with no need to assume it.

feeder.rs · rebound.rs

12Statistics of the studies

x̄ ± t0.975, n−1·s/√n
paired: dj = xjA − xjB on the same weather and fleet

n runs, sample standard deviation s, Student's t quantile for a 95% interval. Strategies are compared day by day on the same random weather.

Why. Pairing removes the day-to-day variation of the weather from the comparison, so a difference of a few euros is visible with 30 days.

study.rs

13Classifying an evening of 2025

k0: Lbase ≤ C all evening
k2: minutes over after 19:30 with the reduction > without it
k3: still over with the reduction, k1: otherwise

C is the transformer capacity of the feeder. The grid operator is assumed to reduce only on evenings that would otherwise go over it.

Why. It answers the practical question: on how many evenings is a reduction needed, and on how many does it cause a second overload.

year.rs

14Proof of compliance

hk = SHA-256(hk−1 ‖ recordk), h0 = fixed genesis digest

Each reduction event produces a record (start, end, budget, measured draw). Each record's hash includes the previous one.

Why. The ruling asks the operator to be able to prove what happened. A chain of hashes makes any later change to one record visible.

BK6-22-300, Anlage 1, 7.2–7.3 · compliance.rs

15Feed-in limit

Pexport ≤ f·120 kWp
PVmax = Pexport,allowed + Pconsumption − 0.3 kW

f is the limit in force after the country's rules: the grid operator's setpoint (IOA 5002), the Austrian 70% cap, or zero once the Swiss free budget is used up outside an emergency.

Why. Only the export is limited. The depot, the chargers and the battery still use all the PV they can, so only the surplus above the limit is lost. Inverter limits are written without a timeout, so they stay in force if the gateway stops.

controller.rs

What the MPC planner adds, measured

Each plan is a convex quadratic program over 96 quarter-hours: battery dynamics with losses and an ageing cost per kWh cycled, a first-order thermal model of the building, each car's energy request and departure time (as ISO 15118 passes them to the charger), the DSO's announced dimming window as a constraint, day-ahead prices, and a demand charge on the highest quarter-hour of import. It is solved by Clarabel, an interior-point solver written in Rust. The same solver runs in the gateway, in the study and in this page. Forecast errors are handled where they matter: the battery keeps a reserve only in and just before an expected dimming, the one time a shortfall cannot be bought from the grid.

A Monte Carlo study measured it. The depot ran through 30 random-weather days each in spring and winter, on the real prices of those days, with the DSO dimming from 17:30 to 19:30. The same code ran 720 times.

Per daySpring · 6 Apr 2025Winter · 20 Jan 2025
Rules alone: energy, battery ageing and peak charge126.6 €357.9 €
MPC−25.6 ± 2.2 € (−20%)−59.7 ± 2.4 € (−17%)
Highest quarter-hour of import89 → 34 kW110 → 57 kW
MPC that ignores the demand charge−15.6 €, peak up to 93 kW−44.4 €, peak up to 118 kW
Chance-constrained or robust MPCsame as MPCsame as MPC
Cars that left without their energy00

Most of the value comes from prices, the peak and the building's thermal mass. Forecast errors matter much less, because the real-time layer absorbs them within seconds and §14a guarantees a floor. With the dimming moved to the afternoon, the chance-constrained reserve cost 5 € a day in spring and bought only a few seconds less above the floor. A plan that ignores the demand charge piles charging into the cheapest hours and raises the peak, and for a German commercial site that pays a Leistungspreis it can lose money.

The formulation, the design choices and the full study →

Country rules for one controller

ConsumptionFeed-inSource
DE§14a EnWG: the DSO may dim heat pumps, chargers and batteries, but must leave Pmin,14a (4.2 kW per device, simultaneity factor for several behind an EMS)DSO setpoints for plants over 100 kW; new systems without smart meter capped at 60% (Solarspitzengesetz 2025)BNetzA BK6-22-300, Anlage 1
ATNo statutory minimum like §14a was found in the sources checked; modelled as a flexibility contract (minimum power, minutes per day)Spitzenkappung: the DSO may cap feed-in of new or extended PV at up to 70% of module peak power; a dynamic version is planned for 2028ElWG, BGBl. I 91/2025
CHFlexibility is used by contract, with pay; the owner may forbid uses that existed before 2026Guaranteed, unpaid curtailment of at most 3% of the yearly energy at the connection point; unlimited in an immediate, serious threatStromVG Art. 17c; StromVV Art. 19b–19d (in force 1 Jan 2026)

IEC 104 point list, common address 1

IOATypeDirectionMeaning
5001C_SC_NA_1DSO → siteReduce consumption ON / OFF (§14a in DE, contract in AT/CH)
5002C_SE_NC_1DSO → siteFeed-in limit, % of installed PV (0–100; other values are refused)
5003C_SC_NA_1DSO → siteEmergency ON / OFF: overrides day limits, budgets and opt-outs
1001M_ME_TF_1site → DSOActive power at the grid connection, kW (+ import)
1002M_ME_TF_1site → DSOPV active power, kW
1003M_ME_TF_1site → DSOControllable devices (steuVE), kW
1004M_ME_TF_1site → DSOsteuVE power drawn from the grid, kW (the quantity §14a limits)
1005M_ME_TF_1site → DSOConsumption floor while dimmed, kW (Pmin,14a in DE)
1006M_ME_TF_1site → DSOFeed-in limit in force, % (feedback of 5002)
1007M_ME_TF_1site → DSOPV limit sent to the inverters, %
1008M_ME_TF_1site → DSOFeed-in limit in force after country rules, %
1009M_ME_TF_1site → DSOBattery power, kW (+ charging)
1010M_ME_TF_1site → DSOBattery state of charge, %
1011M_ME_TF_1site → DSOConsumption dimmed today, minutes
1012M_ME_TF_1site → DSOPV energy curtailed this year, kWh
1013M_ME_TF_1site → DSOFree curtailment budget used, % (CH)
2001M_SP_TB_1site → DSOConsumption dimming active (feedback of 5001)
2002M_SP_TB_1site → DSOGradual release in progress
2003M_SP_TB_1site → DSOMeter fallback (grid measurement lost)
2004M_SP_TB_1site → DSOField device fault
2005M_SP_TB_1site → DSOEmergency active (feedback of 5003)
2006M_SP_TB_1site → DSOContract day limit reached, dimming refused
2007M_SP_TB_1site → DSOCurtailment budget used up