OCPP Charge Point¶
For EVSE devices the DER Simulator connects to a Charging Station Management System (CSMS) over OCPP as a charge point. All three mainline OCPP versions are supported:
| OCPP version |
|---|
| 1.6j |
| 2.0.1j |
| 2.1 |
Pick the version with the --ocpp_version flag; the rest of the wire
behavior follows automatically.
Starting an EVSE in OCPP mode¶
./sim --evse_profile EVSE-DC-150kW-CCS-1port.json \
--ocpp_enable yes \
--ocpp_version ocpp2.1 \
--ocpp_csms_url wss://csms.example.com/ocpp \
--ocpp_csid DERSIM-LAB-1
| Flag | Default | Description |
|---|---|---|
--ocpp_enable |
no |
Start the OCPP charge-point client alongside the rest of the simulator |
--ocpp_version |
ocpp1.6j |
One of ocpp1.6j, ocpp2.0.1j, ocpp2.1 |
--ocpp_csms_url |
(none) | WebSocket URL of the CSMS |
--ocpp_csid |
DERSIM-CP-1 |
Charge-point identifier the CSMS registers |
--evse_profile |
(built-in 22-kW AC) | Path to a JSON EVSE profile |
The --evse_profile flag is the canonical way to load an EVSE topology.
The SunSpec -f flag rejects EVSE profiles with a clear error pointing
at this flag.
See EVSE Profiles for the six bundled profiles (single-AC, dual-AC, DC fast-charger, dual DC ultra-fast, hybrid, V2G) with copy-paste command lines.
Transaction lifecycle¶
The simulator drives a complete transaction lifecycle automatically once the CSMS authorizes the session:
BootNotificationon connect; gates further CP-initiated calls onAccepted.StatusNotification(Available)for each connector.Authorize(idToken)on simulated cable-plug.TransactionEvent(Started)once authorized.MeterValuesat the configured cadence, plusTransactionEvent(Updated)for each state transition (charging → suspended-EV → charging).TransactionEvent(Ended, reason=...)on simulated unplug or remote stop.StatusNotification(Available).
Heartbeat fires at the interval the CSMS returned in its
BootNotificationResponse.
V2G (Vehicle-to-Grid) discharge¶
V2G-capable EVSE profiles advertise bidirectional energy-transfer
modes (AC_BPT, DC_BPT) to the CSMS. During a discharge
transaction:
MeterValuesincludesPower.Active.Exportinstead ofImport.TransactionEventcarries the rightchargingStatetransitions (charging→ev_connected→charging-but-exporting).- OCPP 2.1:
NotifyAllowedEnergyTransferfires mid-transaction when the EV completes ISO 15118-20 capability advertisement and is found to support bidirectional DC.
OCPP 2.1 — DER Control¶
OCPP 2.1's headline feature is a unified SetDERControl envelope
that addresses every IEEE 1547-2018 grid-support mode (Volt-Var,
Volt-Watt, Watt-Var, fixed PF, watt-limit, gradients, enter-service)
on a single message. The simulator reuses the same DER behavior it
exposes over IEEE 2030.5 / CSIP, so an OCPP 2.1 CSMS gets results
indistinguishable from a 2030.5 server driving the same DER.
Supported controlType values¶
controlType |
Behavior |
|---|---|
VoltVar |
Volt-Var curve (reactive power vs. voltage) |
VoltWatt |
Volt-Watt curve (active-power curtailment vs. voltage) |
WattVar |
Watt-Var curve (reactive power vs. active power) |
FixedPFInject |
Fixed power factor while injecting |
FixedPFAbsorb |
Fixed power factor while absorbing |
FixedVar |
Fixed reactive-power setpoint |
LimitMaxDischarge |
Active-power discharge ceiling |
Gradients |
Ramp-rate setpoint |
EnterService |
Enter-service voltage / frequency envelope |
Trip controls (HVMustTrip, LVMustTrip, HFMustTrip,
LFMustTrip, HVMomCess, LVMomCess, etc.) return NotSupported
today.
Supersession stack¶
When multiple SetDERControl calls arrive for the same
(controlType, isDefault) group, the simulator tracks them in a
priority-ordered stack:
- The entry with the lowest numeric
priorityis active and drives the inverter. The rest are superseded and reported asis_superseded=trueinReportDERControl. NotifyDERStartStop(started=true)carries the displaced peers' IDs in thesuperseded_idsfield so the CSMS sees the displacement explicitly.- Clearing the active entry promotes the next-priority peer in the same group so the inverter picks up its parameters seamlessly.
- Re-asserting an existing
controlIdwith a worse priority can demote it; the simulator recomputes against the whole group, not just the previously-active entry.
NotifyDERAlarm on inverter trip¶
While at least one DER control is active, the simulator watches the
inverter's trip state at 1 Hz. On a clean → tripped transition it
emits NotifyDERAlarm(alarm_ended=false) with the matching
GridEventFault cause (UnderVoltage, OverVoltage,
UnderFrequency, OverFrequency). On the clear it emits
alarm_ended=true. One alarm fires per active controlType group;
multiple superseded entries in the same group share the same alarm.
Multi-page ReportDERControl¶
GetDERControl is answered with Accepted, followed by one or
more ReportDERControl calls. The simulator paginates the result
set into chunks of 10 entries by default; every page except the
last carries tbc=true, the final page carries tbc=false. An
empty result set still emits one terminal tbc=false page so the
Accepted request is closed out rather than dangling.
ClearDERControl restore-to-default¶
When the last active overlay (isDefault=false) for a
controlType is cleared, the simulator re-applies the matching
default (isDefault=true) so the inverter reverts to its baseline
IEEE-1547 behavior rather than holding the just-cleared overlay's
setpoints. Within-group supersession promotion still takes
precedence — when a same-group peer is queued, that peer is
promoted and the default is not re-invoked.
OCPP log module¶
Every OCPP CALL / CALLRESULT / CALLERROR frame is mirrored into the
simulator's log buffer under a dedicated OCPP module tag.
Filter the dashboard's Logs view by the OCPP module to see the
live wire conversation between the simulator and your CSMS —
BootNotification, StatusNotification, Authorize,
TransactionEvent, SetDERControl, NotifyDERStartStop,
NotifyDERAlarm, ReportDERControl, the full set — alongside the
simulator's own log lines (boot handshake, DER control
accept/reject, trip detection, etc.).
Sensitive fields are redacted before the log line reaches the buffer:
idToken,idTag— authorization tokens (cardholder PII)certificate,certificateChain,csr,hash— PKI materialpassword— basic-auth credentialscustomerInformation,vin— customer / vehicle identifiers
The value reads <redacted> while the field name stays visible so
a viewer can still tell the frame carried the field. Heartbeat
frames are dropped before reaching the buffer — they fire every
~60 s in steady state and carry no observability value.
Multi-port stations¶
EVSE profiles can declare more than one port (e.g. a dual-cabinet DC fast charger). Each port runs its own connector state machine and transaction lifecycle independently; the inverter / battery shared across ports allocates available power proportionally to per-port demand. MeterValues report the allocated-not-requested kW per port, so an aggregator-side load-balancing test exercising contention sees realistic per-port telemetry.
CLI flag reference (full)¶
Beyond the four commonly-used flags above, the OCPP charge-point client honors these:
| Flag | Default | Description |
|---|---|---|
--ocpp_evse_id |
1 |
Which EVSE entry (from the loaded profile) the OCPP client binds to |
--ocpp_basic_auth_user |
(charge_point_id) | HTTP Basic-auth username for the CSMS connection |
--ocpp_basic_auth_password |
(none) | HTTP Basic-auth password for the CSMS connection (Security Profile 1) |
--ocpp_cert |
(none) | Client certificate PEM for mTLS (Security Profile 2/3); requires --ocpp_key |
--ocpp_key |
(none) | Client private-key PEM for mTLS; requires --ocpp_cert |
--ocpp_root_cert |
(system store) | CA bundle PEM used to verify the CSMS server certificate (wss:// only) |
--ocpp_skip_verify |
no |
Disable server-cert + hostname verification (staging / self-signed only — never production) |
--ocpp_ciphers |
(library default) | OpenSSL cipher list override |
--ocpp_cert_store_dir |
(in-memory) | Directory to persist the OCPP §A2 keypair so cert rotation survives restarts |
Setting up a CSMS¶
The DER Simulator does not distribute a CSMS — pick one from the ecosystem. Two well-known free/open-source options are:
SteVe (OCPP 1.6j)¶
Java-based, MariaDB-backed CSMS from RWTH Aachen.
- Source: steve-community/steve
- Run with Docker: the upstream repo ships a
docker-compose.yml— clone, thendocker compose up -d. SteVe builds the WAR from source on first run; expect ~5–10 min cold, ~30–60 s warm. - Default URLs:
- Operator web UI: http://localhost:8180/steve/manager/home
- OCPP 1.6j WebSocket:
ws://localhost:8180/steve/websocket/CentralSystemService
- Point the simulator at the WS URL with
--ocpp_csms_urland--ocpp_version ocpp1.6j.
CitrineOS (OCPP 2.0.1 + 2.1)¶
TypeScript-based, Postgres + RabbitMQ-backed CSMS sponsored by the Open Charge Alliance — the reference 2.1 implementation.
- Source: citrineos/citrineos-core
- Run with Docker: clone, then
docker compose up -din theServer/directory. - Default URLs:
- Operator web UI: http://localhost:8080/
- OCPP 2.0.1 / 2.1 WebSocket:
ws://localhost:8081/ocpp/<charge_point_id>
- Point the simulator at the WS URL with
--ocpp_csms_urland--ocpp_version ocpp2.0.1jorocpp2.1.
Both CSMSes expect to receive a BootNotification from any charging
station id they don't already know about; check the per-CSMS
configuration if BootNotification is being rejected (SteVe ships
the auto-register-unknown-stations switch; CitrineOS auto-creates
on first contact in its default config).
Web UI — EVSE page¶
When the simulator is launched with --ocpp_enable=yes, the
dashboard's sidebar gains an EVSE / OCPP entry. The page surfaces
per-port state for every EVSE the runtime knows about:
| Column | Meaning |
|---|---|
State |
IEC 61851-1 state (A / B1 / B2 / C / D / E / F) |
OCPP 1.6 |
Mapped 1.6 ChargePointStatus (Available / Preparing / Charging / …) |
OCPP 2.x |
Mapped 2.0.1+ ConnectorStatusEnumType |
Max kW |
Per-port hard rating from the EVSE profile |
Requested |
What the car's curve currently wants |
Allocated |
What the multi-port arbiter actually grants (with throttled chip if reduced) |
Vehicle |
Plugged-in vehicle model + SoC % |
Transaction |
Active OCPP transaction id + delivered kWh, or — |
Per-port action buttons:
- Plug in — opens the vehicle picker (see below) and presents the chosen car to the connector. State transitions A → B2.
- Start — drives a local transaction start (mints idToken, fires Authorize, then TransactionEvent(Started)). Visible once a vehicle is plugged in.
- Stop — ends the active transaction with
Reason=Local. - Unplug — removes the vehicle, state returns to A.
Vehicle picker¶
Picking which car to plug in is a searchable modal over the 50-vehicle catalog bundled with the simulator (Class 1 light-duty through Class 8 heavy-duty trucks). The modal supports:
- Free-text search by manufacturer, model, model id, or class.
- V2G capable only checkbox to filter to bidirectional-capable vehicles for V2G testing.
- Class dropdown to narrow to a specific vehicle class (1–8 / MD / HD).
- Per-vehicle chips showing class, battery capacity (kWh), max AC kW, max DC kW, and a green V2G chip when applicable.
Press Enter on a filtered list to pick the first match; double-click or click Plug in to confirm.
Web UI — OCPP Settings page¶
The OCPP Settings sidebar entry (visible alongside the EVSE / OCPP
page when an EVSE is registered) is where an operator twiddles the
live OCPP charge point without needing the CSMS. The page is split
into three cards:
Authorization¶
Top section. Three toggles map to the OCPP device-model variables
under AuthCtrlr:
- LocalPreAuthorize — when on, the CP authorises an idToken
locally from its cache if a fresh
Acceptedentry exists; only falls through to the CSMS on a cache miss. Saves a round-trip on every plug-in for known cardholders. - LocalAuthorizeOffline — when on and the wire call to the CSMS fails (drop, timeout), the CP falls back to its cache rather than rejecting the cardholder outright.
- AuthorizeRemoteStart — when on, the CP requires an explicit
Authorizefor RemoteStart transactions; when off, the CSMS'sRequestStartTransactionis treated as authoritative.
Below the toggles, the Authorization cache table lists every
cached idToken with its status, optional cache_expiry_date_time,
charging priority, and group token. Each row has a drop button
for surgical removal; Clear all wipes the whole cache.
TLS / Security Profile¶
The Security Profile card shows the persisted state of the Charging Station Certificate keypair (OCPP §A2):
- Store dir — where the keypair lives on disk (set via
--ocpp_cert_store_dir). - Key —
present(with path + SHA prefix) ormissing. - CSR — last CSR sent to the CSMS, with SHA prefix.
- Signed cert — the chain the CSMS returned, with SHA prefix.
The Rotate certificate button kicks off a fresh OCPP §A2 cycle:
the CP mints a new RSA keypair, persists it, ships the CSR via
SignCertificate, and waits for CertificateSigned. When the
signed chain comes back, the bridge automatically tears down the
WebSocket and re-handshakes mTLS with the new client cert. No
operator action needed beyond the button press.
When --ocpp_cert_store_dir is not set, the cert chain lives
in memory only and the Rotate button is disabled. The card surfaces
the disabled state so the operator knows what to fix.
Device model¶
The bottom card lists every variable the OCPP 2.0.1 device model exposes. For each row:
- Component / variable name
- Value — editable inline if the variable is on the editable list (e.g. HeartbeatInterval, TxUpdatedInterval, AuthCtrlr toggles); CSMS-only otherwise
- Flags —
RO(read-only),reboot(change takes effect after a reboot)
Edit and tab/blur to push the change; the new value is applied
immediately to the live device model. The same surface backs the
CSMS's GetVariables / SetVariables calls — what the operator
sees here is what the CSMS sees.
OCPP wire log¶
Filter the dashboard's Logs view by module OCPP to see every
CALL / CALLRESULT / CALLERROR frame the CP and CSMS exchange. Useful
for end-to-end troubleshooting; covered earlier under
OCPP log module.