OCPP Smart Charging Explained: ChargingProfiles in Practice

How OCPP smart charging works: profiles, schedules, stacking, and the operational realities of controlling charger power output via OCPP.

OCPP smart charging is how a CSMS controls the power a charger delivers: how much, to which connector, and when. It builds on the core OCPP protocol and is the technical foundation for dynamic load management (coming soon), time-of-use optimization, demand-response participation, and grid integration.

It is also the part of OCPP where “supported” is least likely to mean what you assume. There is a structural reason for that. In 1.6 smart charging is an optional feature profile rather than core, so a charger can pass conformance without implementing it, and a datasheet that says “smart charging: yes” is not a statement anyone had to prove. The gap only shows up when you send a profile and measure whether the power actually changed.

This article covers what OCPP smart charging defines, how to use it correctly, and where the operational gotchas hide.

The core concept

A charging profile is an instruction to a charger that says “limit charging power according to this schedule, for this purpose, with this stack level.”

The CSMS sends profiles via SetChargingProfile. The charger applies them. When several profiles apply at once, say a default profile plus a transaction-specific one, they stack, and the delivered power is the minimum of all applicable limits.

This stacking is what makes OCPP smart charging more flexible than a simple “set max power” command. You can layer constraints: site-level DLM at the bottom, time-of-use optimization in the middle, per-transaction customization at the top.

The three profile purposes and the minimum rule fit together like this:

flowchart TD
  A[ChargePointMaxProfile<br/>site cap] --> D[Active limit<br/>= minimum]
  B[TxDefaultProfile<br/>default per session] --> D
  C[TxProfile<br/>specific session] --> D
  D --> E[Delivered power]
  style D fill:#0c590c,stroke:#0c590c,color:#fff

The three profile purposes

OCPP defines three purposes that profiles can have.

ChargePointMaxProfile

The absolute maximum for the entire charger, regardless of any other profile. Applies to all connectors, all transactions.

Typical use: enforce a site-level DLM constraint. “This charger may never exceed X amps regardless of what other profiles say.”

Stack level: usually 0 (or low). It’s the foundation.

TxDefaultProfile

The default profile for any transaction that starts on this charger. Applies when no specific TxProfile overrides it.

Typical use: define the default charging behavior. “By default, charge up to Y amps.”

Stack level: variable.

TxProfile

A profile for a specific transaction. Identified by transactionId. Overrides TxDefaultProfile for that transaction.

Typical use is per-session smart charging, applied when a session starts based on the eMSP’s request, the driver’s preferences, or a grid signal.

Stack level: variable. Higher stack level overrides lower-level profile of the same type.

The schedule within a profile

A profile contains a ChargingSchedule, which is where the actual power-over-time data lives.

The schedule has:

  • chargingRateUnit: A (amps) or W (watts). Pick one and stay consistent.
  • chargingSchedulePeriod: an array of periods. Each period has:
    • startPeriod: offset in seconds from the schedule start.
    • limit: maximum power for this period (in the chosen unit).
    • numberPhases: how many phases this limit applies to (1 or 3 for AC).
  • duration: how long the whole schedule lasts (seconds).
  • startSchedule: when the schedule starts (datetime).
  • minChargingRate: minimum rate (rarely used; lets you say “if you charge, charge at least this much”).

A typical schedule:

{
  "chargingRateUnit": "A",
  "chargingSchedulePeriod": [
    { "startPeriod": 0,    "limit": 16 },
    { "startPeriod": 3600, "limit": 32 },
    { "startPeriod": 10800,"limit": 48 }
  ],
  "duration": 14400
}

This says: “Charge at 16A for the first hour, 32A for the next 2 hours, 48A for the final hour, total 4 hours.”

The charger applies these limits over time. When the schedule ends, normal behavior resumes (typically: continue at the default profile or the next applicable profile).

How profile stacking works

When multiple profiles apply, the charger computes the actual limit at each moment as the minimum across all applicable profiles.

