Skip to content
Open-source OCPP 1.6-J central systems and libraries, comparedMobility
Mobility

OCPP 1.6-J Source Code: Five Open-Source Codebases Worth Reading Before You Write a CSMS

RRRavi Rai··8 min read

Two kinds of people search for OCPP 1.6-J source code. One wants to read a working implementation before writing their own. The other is hoping not to write one at all. This post is for the first, and honest with the second. Below are the five codebases we would put in front of an engineer starting on a charging management system today, with the facts that decide whether you can use them checked against each repository this week rather than repeated from memory: what each one is, what licence it carries, which OCPP versions it actually implements, and the half of the product that none of them contain.

We have skin in this. PlugEV, the charging platform we built and operate, runs on an OCPP 1.6-J gateway we wrote in Go, with billing and operations in Laravel. We did not deploy any of the five. The last section says why, and why that is not the right answer for everyone.

Two kinds of source, and the mistake of confusing them

Open-source OCPP code comes in two shapes and they solve different problems. A protocol library parses, validates and serialises OCPP messages and hands you a place to put handlers. Everything else is yours to build: the WebSocket server, the database, the operator screens, the billing. That is the point of a library. OCPP becomes a dependency inside your product rather than the shape of it. A full central system is the opposite: a server you deploy, with a schema and an admin interface already decided, which you configure and extend.

Which one you want follows from a single question. Is OCPP your product, or your plumbing? If you sell charging software, you want a library, because a full system's data model will be fighting yours within a month. If you operate chargers and want something running, you want a full system, and you want to know before you deploy it what it does not do.

The five, with the facts checked

