eebus

Home / Docs / E-mobility

E-mobility

The seven e-mobility use cases: who is on the end of the cable, what it may take, and what it actually did — EVSECC, EVCC, OPEV, OSCEV, EVCEM, EVSOC and EVCS.

The same machinery runs the e-mobility family: a wallbox, the car plugged into it, and the energy manager that has to keep the house fuse intact.

A car is the one appliance in a building that arrives, says what it is, and leaves again. Half of this family is therefore about finding out what is on the end of the cable before anything can be asked of it, and the use cases layer:

Use caseWhat it answers
Who is thereEVSECCwhat the wallbox is
EVCCwhat the car is, and what it can be asked
What it may takeOPEVthe ceiling the fuse imposes — an obligation
OSCEVthe surplus the roof is producing — a recommendation
What it didEVCEMthe current, power and energy that really flowed
EVSOChow full the battery is, and how big
EVCSwhat the finished session cost, and where its energy came from

EVSECC — commissioning the wallbox

EVSE Commissioning and Configuration is how a wallbox introduces itself to an energy manager — manufacturer, model, firmware — and how it reports being broken. Everything else in the family assumes this has happened.

EVCC — commissioning the car

EV Commissioning and Configuration is the same question about the thing on the end of the cable, in eight scenarios. Two of them have no message at all: scenario 1 is an EV entity appearing underneath the EVSE entity, and scenario 8 is it going away again. Detailed discovery carries both.

Scenario 2 is the one that decides what the rest of the family can do:

use eebus::usecases::emobility::evcc::{CommunicationStandard, EvProfile};

let car = EvProfile::new()
    .communication_standard(CommunicationStandard::Iso15118Ed2)
    .asymmetric_charging(true)
    .charging_power(1_400.0, 11_000.0);

car.supports_data_exchange();   // true — so EVSOC can be asked

A car on IEC 61851 has a pilot wire and nothing else. It can be offered a current and it cannot be asked anything: no state of charge, no charging plan, however willing the energy manager is. A car on ISO 15118 has a data link. Read that one field before the first request, or queue reads nothing will ever answer.

Two more fields decide real behaviour. asymmetric_charging is what OPEV needs before it writes three different currents. And the charging power band's minimum is the number that matters more than the maximum: below it a car does not charge slowly, it stops — so an energy manager that offers less than the minimum has switched the charge off rather than slowed it down.

OPEV — keeping the fuse intact

Overload Protection by EV Charging Current Curtailment is LPC's counterpart on the other side of the house, and the differences between them are exactly why it is a separate use case rather than a parameter:

LPCOPEV
What is limitedtotal active power, in wattscharging current, per phase, in amperes
Heartbeat timeout120 s4 s
On losing the peerthe failsafe power limit, for hoursa safe current, immediately

Four seconds, because a car's current is set by a pilot signal it follows at once, and the fuse it protects does not wait — where a heat pump's compressor cannot be asked for a change in less than minutes.

Per phase, because a car that can charge asymmetrically takes 16 A on the phase with room and 6 A on the one already loaded. The specification's own example is 690 W against 460 W.

use eebus::model::ElectricalConnectionPhaseName as Phase;

guard.require(&car, ChargingCurrents::new(16.0, 10.0, 6.0));   // asymmetric [OPEV-002]

// …and on the car:
ev.charging().effective(Phase::A);   // Some(16.0) while the manager is heard from
ev.charging().source();              // Curtailed | GuardSilent | GuardFailed

A manager that goes quiet and a manager that announces a failure mean the same thing to the car: fall back to a current that cannot overload anything, now.

Which limitId is which phase is the car's to say

Table 6 spells the three identifiers <x1>, <x2>, <x3> — placeholders. Nothing on the wire ties a limitId to a phase directly: the limit description points at a measurementId, and the parameter descriptions give that measurementId its phase. A manager has to read both and compose them, which is what PhaseLimits does and what OverloadGuardActor::attach waits for before it writes anything.

Getting it wrong is quiet. Curtailing "phase A" by writing to limitId 1 on a car that numbers them the other way limits a phase the supply was not worried about, leaves the one that was at full current, and is acknowledged — which is the fuse this use case exists to protect. A car that serves OPEV and OSCEV makes it worse: the same feature then carries two limits per phase, an obligation and a recommendation, so PhaseLimits matches on the category and scope too. Writing the fuse's ceiling into the recommendation tells the car it may ignore it.

Saying "no curtailment needed"