Example:

  • ChargePointMaxProfile: 60A always.
  • TxDefaultProfile: 32A during peak hours, 48A off-peak.
  • TxProfile (this session): 40A from now for 2 hours.

At a given moment during peak hours: min(60, 32, 40) = 32A. At a given moment during off-peak: min(60, 48, 40) = 40A.

The charger evaluates this continuously. If profiles change, the active limit changes immediately.

The stacking rule is what makes this workable at all. The site operator, the eMSP, and the driver can each push a constraint without anyone having to negotiate, because taking the minimum resolves every combination deterministically and always in the safe direction.

Practical use cases

A few scenarios that smart charging via OCPP enables.

Dynamic load management

Site has 100 kW available. 20 chargers rated 11 kW each. DLM controller computes per-charger limits in real time as cars arrive and depart. Pushes ChargePointMaxProfile updates accordingly.

When 2 cars are charging: the per-vehicle share works out at 50 kW, which is above what an 11 kW charger can deliver anyway, so each simply charges at its full 11 kW. When 10 cars are charging: each gets 10 kW (sharing the 100 kW budget). When 20 cars are charging: each gets 5 kW.

The CSMS-driven approach uses SetChargingProfile messages. Local DLM controllers may use a different mechanism (per-charger limits via local API) but the concept is the same.

Time-of-use optimization

User wants to charge cheaply overnight. CSMS knows the time-of-use tariff. CSMS pushes a TxProfile that ramps low during peak hours, high during off-peak.

Example schedule:

  • 8pm-10pm (peak): 7 kW limit.
  • 10pm-6am (off-peak): 22 kW max.

The car charges slow during peak (saving the user money) and fast during off-peak.

Demand-response participation

Grid operator signals a demand event. The eMSP receives the signal (via grid services API). The eMSP pushes TxProfiles to all its active sessions, throttling them during the event.

User sees: “Your charging is reduced for 30 minutes to support the grid, and you will receive a bill credit.”

Solar self-consumption

Home solar inverter signals current generation. CSMS adjusts charging profile to match generation. Charger draws more when solar is high, less when low.

The OCPP 1.6 vs 2.0.1 difference

Both versions support smart charging, but 2.0.1 is cleaner. For a broader look at what changed between versions, see the OCPP version comparison.

1.6:

  • An optional feature profile rather than core, which is why support varies so widely.
  • SetChargingProfile message.
  • Limited stacking support.
  • ChargingProfileKind: Absolute, Recurring, Relative.
  • ChargingRateUnit: A or W (consistent within a profile).

2.0.1:

  • Native part of the spec.
  • SetChargingProfileRequest / SetChargingProfileResponse.
  • Cleaner stacking with stackLevel field.
  • ChargingProfilePurpose enumeration matches the three purposes above.
  • Integration with the device model (smart charging variables exposed via Variables).
  • Better ISO 15118 (coming soon) integration for negotiation between vehicle and charger.

If you’re building smart-charging logic today, OCPP 2.0.1 is the right target. OCPP 1.6 smart charging is workable but bumpier.

Common pitfalls

A list of things that go wrong.

Charger claims smart-charging support but ignores profiles. SetChargingProfile returns Accepted and the actual power draw never changes. Accepted means the charger parsed and stored the profile, not that it will act on it, so the only proof is measuring delivered power against the limit you set.

Profile rejected because parameters don’t match the charger’s capabilities. A profile with 80A on a 32A-rated charger gets Rejected. Validate against the charger’s known capability before sending.

Schedule expires unexpectedly. A schedule with duration=3600 (one hour) runs out after an hour. The session continues but without the constraint. If you wanted ongoing limits, send a Recurring profile or extend.

Wrong unit (A vs W). Sending limit:11 with unit=W means 11 watts, not 11 kilowatts. The session crawls. Get the unit right.

Phase confusion. numberPhases matters for AC. A 16A limit on 3 phases is different from a 16A limit on 1 phase (3x different total power). Match phases to the actual installation.

Stacking surprises. Multiple profiles apply but the operator only checked one. Active limit is unexpectedly low. Implement a “show me what’s actually active” query (CompositeSchedule in OCPP) to debug.