Each of these is described from its own repository as it reads this week. Versions and licences change, so if you are deciding on one, read the repository on the day.

  • SteVe (Java, Spring Boot, GPL-3.0). The longest-established full central system in the open, and the one most people mean when they say open-source CSMS. Its README lists OCPP 1.2, 1.5 and 1.6 in both SOAP and JSON, including the 1.6 security extensions, and nothing above 1.6. It requires JDK 25 or newer and MySQL or MariaDB. If you want to see what a complete central system looks like end to end, from the WebSocket endpoint to the operator screens, this is the codebase to read first. The licence is the thing to read second.
  • OCPP.Core (C#, .NET, GPL-3.0). A smaller full server with a management web UI for charge points and RFID tokens, on Entity Framework Core with database scripts for SQL Server and SQLite. Its README lists OCPP 1.6J, 2.0 and 2.1. The most readable of the three full systems if you already think in .NET, and small enough to understand in a day.
  • CitrineOS (TypeScript, Apache-2.0). Initiated by S44 and contributed to Linux Foundation Energy in 2024, it is the most modern architecture of the five: a modular server runtime with OCPP 1.6 and 2.0.1 message validation and an OCPP 2.0.1 certification for the core and advanced security profiles. It is also the heaviest to run. PostgreSQL with PostGIS, RabbitMQ and S3-compatible storage are prerequisites before a single charger connects. That is a fair price for a network of hundreds and a real cost for a pilot of six.
  • ocpp, from The Mobility House (Python, MIT). A protocol library, not a server. It implements OCPP 1.6 to errata v4 and OCPP 2.0.1 to the 2022 edition with the 2024 errata, and it is the clearest source there is for learning what each message looks like on the wire. You can have a charge point simulator running against your own server in an afternoon with it, and that simulator will find bugs that no amount of reading the specification will.
  • ocpp-go (Go, MIT). The Go equivalent, a library with OCPP 1.6 and the security extension fully supported and, in its own README's words, 2.0.1 "examples working, but will need more real-world testing". That sentence is the kind of honesty you want from a dependency, and it tells you exactly which version to build on today.

One more that is not a CSMS at all but turns up in the same searches. EVerest (Apache-2.0, Linux Foundation Energy, started by PIONIX) is the other end of the wire: a framework for the software that runs on the charger itself, with OCPP 1.6, 2.0.1 and 2.1 modules. If what you actually need is a real charge point to test against rather than a simulator, it is where to look.

What the library route looks like in practice

This is the central-system example from the Python library's own repository, trimmed to the part that matters. It is the whole of a server that accepts a charger, checks that it asked for the OCPP 1.6 subprotocol, and answers its BootNotification:

pythonimport asyncio
import logging
from datetime import datetime, timezone

import websockets

from ocpp.charge_point import extract_charge_point_id
from ocpp.routing import on
from ocpp.v16 import ChargePoint as cp
from ocpp.v16 import call_result
from ocpp.v16.enums import Action, RegistrationStatus


class ChargePoint(cp):
    @on(Action.boot_notification)
    def on_boot_notification(self, charge_point_vendor: str, charge_point_model: str, **kwargs):
        return call_result.BootNotification(
            current_time=datetime.now(timezone.utc).isoformat(),
            interval=10,
            status=RegistrationStatus.accepted,
        )


async def on_connect(websocket):
    # A charger that did not ask for ocpp1.6 in the handshake is closed, not tolerated.
    if not websocket.subprotocol:
        logging.warning("no matching subprotocol, closing")
        return await websocket.close()

    charge_point_id = extract_charge_point_id(websocket.request.path)
    if not charge_point_id:
        return await websocket.close()

    await ChargePoint(charge_point_id, websocket).start()


async def main():
    server = await websockets.serve(on_connect, "0.0.0.0", 9000, subprotocols=["ocpp1.6"])
    await server.wait_closed()


asyncio.run(main())

Two things in those forty lines are the ones that bite in production. The subprotocol check: a charger that does not ask for ocpp1.6 in the WebSocket handshake gets closed, because proceeding without one is how you end up parsing a 2.0.1 charger's messages against a 1.6 schema. And the charge point identity coming from the URL path, which is fine for a demo and is not authentication. A production gateway checks a credential before it lets that identity claim anything. The rest of a CSMS is what you add on top: the eight messages a 1.6-J server has to get right, the transaction lifecycle, and the screens an operator lives in. Note the identifiers too. Older tutorials show Action.BootNotification and call_result.BootNotificationPayload; the library renamed both, and code copied from a 2022 blog post will not import.

The half of the product that none of them contain

Every codebase above stops at the same line, and in India that line is where most of the work begins. None of them know anything about:

  • Collecting money the way Indian drivers pay. UPI, whether by QR at the charger or from an app, through a gateway such as Razorpay or Cashfree, with the webhook handling that makes a captured payment and a StopTransaction agree with each other.
  • Tariffs the way Indian operators set them. Per kWh and per minute, time-of-day rates, idle fees, and the reconciliation between the meter reading in StopTransaction and the amount actually charged. The tariff and billing side is written up separately.
  • GST-compliant invoicing for every session, and the difference between billing energy and billing a service.
  • Notifications through channels that work here. DLT-registered SMS templates and WhatsApp, rather than the email-first assumptions of a European codebase.
  • Authorization for a public network. RFID is what the specification assumes. An Indian public charger is far more often started from an app or a UPI payment, which is an Authorize flow the protocol supports and no open-source project ships.

This is not a criticism of the projects. It is the reason the question "which open-source CSMS should we use" is smaller than it sounds. Whichever one you pick, the Indian half is yours, and it is the half that decides whether the network makes money.

Licences, in plain words

Three of the five are permissive: MIT for the two libraries, Apache-2.0 for CitrineOS, which adds an explicit patent grant, and the same for EVerest. Two are GPL-3.0, SteVe and OCPP.Core. The GPL governs what you must share when you distribute modified copies. Whether running a modified copy as a hosted service counts as distribution is a question with a long history and a lawyer's answer, not a blog post's. If your business plan is a white-labelled SteVe sold to operators, get that answer before the first line of code rather than after the first customer.

What we did on PlugEV, and why

PlugEV's OCPP gateway is our own Go service. Everything that is not OCPP, which is tariffs, UPI collection, GST invoices, WhatsApp and the operator dashboard, is Laravel on PostgreSQL and Redis. We made that trade for a reason that only holds if it holds for you: the OCPP surface we needed for 1.6-J is a small number of messages we had to get exactly right, and a gateway that does only that is one small binary with nothing in it we do not understand. The product, the part that changes every month, lives in the framework our team is fastest in. We also wrote our own charge point simulator in Go and could load ten thousand simulated chargers on a laptop, which caught most of the gateway's bugs before a real charger ever connected. The full account of what worked and what broke is here.

That is a build-the-plumbing decision by a team that builds software for a living. If you operate chargers and do not, it is the wrong trade, and the right one is below.

The honest rule

  • You operate chargers and want something running. Deploy a full system, SteVe or CitrineOS depending on how much infrastructure you can carry, and budget the Indian half as a real project on top rather than a configuration step.
  • OCPP is your product. Use a library and own the server and the data model. The Python library to learn on and to build your simulator; ocpp-go, or your own code, for the gateway you ship.
  • You are choosing between all of this and a SaaS. That decision turns on charger count and operator profile more than on code, and the shortcuts by operator profile are here.

Starting from one of these codebases, or from nothing, and want a second opinion on the trade before you commit a team to it? Tell us how many chargers, which OCPP version, and who is paying, and we will tell you what we would do.

Talk to us about your CSMS

Frequently asked questions

Is there an open-source OCPP 1.6-J central system I can run in production?
Yes. SteVe is the longest-established, implements OCPP 1.2 through 1.6 including the security extensions, and describes itself as a trusted open-source charging station management system. CitrineOS is newer, is certified for the OCPP 2.0.1 core and advanced security profiles, and supports 1.6 as well. Both are servers you deploy and maintain, and neither contains the billing, UPI, GST or notification code an Indian network needs.
Does SteVe support OCPP 2.0.1?
No. As its README reads today, SteVe supports OCPP 1.2, 1.5 and 1.6 in SOAP and JSON, with the 1.6 security extensions, and nothing above 1.6. For 2.0.1, look at CitrineOS, OCPP.Core, or the Python and Go libraries.
Which library should I use to build an OCPP charge point simulator?
The Python ocpp library from The Mobility House. It is MIT licensed, implements both 1.6 and 2.0.1, and its repository ships charge point and central system examples you can run in minutes. A simulator built on it will find bugs in your server that reading the specification never will.
Can I use SteVe or OCPP.Core in a commercial charging network?
Both are GPL-3.0. Running one to operate your own network is one question. Modifying one and providing the modified copy to customers, hosted or otherwise, is a different question with a legal answer that depends on how it is provided. Get that answer from a lawyer before building a business on either.
RR
Written by
Ravi Rai

Founder of buildbyravirai, a web development agency based in Noida, India. 5+ years shipping Next.js, WordPress, Shopify, and Laravel projects for clients in India, USA, Canada, and the UK.

EV charging software

We build the software behind EV charging networks

PlugEV is ours: OCPP charge point management, RFID and UPI billing, and a driver app, live on 40 plus chargers. If you are standing up a network, we have already solved most of what is about to go wrong.