Bidirectional Power Transfer (V2X) - CSMS Developer Guide
Based on OCPP 2.1 Edition 2 Specification (Part 2), Section Q (Bidirectional Power Transfer). This guide covers all V2X flows (Q01–Q12), including V2X authorization, central and external setpoint control, frequency support, local load balancing, idle mode, and offline handling from the CSMS perspective using OCPP-J (JSON over WebSocket).
1. Overview & Terminology
IntroductionV2X (Vehicle-to-Anything) is bidirectional power transfer between an EV and external systems. This section covers all V2X flows from the CSMS perspective, including authorization, setpoint control, frequency support, load balancing, and offline handling.
What is V2X?
V2G (Vehicle-to-Grid)
Bidirectional power transfer between an EV and the electrical grid.
V2H (Vehicle-to-Home)
Bidirectional power transfer between an EV and a home.
V2B (Vehicle-to-Building)
Bidirectional power transfer between an EV and a building.
Note: V2L (Vehicle-to-Load) is NOT relevant to OCPP as it does not involve OCPP communication.
Prerequisites
The Charging Station must have the following configuration for V2X operations:
Required Configuration
ISO15118Ctrlr.Enabled=trueV2XChargingCtrlr.Enabled=true
Capability Reporting
V2XChargingCtrlr.V2XSupportedOperationModesV2XChargingCtrlr.SupportedEnergyTransferModes
Sign Convention
| Value | Meaning |
|---|---|
Positive setpoint | Charging (grid → EV) |
Negative setpoint | Discharging (EV → grid) |
Positive limit | Maximum allowed charging power (always positive) |
Negative dischargeLimit | Maximum allowed discharging power (always negative, e.g. -5000 W) |
CSMS Role Summary
The CSMS is responsible for the following V2X operations:
- Authorizing V2X operations (providing
allowedEnergyTransferlist) - Responding to
NotifyEVChargingNeedsRequestfrom Charging Stations - Sending
SetChargingProfileRequestwith V2X-enabled charging schedules - Dynamically updating setpoints via
UpdateDynamicScheduleRequest - Responding to
PullDynamicScheduleUpdateRequestwith updated parameters - Sending
AFRRSignalRequestfor frequency regulation - Sending
NotifyAllowedEnergyTransferRequestto update allowed energy transfers mid-session - Handling
NotifyChargingLimitRequestfrom Charging Stations (external system limits) - Handling
NotifyEVChargingScheduleRequestfrom Charging Stations - Handling
TransactionEventRequestmessages with V2X-specific fields
2. V2X Operation Modes Reference
ReferenceEach ChargingSchedulePeriodType can specify an operationMode.
These are defined in OperationModeEnumType.
Mode Reference
| Mode | Description | Who Controls? | CSMS Fields |
|---|---|---|---|
ChargingOnly | Default. Charging only, no discharging. | N/A | limit (required) |
CentralSetpoint | CSMS provides power setpoint. | CSMS | setpoint (required), optionally limit, dischargeLimit |
ExternalSetpoint | External system (e.g. EMS) provides setpoint. | External System | setpoint set externally; CSMS optionally sets limit, dischargeLimit |
ExternalLimits | External system provides limits. | External System | limit, dischargeLimit set externally |
CentralFrequency | CSMS provides setpoint for frequency support. | CSMS (dynamic) | setpoint (required, updated via UpdateDynamicScheduleRequest) |
LocalFrequency | CS uses local frequency readings + curves. | Charging Station | v2xFreqWattCurve, v2xBaseline, optionally v2xSignalWattCurve |
LocalLoadBalancing | CS reads upstream meter, adjusts setpoint. | Charging Station | Thresholds configured via device model |
Idle | EV should not charge or discharge. | N/A | No limit/setpoint/dischargeLimit |
Limits & Setpoints per Operation Mode
| Mode | limit | dischargeLimit | setpoint | setpointReactive |
|---|---|---|---|---|
ChargingOnly | required | - | - | - |
CentralSetpoint | optional | optional | required | optional |
CentralFrequency | optional | optional | required | - |
LocalFrequency | - | - | calculated from freq/signal curves | - |
ExternalSetpoint | optional | optional | set externally | - |
ExternalLimits | set externally | set externally | - | - |
LocalLoadBalancing | - | - | calculated from load and thresholds | - |
3. Key Data Types & Enumerations
ReferenceEnergyTransferModeEnumType
Used in allowedEnergyTransfer and requestedEnergyTransfer:
"AC_single_phase" - AC single-phase charging only
"AC_two_phase" - AC two-phase charging only
"AC_three_phase" - AC three-phase charging only
"DC" - DC charging only
"AC_BPT" - AC Bidirectional Power Transfer
"AC_BPT_DER" - AC BPT with DER (Distributed Energy Resource)
"AC_DER" - AC with DER
"DC_BPT" - DC Bidirectional Power Transfer
"DC_ACDP" - DC with ACDP
"DC_ACDP_BPT" - DC ACDP Bidirectional
"WPT" - Wireless Power TransferBidirectional modes: AC_BPT, AC_BPT_DER, DC_BPT, DC_ACDP_BPT
OperationModeEnumType
"Idle" - No charging/discharging
"ChargingOnly" - Only charging allowed (default)
"CentralSetpoint" - CSMS controls setpoint
"ExternalSetpoint" - External actor controls setpoint
"ExternalLimits" - External actor controls limits
"CentralFrequency" - CSMS controls frequency support
"LocalFrequency" - Local frequency measurements
"LocalLoadBalancing" - Local load balancingChargingProfilePurposeEnumType (V2X relevant)
"TxProfile" - Per-transaction profile (V2X operations)
"TxDefaultProfile" - Default per-transaction profile
"ChargingStationExternalConstraints" - External system constraintsChargingProfileKindEnumType
"Absolute" - Fixed time schedule
"Recurring" - Recurring schedule (daily/weekly)
"Relative" - Relative to transaction start
"Dynamic" - Single period, updated dynamicallyV2X Point Types
{
"frequency": 50.0, // Net frequency in Hz (number, required)
"power": 0 // Power in W: positive=charge, negative=discharge (number, required)
}{
"signal": 0, // Signal value from AFRRSignalRequest (integer, required)
"power": 0 // Power in W: positive=charge, negative=discharge (number, required)
}Power.Active.Residual Measurand
The CSMS can monitor how well the Charging Station follows a setpoint using this measurand:
If Power.Active.Setpoint is None: Residual = 0
If Power.Active.Setpoint == 0: Residual = Power.Active.Import + Power.Active.Export
If Power.Active.Setpoint > 0: Residual = Power.Active.Import - Power.Active.Setpoint
If Power.Active.Setpoint < 0: Residual = Power.Active.Export + Power.Active.SetpointPositive Residual
Charging/discharging at higher power than setpoint.
Negative Residual
Charging/discharging at lower power than setpoint.
4. Q01 — V2X Authorization
CS-Initiated| Use Case ID | Q01 |
| Objective | Authorize an EV for V2X power transfer and start a V2X session |
| Direction | CS → CSMS (CS initiates) |
| Key Messages | AuthorizeRequest, TransactionEventRequest, NotifyEVChargingNeedsRequest, SetChargingProfileRequest |
Step 2: Handle TransactionEventRequest (CS → CSMS)
The CS sends this after authorization with eventType = Started.
For V2X sessions, the CS adds the EVCCID to idToken.additionalInfo.
{}The response body has no required fields.
Step 3: Handle NotifyEVChargingNeedsRequest (CS → CSMS)
This is the critical message where the Charging Station reports the EV's charging needs, including V2X parameters.
{
"evseId": 1,
"chargingNeeds": {
"requestedEnergyTransfer": "DC_BPT",
"availableEnergyTransfer": ["DC", "DC_BPT"],
"controlMode": "DynamicControl",
"departureTime": "2026-02-17T18:00:00Z",
"v2xChargingParameters": {
"minChargePower": 500,
"maxChargePower": 11000,
"minDischargePower": 500,
"maxDischargePower": 11000,
"maxChargeCurrent": 32,
"maxDischargeCurrent": 32,
"minVoltage": 230,
"maxVoltage": 400,
"evTargetEnergyRequest": 20000,
"evMinEnergyRequest": 5000,
"evMaxEnergyRequest": 40000,
"evMinV2XEnergyRequest": -15000,
"evMaxV2XEnergyRequest": 10000,
"targetSoC": 80
}
}
}NotifyEVChargingNeedsResponse
| Scenario | status value | Meaning |
|---|---|---|
| CSMS accepts and can provide profile now | Accepted | CS expects a SetChargingProfileRequest soon |
| CSMS accepts but needs time | Processing | CS expects a SetChargingProfileRequest later |
| CSMS accepts but uses TxDefaultProfile | NoChargingProfile | No SetChargingProfileRequest will follow |
| CSMS rejects requested energy transfer | Rejected | CS will stop the transaction |
{
"status": "Accepted"
}{
"status": "Rejected"
}Requirement Q01.FR.04: If CSMS does not accept the requestedEnergyTransfer, respond with status = Rejected.
Requirement Q01.FR.07: If CSMS accepts, respond with Accepted (profile ready), Processing (need more time), or NoChargingProfile (use TxDefaultProfile).
Step 4: Send SetChargingProfileRequest (CSMS → CS)
After accepting the charging needs, send a V2X charging profile:
{
"evseId": 1,
"chargingProfile": {
"id": 100,
"stackLevel": 2,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Absolute",
"transactionId": "tx-abc-123",
"chargingSchedule": [
{
"id": 1,
"chargingRateUnit": "W",
"chargingSchedulePeriod": [
{
"startPeriod": 0,
"operationMode": "CentralSetpoint",
"limit": 11000,
"dischargeLimit": -11000,
"setpoint": 5000
}
]
}
]
}
}{
"status": "Accepted"
}{
"status": "Rejected",
"statusInfo": {
"reasonCode": "InvalidSchedule",
"additionalInfo": "setpoint outside limit/dischargeLimit range"
}
}Step 5 (Optional): Update Allowed Energy Transfer Mid-Session
If V2X authorization arrives later (e.g. third-party approval), send NotifyAllowedEnergyTransferRequest:
{
"transactionId": "tx-abc-123",
"allowedEnergyTransfer": ["DC", "DC_BPT"]
}{
"status": "Accepted"
}{
"transactionId": string (max 36, required) // Transaction ID
"allowedEnergyTransfer": [ // required, min 1 item
EnergyTransferModeEnumType
]
}Requirement Q02.FR.05: When CSMS changes the list of allowed energy transfers for a transaction, CSMS SHALL send NotifyAllowedEnergyTransferRequest.
Error Handling: If the requestedEnergyTransfer from NotifyEVChargingNeedsRequest is not accepted, respond with Rejected and the CS will stop the current transaction.
5. Q02 — Starting in ChargingOnly Before Enabling V2X
CSMS-Initiated| Use Case ID | Q02 |
| Objective | Start a transaction in ChargingOnly mode and upgrade to V2X later |
| When to Use | When the CSMS is unsure during energy service negotiation whether V2X is allowed |
Steps 1–3: Initial ChargingOnly Setup
Step 1: Handle AuthorizeRequest — Omit allowedEnergyTransfer
{
"idTokenInfo": {
"status": "Accepted"
}
}Step 2: Handle NotifyEVChargingNeedsRequest
The CS will send requestedEnergyTransfer set to its default (charging-only, e.g. DC or AC_single_phase).
{
"status": "Accepted"
}Step 3: Send SetChargingProfileRequest with ChargingOnly
{
"evseId": 1,
"chargingProfile": {
"id": 50,
"stackLevel": 1,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Absolute",
"transactionId": "tx-abc-123",
"chargingSchedule": [{
"id": 1,
"chargingRateUnit": "W",
"chargingSchedulePeriod": [{
"startPeriod": 0,
"operationMode": "ChargingOnly",
"limit": 11000
}]
}]
}
}Requirement Q02.FR.01: When CS does not request bidirectional transfer, CSMS SHALL send SetChargingProfileRequest with operationMode = ChargingOnly (or omit it, as ChargingOnly is the default).
Requirement Q02.FR.02: In ChargingOnly mode, CSMS SHALL NOT include dischargeLimit, setpoint, or setpointReactive.
Steps 4–6: Enable V2X Later
Step 4: Determine V2X Permission (async)
Use the EVCCID from additionalInfo in the AuthorizeRequest or from ConnectedEV.VehicleId device model variable.
Optionally check the vehicle certificate from ConnectedEV.VehicleCertificate.
Step 5: Send NotifyAllowedEnergyTransferRequest to Enable V2X
{
"transactionId": "tx-abc-123",
"allowedEnergyTransfer": ["DC", "DC_BPT"]
}This triggers a service renegotiation between CS and EV. The CS will then send a new NotifyEVChargingNeedsRequest with the updated requestedEnergyTransfer.
Step 6: Handle New NotifyEVChargingNeedsRequest and Send V2X Profile
Respond with Accepted and follow up with a V2X-enabled SetChargingProfileRequest (as in Q01 Step 4).
6. Q03 — Central V2X Control with Charging Schedule
CSMS-Initiated| Use Case ID | Q03 |
| Objective | CSMS controls EV charge/discharge behavior via a power profile with scheduled periods using CentralSetpoint mode |
| Prerequisites | Profile purpose: TxProfile or TxDefaultProfile. Transaction must be active with V2X authorization. |
Scheduled Mode (Multiple Periods)
{
"evseId": 1,
"chargingProfile": {
"id": 200,
"stackLevel": 2,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Absolute",
"transactionId": "tx-abc-123",
"chargingSchedule": [{
"id": 1,
"chargingRateUnit": "W",
"startSchedule": "2026-02-17T08:00:00Z",
"chargingSchedulePeriod": [
{
"startPeriod": 0,
"operationMode": "CentralSetpoint",
"setpoint": 7000,
"limit": 11000,
"dischargeLimit": -5000
},
{
"startPeriod": 7200,
"operationMode": "CentralSetpoint",
"setpoint": -3000,
"limit": 11000,
"dischargeLimit": -5000
},
{
"startPeriod": 14400,
"operationMode": "CentralSetpoint",
"setpoint": 5000,
"limit": 11000,
"dischargeLimit": -5000
}
]
}]
}
}Requirement Q03.FR.01: CSMS SHALL send SetChargingProfileRequest with operationMode = CentralSetpoint.
Requirement Q03.FR.02: CSMS SHALL provide a value for setpoint (optionally L2 and L3 variants).
Requirement Q03.FR.03: CSMS MAY include limit and/or dischargeLimit to constrain the setpoint range.
Dynamic Mode Variant
If chargingProfileKind = Dynamic, the CSMS can update the setpoint without sending a new profile:
{
"evseId": 1,
"chargingProfile": {
"id": 201,
"stackLevel": 2,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Dynamic",
"transactionId": "tx-abc-123",
"chargingSchedule": [{
"id": 1,
"chargingRateUnit": "W",
"chargingSchedulePeriod": [{
"startPeriod": 0,
"operationMode": "CentralSetpoint",
"setpoint": 5000,
"limit": 11000,
"dischargeLimit": -5000
}]
}]
}
}Then update dynamically with UpdateDynamicScheduleRequest (see Q04).
7. Q04 — Central V2X Control with Dynamic CSMS Setpoint
CSMS-Initiated| Use Case ID | Q04 |
| Objective | CSMS dynamically controls the setpoint in real-time, updating it frequently |
| Prerequisites | Profile purpose: TxProfile or TxDefaultProfile. Profile kind: Dynamic. |
Step 1: Send Initial SetChargingProfileRequest
{
"evseId": 1,
"chargingProfile": {
"id": 300,
"stackLevel": 2,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Dynamic",
"transactionId": "tx-abc-123",
"dynUpdateInterval": 60,
"chargingSchedule": [{
"id": 1,
"chargingRateUnit": "W",
"duration": 120,
"chargingSchedulePeriod": [{
"startPeriod": 0,
"operationMode": "CentralSetpoint",
"setpoint": 5000
}]
}]
}
}Key Fields
chargingProfileKind:Dynamic(required)dynUpdateInterval: seconds between pull updates from CS (optional, 0 = no pulling)chargingSchedule.duration: if set, schedule expires after this many seconds without update
Step 2a: Push Updates via UpdateDynamicScheduleRequest (CSMS → CS)
{
"chargingProfileId": 300,
"scheduleUpdate": {
"setpoint": 2000
}
}{
"chargingProfileId": integer (required) // ID of the dynamic charging profile
"scheduleUpdate": { // required
"limit": number, // optional
"limit_L2": number, // optional
"limit_L3": number, // optional
"dischargeLimit": number (max 0), // optional, negative values
"dischargeLimit_L2": number (max 0), // optional
"dischargeLimit_L3": number (max 0), // optional
"setpoint": number, // optional
"setpoint_L2": number, // optional
"setpoint_L3": number, // optional
"setpointReactive": number, // optional
"setpointReactive_L2": number, // optional
"setpointReactive_L3": number // optional
}
}{
"status": "Accepted"
}Step 2b: Handle Pull Updates via PullDynamicScheduleUpdateRequest (CS → CSMS)
When dynUpdateInterval is set, the CS pulls updates at that interval.
{
"chargingProfileId": 300
}{
"status": "Accepted",
"scheduleUpdate": {
"setpoint": 2000
}
}{
"status": "Accepted" | "Rejected" (required)
"scheduleUpdate": { // optional (only when Accepted)
"limit": number,
"limit_L2": number,
"limit_L3": number,
"dischargeLimit": number (max 0),
"dischargeLimit_L2": number (max 0),
"dischargeLimit_L3": number (max 0),
"setpoint": number,
"setpoint_L2": number,
"setpoint_L3": number,
"setpointReactive": number,
"setpointReactive_L2": number,
"setpointReactive_L3": number
},
"statusInfo": { // optional
"reasonCode": string (max 20),
"additionalInfo": string (max 1024)
}
}Duration / Fallback Behavior
If chargingSchedule.duration is set and no update
(SetChargingProfileRequest, UpdateDynamicScheduleRequest, or PullDynamicScheduleUpdate) is received within duration seconds, the charging profile expires
and the CS falls back to the next valid profile at a lower stack level.
8. Q05 — External V2X Setpoint Control with Charging Profile from CSMS
CSMS-Initiated| Use Case ID | Q05 |
| Objective | CSMS delegates real-time power control to an External System (e.g. EMS) while retaining control over the charging profile structure |
| Prerequisites | ExternalControlSignalsEnabled = true, ExternalConstraintsProfileDisallowed = true, Profile kind: Dynamic |
CSMS Action: Send SetChargingProfileRequest
ExternalSetpoint Mode
{
"evseId": 1,
"chargingProfile": {
"id": 400,
"stackLevel": 2,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Dynamic",
"transactionId": "tx-abc-123",
"chargingSchedule": [{
"id": 1,
"chargingRateUnit": "W",
"duration": 3600,
"chargingSchedulePeriod": [{
"startPeriod": 0,
"operationMode": "ExternalSetpoint",
"setpoint": 0,
"limit": 11000,
"dischargeLimit": -5000
}]
}]
}
}ExternalLimits Mode
{
"operationMode": "ExternalLimits",
"limit": 11000,
"dischargeLimit": -5000
}Behavior
ExternalSetpoint
The External System controls setpoint and setpointReactive.
CSMS optionally provides limit and dischargeLimit.
ExternalLimits
The External System controls limit and dischargeLimit.
Setpoint is not used.
For Dynamic profiles, the External System can update values dynamically via the Charging Station's local interface.
The CS updates dynUpdateTime whenever it receives an external update.
If duration is set and no update is received within duration seconds, the profile expires.
9. Q06 — External V2X Control with Charging Profile from External System
External| Use Case ID | Q06 |
| Objective | An External System (e.g. EMS) directly controls charging limits or setpoints via a ChargingStationExternalConstraints profile |
CSMS Role
The CSMS does not directly send the charging profile in this flow. Instead, the External System sends it directly to the Charging Station. However, the CSMS:
- Receives
NotifyChargingLimitRequestmessages from the Charging Station informing it of the external limits. - May need to manage priority via
SmartChargingCtrlr.SetpointPriority.
Handle NotifyChargingLimitRequest (CS → CSMS)
{
"chargingLimit": {
"chargingLimitSource": "EMS",
"isGridCritical": false
},
"evseId": 0,
"chargingSchedule": [{
"id": 1,
"chargingRateUnit": "W",
"chargingSchedulePeriod": [{
"startPeriod": 0,
"operationMode": "ExternalLimits",
"limit": 15000,
"dischargeLimit": -10000
}]
}]
}{}Empty response body — no required fields.
Setpoint Priority (Q06.FR.20-22)
When both a ChargingStationExternalConstraints profile with ExternalSetpoint AND a TxProfile/TxDefaultProfile also define a setpoint, the variable SmartChargingCtrlr.SetpointPriority determines which takes precedence:
| Priority Value | Behavior |
|---|---|
"ExternalControl" (or absent) | Use setpoint from ChargingStationExternalConstraints profile |
"CSMS" | Use setpoint from TxProfile or TxDefaultProfile, overruling external |
10. Q07 — Central V2X Control for Frequency Support
CSMS-Initiated| Use Case ID | Q07 |
| Objective | CSMS controls the power setpoint for frequency support, updating it dynamically based on centrally measured frequency |
| Prerequisites | CS reports CentralFrequency in V2XSupportedOperationModes |
Step 1: Send SetChargingProfileRequest
{
"evseId": 1,
"chargingProfile": {
"id": 500,
"stackLevel": 2,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Dynamic",
"transactionId": "tx-abc-123",
"chargingSchedule": [{
"id": 1,
"chargingRateUnit": "W",
"duration": 60,
"chargingSchedulePeriod": [{
"startPeriod": 0,
"operationMode": "CentralFrequency",
"setpoint": 0
}]
}]
}
}Requirement Q07.FR.02: CSMS SHALL send a profile with chargingProfileKind = Dynamic and operationMode = CentralFrequency.
Requirement Q07.FR.03: CSMS SHALL NOT include limit and dischargeLimit (including L2/L3 variants).
Requirement Q07.FR.04: CSMS IS RECOMMENDED to set a duration to prevent indefinite operation if CSMS becomes unavailable.
Step 2: Periodically Update Setpoint
Use UpdateDynamicScheduleRequest to push frequency-based setpoint changes:
{
"chargingProfileId": 500,
"scheduleUpdate": {
"setpoint": -3000
}
}Fallback Behavior: If no UpdateDynamicScheduleRequest is received before duration expires, the profile terminates. If the CS supports LocalFrequency, it can fall back to a lower-stack-level profile with LocalFrequency mode.
11. Q08 — Local V2X Control for Frequency Support
CSMS-Initiated| Use Case ID | Q08 |
| Objective | CSMS provides frequency-watt curves and the Charging Station autonomously adjusts power based on local frequency measurements |
Scenario 1: FCR (Frequency Containment Reserve)
{
"evseId": 1,
"chargingProfile": {
"id": 600,
"stackLevel": 2,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Absolute",
"transactionId": "tx-abc-123",
"chargingSchedule": [{
"id": 1,
"chargingRateUnit": "W",
"chargingSchedulePeriod": [{
"startPeriod": 0,
"operationMode": "LocalFrequency",
"v2xBaseline": 0,
"v2xFreqWattCurve": [
{ "frequency": 47.0, "power": -10000 },
{ "frequency": 49.80, "power": -10000 },
{ "frequency": 49.99, "power": -500 },
{ "frequency": 49.991, "power": 0 },
{ "frequency": 50.0, "power": 0 },
{ "frequency": 50.009, "power": 0 },
{ "frequency": 50.01, "power": 500 },
{ "frequency": 50.20, "power": 10000 },
{ "frequency": 53.0, "power": 10000 }
]
}]
}]
}
}Scenario 2: FCR + aFRR (Automatic Frequency Restoration Reserve)
Add v2xSignalWattCurve for aFRR:
{
"startPeriod": 0,
"operationMode": "LocalFrequency",
"v2xBaseline": 0,
"v2xFreqWattCurve": [
{ "frequency": 47.0, "power": -10000 },
{ "frequency": 49.80, "power": -10000 },
{ "frequency": 49.99, "power": -500 },
{ "frequency": 50.0, "power": 0 },
{ "frequency": 50.01, "power": 500 },
{ "frequency": 50.20, "power": 10000 },
{ "frequency": 53.0, "power": 10000 }
],
"v2xSignalWattCurve": [
{ "signal": -1, "power": -2000 },
{ "signal": 0, "power": 0 },
{ "signal": 1, "power": 2000 }
]
}Validation Rules
The CS will reject the profile with SetChargingProfileResponse.status = Rejected if:
| Condition | reasonCode |
|---|---|
| v2xFreqWattCurve missing or has < 2 points, or v2xBaseline missing | "NoFreqWattCurve" |
| Contains limit, dischargeLimit, setpoint, or setpointReactive | "InvalidSchedule" |
Requirement Q08.FR.05: The chargingRateUnit MUST be W for LocalFrequency schedules.
Setpoint Calculation (done by CS)
setpoint = v2xBaseline + interpolate(v2xFreqWattCurve, measuredFrequency) + interpolate(v2xSignalWattCurve, signal)Send AFRRSignalRequest for aFRR (CSMS → CS)
When the CSMS receives an aFRR signal from a TSO:
{
"timestamp": "2026-02-17T14:30:00Z",
"signal": 1
}{
"timestamp": string (date-time, required) // Time when signal becomes active
"signal": integer (required) // Value mapped to v2xSignalWattCurve
}{
"status": "Accepted"
}{
"status": "Rejected",
"statusInfo": {
"reasonCode": "NoSignalWattCurve"
}
}Requirement Q08.FR.12: When CSMS receives an aFRR signal from external actor (TSO), CSMS SHALL send AFRRSignalRequest with timestamp = current time and signal = value from external actor.
12. Q09 — Local V2X Control for Load Balancing
CSMS-Initiated| Use Case ID | Q09 |
| Objective | CSMS provides a LocalLoadBalancing schedule and the Charging Station autonomously adjusts power based on local grid meter readings |
CSMS Action: Send SetChargingProfileRequest
{
"evseId": 1,
"chargingProfile": {
"id": 700,
"stackLevel": 2,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Absolute",
"transactionId": "tx-abc-123",
"chargingSchedule": [{
"id": 1,
"chargingRateUnit": "W",
"chargingSchedulePeriod": [{
"startPeriod": 0,
"operationMode": "LocalLoadBalancing"
}]
}]
}
}Rejection Conditions
| Condition | reasonCode |
|---|---|
| LocalLoadBalancing not in V2XSupportedOperationModes | "UnsupportedParam" |
| Required thresholds not configured | "MissingDevModelInfo" |
Configuration Variables (set via SetVariablesRequest)
These must be configured on the Charging Station before using LocalLoadBalancing:
| Variable | Description |
|---|---|
V2XLocalLoadBalancing[UpperThreshold] | Load above this triggers discharge |
V2XLocalLoadBalancing[LowerThreshold] | Load below this triggers charge |
V2XLocalLoadBalancing[UpperOffset] | Overshoot above upper threshold |
V2XLocalLoadBalancing[LowerOffset] | Overshoot below lower threshold |
Setpoint Algorithm (executed by CS)
Load = get latest load power reading()
If Load > UpperThreshold:
DeltaSetpoint = UpperThreshold + UpperOffset - Load
Else If Load < LowerThreshold:
DeltaSetpoint = LowerThreshold + LowerOffset - Load
Else:
DeltaSetpoint = 0
setpoint = setpoint + DeltaSetpointSetpoint is capped by limit (positive) and dischargeLimit (negative) when set.
13. Q10 — Idle, Minimizing Energy Consumption
CSMS-Initiated| Use Case ID | Q10 |
| Objective | Request the EV to stop all charging/discharging and optionally precondition or sleep |
Scenario 1: Idle with Preconditioning
{
"evseId": 1,
"chargingProfile": {
"id": 800,
"stackLevel": 2,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Absolute",
"transactionId": "tx-abc-123",
"chargingSchedule": [{
"id": 1,
"chargingRateUnit": "W",
"chargingSchedulePeriod": [{
"startPeriod": 0,
"operationMode": "Idle",
"preconditioningRequest": true
}]
}]
}
}Scenario 2: Idle without Preconditioning
{
"startPeriod": 0,
"operationMode": "Idle",
"preconditioningRequest": false
}Scenario 3: EVSE Sleep
{
"startPeriod": 0,
"operationMode": "Idle",
"evseSleep": true
}When evseSleep = true, the CS turns off power electronics.
The CS reports evseSleep = true in subsequent TransactionEventRequest messages.
Requirement Q10.FR.01: In Idle mode, limit, dischargeLimit, setpoint, and setpointReactive (including L2/L3) SHALL be absent.
Requirement Q10.FR.02: CS will reject profiles that include those fields with reasonCode = "InvalidSchedule".
14. Q11 — Going Offline During V2X Operation
CSMS-Initiated| Use Case ID | Q11 |
| Objective | Define how long V2X operations continue when the Charging Station loses connection to CSMS |
CSMS Action: Set maxOfflineDuration on Charging Profiles
{
"evseId": 1,
"chargingProfile": {
"id": 900,
"stackLevel": 2,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Absolute",
"transactionId": "tx-abc-123",
"maxOfflineDuration": 300,
"invalidAfterOfflineDuration": false,
"chargingSchedule": [{
"id": 1,
"chargingRateUnit": "W",
"chargingSchedulePeriod": [{
"startPeriod": 0,
"operationMode": "CentralSetpoint",
"setpoint": 5000,
"limit": 11000,
"dischargeLimit": -5000
}]
}]
}
}Key Fields
| Field | Type | Description |
|---|---|---|
maxOfflineDuration | integer (seconds) | Time the profile stays valid after going offline. 0 = immediately invalid. Absent = no timeout. |
invalidAfterOfflineDuration | boolean | If true, profile is permanently invalidated after timeout. If false (default), profile reactivates when connection is restored. |
Behavior
- CS detects it is offline.
maxOfflineDurationcountdown starts.- After
maxOfflineDurationseconds, CS falls back to the next valid profile with a lower stack level. - CSMS should assume V2X has reverted when it detects the CS is offline.
Recommended Stack Level Strategy
Send two profiles:
- Stack level 1:
ChargingOnly(safe fallback) - Stack level 2: V2X operation (e.g.
CentralSetpoint) withmaxOfflineDuration
15. Q12 — Resuming V2X After Offline Period
CSMS-Initiated| Use Case ID | Q12 |
| Objective | Resume V2X operations after the Charging Station reconnects |
Scenarios
Scenario 1: maxOfflineDuration NOT elapsed
The V2X profile remained active. No CSMS action needed. CS continues with the same profile.
Scenario 2: maxOfflineDuration elapsed, invalidAfterOfflineDuration = false
- CS reconnects.
- CS re-evaluates profiles and selects the V2X profile (stack level 2) again.
- V2X resumes automatically.
- CSMS can send a new
SetChargingProfileRequestif needed.
Scenario 3: maxOfflineDuration elapsed, invalidAfterOfflineDuration = true
- CS reconnects.
- The V2X profile is permanently invalid.
- CS continues using the fallback profile (stack level 1,
ChargingOnly). - CSMS must send a new
SetChargingProfileRequestto re-establish V2X.
CSMS Implementation
on_connection_restored(charging_station):
if invalidAfterOfflineDuration was true:
// Must send a new V2X profile
send_new_v2x_charging_profile(charging_station)
else:
// Profile may have auto-restored, verify via GetCompositeSchedule
verify_active_profile(charging_station)16. Generic V2X Smart Charging Rules
ReferenceThese rules apply across ALL V2X use cases:
| Rule ID | Description |
|---|---|
V2X.01 | All requirements from section K (Smart Charging) also apply to V2X. |
V2X.02 | A setpoint (or setpointReactive) is a target value; a limit is a maximum value. |
V2X.03 | CS SHALL cap positive setpoint to limit value (e.g. setpoint=2000, limit=1000 → sent to EV: 1000). |
V2X.04 | CS SHALL cap negative setpoint to dischargeLimit (e.g. setpoint=-2000, dischargeLimit=-1000 → sent to EV: -1000). Does NOT apply to setpointReactive. When chargingRateUnit = A, limit is about current. |
V2X.05 | CS SHALL reject SetChargingProfileRequest with Rejected + reasonCode="InvalidSchedule" if setpoint is outside the range [dischargeLimit, limit]. E.g. reject if setpoint=-750 when limit=1000 and dischargeLimit=-500. |
V2X.06 | Requirements for limit, dischargeLimit, setpoint, setpointReactive also apply to _L2 and _L3 variants. |
V2X.07 | L2/L3 variants can only occur in combination with the base field (without postfix). |
V2X.08 | CSMS SHALL NOT provide L2/L3 values for dischargeLimit, setpoint(Reactive) if the EV didn't report maxDischargePower_L2/_L3 in NotifyEVChargingNeedsRequest. |
V2X.09 | CSMS SHALL NOT provide L2/L3 discharge/setpoint fields for profiles other than TxProfile. |
V2X.10 | If V2X.08 or V2X.09 is violated and CS receives L2/L3 fields, CS SHALL reject with status=Rejected, reasonCode="PhaseConflict". |
17. Configuration Variables Reference
Reference| Variable | Description |
|---|---|
ISO15118Ctrlr.Enabled | Must be true for V2X |
V2XChargingCtrlr.Enabled | Must be true for V2X |
V2XChargingCtrlr.V2XSupportedOperationModes | List of supported operation modes |
V2XChargingCtrlr.SupportedEnergyTransferModes | List of supported energy transfer modes |
V2XChargingCtrlr.TxStartedMeasurands | Measurands to include at transaction start per operation mode |
V2XChargingCtrlr.TxEndedMeasurands | Measurands to include at transaction end per operation mode |
V2XChargingCtrlr.TxUpdatedMeasurands | Measurands to include in periodic updates per operation mode |
V2XChargingCtrlr.LocalFrequencyUpdateThreshold | Min frequency change (mHz) to trigger recalculation |
V2XLocalLoadBalancing[UpperThreshold] | Load threshold to start discharging |
V2XLocalLoadBalancing[LowerThreshold] | Load threshold to start charging |
V2XLocalLoadBalancing[UpperOffset] | Overshoot above upper threshold |
V2XLocalLoadBalancing[LowerOffset] | Overshoot below lower threshold |
ExternalControlSignalsEnabled | Must be true for Q05 |
ExternalConstraintsProfileDisallowed | When true, blocks external system from setting profiles directly (Q05). When false or absent, allows external profiles (Q06). |
SmartChargingCtrlr.SetpointPriority | "ExternalControl" or "CSMS" — determines priority between external and CSMS setpoints |
SmartChargingCtrlr.SupportsEvseSleep | Whether EVSE supports sleep mode |
SmartChargingCtrlr.LimitChangeSignificance | Threshold for reporting limit changes |
ConnectedEV.VehicleId | EVCCID of connected EV |
ConnectedEV.VehicleCertificate | Vehicle certificate |
ConnectedEV.Available | Whether EV is connected |
ConnectedEV.ProtocolAgreed | Agreed protocol |
ConnectedEV.ProtocolSupportedByEV | Protocols supported by EV |
18. Message Direction Summary
Reference| Message | Direction | CSMS Role |
|---|---|---|
AuthorizeRequest | CS → CSMS | Respond with allowedEnergyTransfer |
AuthorizeResponse | CSMS → CS | Include allowedEnergyTransfer array |
TransactionEventRequest | CS → CSMS | Handle V2X fields (operationMode, evseSleep) |
TransactionEventResponse | CSMS → CS | Standard response |
NotifyEVChargingNeedsRequest | CS → CSMS | Respond with accept/reject, then send profile |
NotifyEVChargingNeedsResponse | CSMS → CS | Accepted/Processing/NoChargingProfile/Rejected |
SetChargingProfileRequest | CSMS → CS | Send V2X charging profiles |
SetChargingProfileResponse | CS → CSMS | Accepted/Rejected with reason |
UpdateDynamicScheduleRequest | CSMS → CS | Send dynamic setpoint updates |
UpdateDynamicScheduleResponse | CS → CSMS | Accepted/Rejected |
PullDynamicScheduleUpdateRequest | CS → CSMS | Respond with updated schedule parameters |
PullDynamicScheduleUpdateResponse | CSMS → CS | Include scheduleUpdate with new values |
AFRRSignalRequest | CSMS → CS | Send aFRR signal for frequency support |
AFRRSignalResponse | CS → CSMS | Accepted/Rejected |
NotifyAllowedEnergyTransferRequest | CSMS → CS | Send updated allowed energy transfer list |
NotifyAllowedEnergyTransferResponse | CS → CSMS | Accepted/Rejected |
NotifyChargingLimitRequest | CS → CSMS | Handle external limit notifications |
NotifyChargingLimitResponse | CSMS → CS | Empty response |
NotifyEVChargingScheduleRequest | CS → CSMS | Handle EV-selected schedule notifications |
NotifyEVChargingScheduleResponse | CSMS → CS | Accepted/Rejected |
Quick Decision Tree for CSMS
EV plugs in
|
v
AuthorizeRequest received
|
+--> Can determine V2X permission now?
| YES --> Include allowedEnergyTransfer in AuthorizeResponse
| NO --> Omit allowedEnergyTransfer (flow Q02)
|
v
TransactionEventRequest (Started) received
|
v
NotifyEVChargingNeedsRequest received
|
+--> Is requestedEnergyTransfer a BPT mode?
| YES --> Accept, send V2X profile (Q01/Q03/Q04/Q07/Q08/Q09)
| NO --> Accept, send ChargingOnly profile (Q02)
|
+--> Is requestedEnergyTransfer rejected?
YES --> Respond with status=Rejected
|
v
Choose V2X operation mode:
|
+--> CSMS controls setpoint with schedule? ---------> Q03 (CentralSetpoint, Absolute)
+--> CSMS controls setpoint dynamically? -----------> Q04 (CentralSetpoint, Dynamic)
+--> External system controls via CSMS profile? ----> Q05 (ExternalSetpoint/ExternalLimits)
+--> External system controls directly? ------------> Q06 (ChargingStationExternalConstraints)
+--> CSMS controls frequency support? --------------> Q07 (CentralFrequency)
+--> Local frequency support with curves? ----------> Q08 (LocalFrequency)
+--> Local load balancing? -------------------------> Q09 (LocalLoadBalancing)
+--> Pause/idle EV? -------------------------------> Q10 (Idle)