An empty limit list says nothing — it cannot be distinguished from a message that lost its contents. When no curtailment is required the guard sends limits that name every phase explicitly with isLimitActive: false, so the car knows the difference between "you are unconstrained" and "I have not spoken yet".

OSCEV — the same message, the opposite meaning

Optimization of Self-Consumption During EV Charging is OPEV's mirror image. The energy manager tells the car how much self-produced current is going spare, and the car may take it. The wire is nearly identical: the same per-phase ceilings in amperes on the same LoadControl feature, the same four-second heartbeat, the same fallback.

Two elements differ, and they are the whole use case:

OPEVOSCEV
limitCategoryobligation — a fuserecommendation — an offer
scopeTypeoverloadProtectionselfConsumption

A car that reads a recommendation as an obligation will throttle itself to the solar surplus and stop charging when a cloud passes — having been offered free power, not limited to it. The two usually run over the same connection at the same time, which is the ordinary installation: the fuse says what the car may never exceed, the roof says what is cheap right now, and the obligation wins.

The two are one implementation, usecases::emobility::charging, pointed by a Purpose — the same shape as Direction for LPC and LPP.

EVCEM — what actually flowed

EV Charging Electricity Measurement is what makes a curtailment checkable rather than asserted. A manager that limits a car to 6 A on phase B and never reads back what it drew is working from a number it has only ever written down.

use eebus::usecases::emobility::evcem;
use eebus::usecases::monitoring::{Measurand, Quantity};

let mut car = evcem::monitored_unit(1)
    .with(Measurand::on(Quantity::Current, Phase::B))
    .with(Measurand::unphased(Quantity::EnergyCharged));

EnergyCharged has scope charge: this session, into the battery. A wallbox's lifetime import is a different number that happens to share a unit.

EVSOC — how full, and how big

EV State of Charge is four scenarios, one mandatory and three optional, and the first two are worth having together and nearly useless apart. A percentage says nothing about how long charging will take; a capacity says nothing about how much is needed.

use eebus::usecases::emobility::evsoc::Battery;

battery.energy_to_full();     // Some(46_200.0) Wh — the number a planner uses
battery.usable_capacity();    // nominal × state of health

energy_to_full answers None rather than guessing when only one of the two has arrived: a manager that assumed a capacity would plan a charge for a battery it invented.

Scenario 2 is not a measurement. The nominal capacity is a characteristic on the ElectricalConnection feature — energyCapacityNominalMax — because the size of a battery is a property of the car, not something read off it moment by moment. And scenario 4's travel range is in metres, which is what the specification fixes and not what a dashboard shows.

EVCEM and EVSOC run on the shared measurement layer; what they add is vocabulary, not mechanism.

EVCS — what the session cost, and where the energy came from

EV Charging Summary goes the other way from everything above. The EVSE serves a writeable Bill and the energy manager writes into it: only the manager knows what the roof was producing at the time and what the tariff was, and the wallbox is where a driver looks. It is the one function in this crate a client actor writes for somebody else's screen.

use eebus::usecases::emobility::evcs::{ChargingSummary, Share};
use eebus::model::Currency;

// 18 kWh at €4.20 — a quarter of the energy off the grid, and nearly all of the bill.
let summary = ChargingSummary::new(18_000.0, 4.20, Currency::EUR)
    .from_grid(Share::new(25.0, 90.0))
    .self_produced(Share::new(75.0, 10.0));

summary.grid_energy_wh();       // Some(4_500.0)
summary.self_produced_cost();   // Some(0.42)

The energy split and the cost split are different numbers, and keeping them apart is the point of the use case. Reading it back is what makes a session's accounting the car's, rather than something inferred from the wallbox's power curve.

Two things matter before matching on an actor name:

  • The client has two conformant names. §3.2.2.1: CEM where the Energy Guard and the Energy Broker are the same CEM entity, EnergyBroker where they are different ones.
  • evcs::locate matches on the role, not the name. §3.2.2 gives the Energy Broker "only client functionality", so the peer serving a Bill is the EVSE whatever it calls itself — which is how the Porsche Mobile Charger Connect is found, announcing this use case under actor EV.

The wallbox asks for a summary by setting updateRequired [EVCS-009], and clears it once the write arrives. Tables 6 and 8 both warn the result "SHOULD NOT be used for actual billing as it may also contain approximated values" — it is what a driver is shown, not what anybody is invoiced.

Not here: CEVC

Coordinated EV Charging is a different shape from everything above: three actors, power sequences, incentive tables, and a charging plan negotiated between the car and the energy broker rather than a ceiling imposed on it. It is not implemented.