Back to Blog
OCPPMigrationOCPP 2.0.1

Migrating from OCPP 1.6 to 2.0.1: What You Need to Know

EVSEBridge TeamFebruary 20, 20269 min read

Why Migrate at All?

OCPP 1.6 has served the industry well for over a decade. It is stable, widely implemented, and handles the vast majority of charging workflows. So why invest engineering time in migrating to 2.0.1?

The short answer: the industry is moving, and your platform needs to move with it. Major charger manufacturers including ABB, Alfen, Wallbox, and Kempower are shipping 2.0.1-native firmware. Regulatory frameworks in the EU and certain US states are beginning to reference 2.0.1 capabilities as baseline requirements. And features like ISO 15118 Plug & Charge, advanced device monitoring, and granular security profiles simply do not exist in 1.6.

The longer answer involves understanding exactly what changed between the two versions and planning a migration that does not disrupt your existing charger fleet.

Key Differences Between 1.6 and 2.0.1

Transaction Handling

This is the most significant architectural change. In OCPP 1.6, a transaction is a simple start/stop pair tied to a connector. You receive StartTransaction, assign a transaction ID, collect MeterValues, and eventually receive StopTransaction. The transaction ID is an integer assigned by the CSMS.

In OCPP 2.0.1, transactions are event-driven. The charger sends TransactionEvent messages with an eventType of Started, Updated, or Ended. The transaction ID is a string generated by the charger, not the CSMS. Meter values are embedded directly in TransactionEvent messages rather than sent as separate MeterValues requests (though standalone MeterValues is still supported for non-transactional metering).

This means your transaction state machine needs a complete rewrite. You can no longer assume a clean start/stop lifecycle. A TransactionEvent with eventType: Updated might arrive carrying an offline flag, indicating the charger queued this event while disconnected and is now replaying it. Your CSMS must handle out-of-order and duplicate events gracefully.

The Device Model

OCPP 1.6 uses a flat key-value store for charger configuration. You call GetConfiguration to retrieve a list of keys like HeartbeatInterval, MeterValueSampleInterval, and AuthorizeRemoteTxRequests. Changing a value means sending ChangeConfiguration with the key and new value.

OCPP 2.0.1 replaces this with a hierarchical device model organized into Components and Variables. A Component represents a logical or physical part of the charger (e.g., EVSE, Connector, ChargingStation, SecurityCtrlr). Each Component has Variables with attributes like Actual, Target, MinSet, and MaxSet.

For example, instead of GetConfiguration(key: "HeartbeatInterval"), you now use GetVariables with:

json
{
  "getVariableData": [
    {
      "component": { "name": "ChargingStation" },
      "variable": { "name": "HeartbeatInterval" },
      "attributeType": "Actual"
    }
  ]
}

This is more verbose, but it is also far more expressive. You can query the minimum and maximum allowed values, set targets that the charger will try to reach, and discover components dynamically through GetBaseReport.

Security Profiles

OCPP 1.6 has minimal built-in security. Most deployments rely on basic authentication (a charger ID and password in the WebSocket URL) and optional TLS. Security was an afterthought bolted on through the 1.6 Security Whitepaper.

OCPP 2.0.1 defines three security profiles as first-class protocol features:

  1. Profile 1: Unsecured transport with basic HTTP authentication. Not recommended for production.
  2. Profile 2: TLS with server-side certificates. The charger validates the CSMS certificate.
  3. Profile 3: TLS with mutual authentication. Both the charger and CSMS present certificates. This is the recommended profile for production deployments.

The protocol also includes SignCertificate and CertificateSigned messages for automated certificate lifecycle management. Your CSMS can act as a sub-CA or proxy certificate signing requests to an external CA.

EVSE and Connector Separation

In OCPP 1.6, a connector is the primary addressable unit. Connector ID 1 on a charger is referenced directly in transactions and status notifications.

OCPP 2.0.1 introduces an explicit EVSE layer between the charging station and its connectors. An EVSE (Electric Vehicle Supply Equipment) represents a single charging point that may have multiple connectors (e.g., a dual-cable charger with both CCS and CHAdeMO). A transaction is associated with an EVSE, not a connector. Status notifications report both EVSE and connector state independently.

