
OCPP Is Open. That Does Not Mean Chargers Agree.

OCPP says a charger must accept a BootNotification. It says nothing about what the charger does when your backend sends a RemoteStartTransaction three seconds after that notification. Vendors fill that gap differently, and that gap is where EV charging platforms break.
What OCPP Actually Specifies (and What It Leaves Out)
OCPP 1.6 and OCPP 2.0.1 are messaging protocols. They define message names, field types, required vs. optional fields, and the expected sequence of certain handshakes. They do not define timing tolerances, retry semantics, TLS cipher requirements, WebSocket ping intervals, or how a charger should behave when it receives a message it wasn't expecting in its current state.
That last point is the one that causes the most grief.
The specification defines a state machine for transactions, but vendors implement it themselves. Two chargers can both be "OCPP 1.6 compliant" and handle the Unavailable to Available transition in completely different ways. One might queue incoming RemoteStartTransaction calls during a firmware update. Another will silently drop them.
OCPP 2.0.1 tightened several things. It introduced device management, better security profiles, and a proper certificate management flow. But it also added complexity, and many charger manufacturers shipped 2.0.1 support that is, in practice, 1.6 with a 2.0.1 header. The Open Charge Alliance runs a conformance testing programme, but passing that test does not mean the charger works correctly under real-world load or with your specific CSMS.
Why Does Every Charger Feel Like a New Integration?
Because it is.
The practical differences between charger vendors cluster into a few categories:
WebSocket behaviour. Some chargers drop the connection if they don't receive a ping within 30 seconds. Others ignore pings entirely. A few send malformed ping frames. Your CSMS needs to handle all three, which means you cannot rely on a single WebSocket library default.
Message ordering assumptions. A charger may assume that after sending a StatusNotification with status Preparing, the next message it will receive is a RemoteStartTransaction. If your CSMS sends an UnlockConnector first for any reason, some chargers enter an undefined state. You find this out in the field.
Optional field handling. OCPP marks many fields as optional. Some chargers treat an absent optional field as an error and close the connection. Others accept it and behave unexpectedly. The reservationId field in StartTransaction is a good example: half the chargers in the wild either ignore it or reject the message if it appears when they didn't initiate a reservation.
Timestamps. Chargers are supposed to sync to the CSMS clock via BootNotification response. Many don't. You will see transaction records with start times in 2019 because the charger's RTC lost power and reset.
| Behaviour | OCPP 1.6 Spec Says | Reality |
|---|---|---|
| Heartbeat interval | CSMS sets it in BootNotification response |
Many chargers hardcode their own interval |
| Meter values during transaction | Configurable via MeterValueSampleInterval |
Some chargers send fixed intervals regardless |
| Reconnect after disconnect | Not specified | Ranges from 5 s to 10 min across vendors |
| Firmware update status reporting | FirmwareStatusNotification sequence defined |
Vendors skip states or report them out of order |
| Connector status on boot | StatusNotification expected |
Some chargers send it, some wait for a poll |
/// Not sure where to start?
Get the architecture before you commit
Tell us what you're building and we'll map the technical approach, stack, and rough timeline. No cost, no obligation, no sales call required.
How Do You Build a CSMS That Actually Works Across Vendors?
You treat each charger model as its own integration target until proven otherwise.
The architecture that holds up under that assumption looks like this: a thin, standards-compliant OCPP layer handles message parsing, validation, and routing. Below that sits a per-model adapter layer that normalises charger-specific behaviour before it reaches your business logic. The adapter knows that a particular 7 kW AC unit from a specific vendor sends StopTransaction without a reason field and that you should default it to Local. Your billing engine never sees that quirk.
This is more work upfront. The alternative is cheaper upfront and expensive forever, because every time you onboard a new hardware SKU your billing, reservation, and load management code needs to change.
State Machine Discipline
Build your CSMS state machine explicitly, not implicitly. If you're using a framework that infers state from message arrival order, you will get burned. Represent connector state as an explicit enum, persist it, and write transition guards that reject illegal transitions rather than silently accepting them.
When a charger sends you a StatusNotification with status: Available while your records show an active transaction, you need to decide: is the charger wrong, or did you miss a StopTransaction? The answer changes depending on the charger model. Some chargers send Available before StopTransaction. Your state machine needs to handle both orderings.
Testing Infrastructure
You cannot catch vendor deviations in unit tests. You need a charger simulator that replays real traffic captures from the physical hardware. Tools like OCTT (the OCA's own test tool) cover conformance, not integration. Build or buy a simulator that lets you inject timing delays, drop messages, and replay sequences out of order. Run this against every new firmware version a vendor ships, because vendors break things in firmware updates.
What About OCPP 2.0.1's Security Profiles?
Security Profile 3 in OCPP 2.0.1 requires mutual TLS with client certificates managed via the CertificateManagement feature set. In practice, fewer than 30% of chargers shipped as of 2024 support Profile 3 correctly. Most support Profile 1 (HTTP Basic over TLS) or Profile 2 (client certificate, but without the management flow).
This matters if you're building a platform that needs to meet energy sector compliance requirements or is handling billing data under PCI-adjacent expectations. Plan your certificate rotation strategy before you onboard hardware, not after. Rotating a root CA across a fleet of chargers that don't support InstallCertificate correctly is a painful manual process.
When Should You Not Build Your Own CSMS?
If you're running fewer than 500 charge points across a small number of hardware SKUs, the build-vs-buy calculation usually favours a commercial CSMS. The integration work is real, and unless differentiated charging logic is your business, you're paying to solve a problem that existing platforms have already absorbed.
The case for building your own is clear when you need deep integration with your own energy management system, when you're doing dynamic load balancing across a site with variable generation, or when your pricing and reservation logic is complex enough that adapting it to a third-party CSMS API costs more than owning the stack.
The honest answer is that most teams underestimate both sides: they underestimate how much work the OCPP integration layer actually is, and they underestimate how constraining a commercial CSMS becomes once your use case diverges from the standard charger-network model.
Conclusion
OCPP gives you a shared vocabulary. It does not give you a working integration. The gap between spec and hardware is where most EV platform projects actually get stuck, and closing that gap requires per-vendor testing, an explicit adapter architecture, and a state machine you've thought through before the first charger connects.
If you're at the point of evaluating your CSMS architecture or you've hit a specific vendor integration problem, talk to the team at Sodio. We've worked across the stack from protocol handling to billing and can give you a direct read on where your current approach is likely to break.
FAQ
Is OCPP 2.0.1 backward compatible with OCPP 1.6? No. They use different message formats and field structures. Some CSMS platforms support both versions simultaneously by negotiating the protocol version during the WebSocket handshake. Chargers typically support one version only, set at firmware level. You cannot mix versions on a single connection.
Why do chargers from the same manufacturer behave differently?
Firmware versions, regional variants, and hardware generations all produce different behaviour, even within a single product line. A charger running firmware 2.1.3 from a vendor may handle ReserveNow correctly, while firmware 2.0.8 from the same vendor silently ignores it. Always version-pin your test matrix.
What is the OCA conformance test, and is it enough? The Open Charge Alliance's OCTT tool validates that a charger sends correctly formed messages and handles standard sequences. It does not test timing edge cases, load behaviour, or how the charger responds to unusual but valid CSMS inputs. Passing conformance is a floor, not a ceiling.
How do you handle meter value drift across chargers? Most platforms timestamp meter values at receipt and cross-reference against the charger's reported timestamp. When the delta exceeds a threshold (typically 60 seconds), you flag the record and use the CSMS receipt time for billing. Some platforms also normalise units, since chargers report Wh, kWh, or W depending on vendor configuration.
Does OCPP handle load balancing across multiple chargers?
OCPP 2.0.1 includes a SmartCharging feature set that covers per-connector and per-site power profiles. Implementation quality varies widely. For serious load management across a mixed-hardware site, most teams build the optimisation logic in the CSMS and push the result as charging profiles, rather than relying on charger-side intelligence.
Have a project in mind? Contact Sodio Technologies to discuss your requirements and explore the right technology solution for your business.
/// Work with us
Talk to the engineers who'd build it
You'll get a technical scope, timeline and cost estimate from the people doing the work, not an account manager. In-house team, no subcontracting, since 2016.
