DRAFT — not indexed by search engines. Visible only via direct URL or the admin page.

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 the mechanism through which a CSMS controls how much power a charger delivers — when, to which connector, at what rate. 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’s also one of the most under-implemented OCPP features in the field. Many chargers technically claim support but have buggy or incomplete implementations. Many CSMSes can push profiles but don’t really test them. Real production smart charging deployments often discover that “OCPP says we support smart charging” doesn’t quite mean “smart charging actually works as expected.”

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 multiple profiles apply (e.g. one defaults profile + one transaction-specific profile), they stack — the actual delivered power is the minimum of all applicable profile 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: per-session smart charging — applied when a session starts based on the eMSP’s request, the user’s preferences, or the 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.

This stacking is powerful — multiple stakeholders (site operator, eMSP, user) can each push constraints without conflict resolution required at the protocol level. The minimum rule resolves it deterministically.

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: each gets 50 kW (well above charger rating — full 11 kW each). 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; you’ll receive a $5 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 (coming soon).

1.6:

  • Optional extension; many chargers don’t fully implement.
  • 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. Common in older or budget chargers. The SetChargingProfile returns Accepted but the actual charging behavior doesn’t change. Test, don’t trust the spec sheet.

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. Don’t assume; query and store. Some chargers support it well, some poorly, some not at all.

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. Honor the schedule. Many chargers fall short here; you can differentiate by doing it right.

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 spec” and “works in production” is significant — implementations vary, edge cases are common, debugging is hard. Test rigorously; treat charger-level smart-charging support as a per-vendor capability rather than a universal given; and build observability so you know what’s actually happening in production. Done well, OCPP smart charging is the foundation for grid-integrated, cost-optimized, user-friendly EV charging.

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 — 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.