Profile collisions. Two parties (CSMS and a local controller) both push profiles. They conflict. Behavior is implementation-defined. Coordinate or pick one source of truth.

What CSMS implementers should do

If you’re building smart-charging logic in a CSMS:

Treat smart-charging support as a per-charger capability. Query it, store it against the model and firmware version, and re-check it after firmware updates. It is a property of a specific build, not of a product line.

Validate profiles before sending. Reject malformed or impossible profiles in your own code; don’t let them go to the charger and come back as Rejected.

Test against the actual hardware. Send a profile; verify (via MeterValues or via observing the actual energy flow) that the charger honors it. Many discrepancies emerge here.

Implement CompositeSchedule queries. Lets you ask the charger “what’s actually active right now after stacking?” Essential for debugging.

Coordinate with local DLM. If a site has both CSMS-pushed profiles AND a local DLM controller, define which wins. Document.

Audit smart-charging behavior in production. Periodic checks that profiles you’ve pushed are still in effect; if they’ve been cleared or replaced unexpectedly, investigate.

What charger firmware should do

For OEMs building charger firmware:

Implement profiles correctly. Apply the limits, respect the stacking, honour the schedule. Since Accepted is cheap to return and correct behaviour is not, this is a place where a firmware team can be measurably better than the datasheet average.

Report capabilities accurately. Don’t claim ChargingProfileMaxStackLevel:8 if you actually only support 3.

Handle profile updates gracefully. A profile update mid-session should take effect smoothly without disrupting the session.

Honor units. A and W are different. Implementations that confuse them are a frequent bug source.

Support CompositeSchedule queries. Lets operators verify what’s actually active.

Handle profile expiration. When a schedule ends, fall back gracefully to the next applicable profile or the default behavior.

The honest summary

OCPP smart charging is a real and powerful capability when both the CSMS and the charger implement it well. The protocol mechanism (SetChargingProfile, ChargingSchedule, stacking) is well-designed and supports a wide range of use cases. The gap between “supported in the spec” and “works in production” is where the difficulty lives. Implementations vary, edge cases are common, and debugging is genuinely hard because the thing you want to inspect, the effective limit, is computed inside the charger.

That last point is the one to design around. GetCompositeSchedule exists precisely because you cannot infer the applied limit from the profiles you sent, and a smart-charging deployment without it is a deployment where you find out about stacking mistakes from an energy bill. Treat charger-level smart-charging support as a per-vendor capability to be tested rather than a checkbox to be trusted, and build the observability before you build the optimization.

Quick check

Q1. What OCPP message does a CSMS use to push a charging profile to a charger?
Q2. A schedule sends limit:11 with chargingRateUnit set to W. What happens?
Q3. Which query lets an operator ask a charger what limit is actually active after stacking?
Q4. How does OCPP 2.0.1 differ from 1.6 for smart charging?
Q5. Why should a CSMS treat smart-charging support as a per-charger capability?

Frequently asked questions

Does every OCPP charger support smart charging?

No. OCPP 1.6 made smart charging optional, and many older chargers don't implement it or implement it incompletely. OCPP 2.0.1 makes it more central but still optional. When evaluating chargers, ask specifically about ChargingProfile support and test it.

What is the difference between charging profile and charging schedule?

A charging profile is the overall instruction (purpose, stack level, validity period). A charging schedule is the actual power-over-time data inside the profile (e.g. '100 amps for the next hour, then 200 amps'). One profile contains one schedule.

What are the three profile purposes?

ChargePointMaxProfile (always applies, the absolute max for the charger), TxDefaultProfile (default for any transaction), TxProfile (for a specific transaction). They stack, and the actual limit is the minimum of all applicable profiles.

How do I push a profile to a charger?

The CSMS sends a SetChargingProfile message via OCPP, specifying the profile and the connector it applies to. The charger applies the profile and sends a response indicating Accepted or Rejected. Charge proceeds within the profile's constraints.

Found this useful? Share it.