This distinction matters for chargers with shared power modules. A charger might have two EVSEs that dynamically share a 150 kW power module. In 1.6, there is no clean way to model this. In 2.0.1, the device model exposes power allocation per EVSE, and the CSMS can set charging profiles at the EVSE level to control distribution.

ISO 15118 and Plug & Charge

OCPP 2.0.1 adds native support for ISO 15118, the protocol that enables Plug & Charge -- a workflow where the vehicle authenticates itself to the charger via a TLS certificate exchange over the charging cable, eliminating the need for RFID cards or mobile apps.

Your CSMS participates in this flow through the Authorize message, which now includes an idToken of type eMAID (e-Mobility Account Identifier). The CSMS validates the eMAID against a contract certificate pool and authorizes the session. Certificate installation and updates are handled through dedicated OCPP messages (Get15118EVCertificate, DeleteCertificate, InstallCertificate).

Step-by-Step Migration Strategy

Step 1: Run Both Versions in Parallel

Do not attempt a big-bang migration. Your existing 1.6 chargers will continue running 1.6 for years -- many will never receive a firmware update to 2.0.1. Your CSMS must support both versions simultaneously.

Design your WebSocket server to detect the OCPP version from the Sec-WebSocket-Protocol header. OCPP 1.6 chargers request ocpp1.6, while 2.0.1 chargers request ocpp2.0.1. Route incoming connections to the appropriate message handler based on this header.

Step 2: Implement a Protocol Abstraction Layer

Create an internal domain model that is version-agnostic. Your business logic should operate on concepts like "a charging session started on EVSE 1" rather than "a StartTransaction message arrived" or "a TransactionEvent with eventType: Started arrived."

Map both OCPP 1.6 and 2.0.1 messages into this unified domain model at the protocol boundary. This keeps your business logic clean and avoids duplicating authorization rules, billing calculations, and reporting queries for each OCPP version.

Step 3: Migrate the Device Model

Build a component-variable registry that stores the 2.0.1 device model. For 1.6 chargers, synthesize a virtual device model by mapping known configuration keys to their 2.0.1 equivalents. For example, map the 1.6 key HeartbeatInterval to Component ChargingStation, Variable HeartbeatInterval. This allows your management UI to present a unified configuration interface regardless of the charger's OCPP version.

Step 4: Implement Security Profile 3

If you are not already terminating TLS on your WebSocket endpoint, now is the time. Generate a CA certificate for your CSMS, configure mutual TLS, and implement the SignCertificate flow so that chargers can request and rotate their client certificates automatically. This is a hard requirement for many enterprise and utility customers.

Step 5: Test with Real Hardware

The OCPP 2.0.1 specification is large (over 400 pages across three documents), and charger implementations vary in their interpretation of edge cases. Test against at least three different charger vendors before going to production. The OCA maintains a compliance testing tool, but real-world interoperability testing is irreplaceable.

Step 6: Gradual Rollout

Start by onboarding new chargers on 2.0.1 while keeping existing chargers on 1.6. Monitor for issues, tune your protocol abstraction layer, and expand the 2.0.1 fleet gradually. Set a timeline for encouraging (not forcing) firmware upgrades on chargers that support both versions.

What EVSEBridge Gives You

Implementing dual-version OCPP support, a protocol abstraction layer, certificate management, and the 2.0.1 device model is easily six to twelve months of dedicated engineering work. EVSEBridge ships all of this out of the box.

Our gateway handles OCPP 1.5, 1.6, and 2.0.1 connections on the same endpoint. Protocol translation happens transparently -- your API consumers see a unified data model regardless of which OCPP version the charger speaks. Security Profile 3 with automated certificate rotation is built in. And the device model registry supports both native 2.0.1 components and synthesized models for legacy chargers.

Conclusion

Migrating from OCPP 1.6 to 2.0.1 is not a weekend project. The protocol changes are deep and structural, touching transaction handling, device configuration, security, and charger topology. But the benefits -- Plug & Charge support, proper security, granular monitoring, and future-proofing your platform -- make the investment worthwhile.

The key is to approach the migration incrementally: run both versions in parallel, abstract the protocol at your domain boundary, and test relentlessly against real hardware. Or skip the infrastructure work entirely and let EVSEBridge handle the protocol layer while you focus on building the features your customers actually care about.

EVSEBridge Team

Writing about OCPP, EV charging infrastructure, and distributed systems at EVSEBridge.