We build Next.js, Shopify, Laravel, Flutter, in Noida, India. Free 1-page audit, no obligation.
Get a free quote- .NET Does Not Need Windows Any More: What It Actually Costs to Run in 2026September 19, 2026
- The javax to jakarta Break: Why Your Old Java App Is Stuck, and What It Costs to MoveSeptember 19, 2026
- What to Send a QA Team, and What You Should Get BackSeptember 19, 2026
- Inheriting a Laravel App: The Seven Things We Open on Day OneSeptember 18, 2026
OCPP 1.6-J Source Code: Five Open-Source Codebases Worth Reading Before You Write a CSMS
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 CSMSFrequently asked questions
Is there an open-source OCPP 1.6-J central system I can run in production?
Does SteVe support OCPP 2.0.1?
Which library should I use to build an OCPP charge point simulator?
Can I use SteVe or OCPP.Core in a commercial charging network?
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.
How this worked in practice
Working with us in your city
Keep Reading
OCPP 1.6-J by Example: The Eight Messages a CSMS Has to Get Right, With Real Payloads
The exact JSON a charger sends and a CSMS must answer, message by message, with the fields that matter for billing and the mistakes we made implementing each one on PlugEV.
EV Charging Software Companies in India (2026)
There are ~14 EV charging software (CSMS) companies in India in 2026. We built PlugEV and audited 9. The honest comparison: who's good, what it costs in INR.
Building an OCPP CSMS: What Worked, What Broke
Honest breakdown of building a production OCPP 1.6-J CSMS in India: message flow, transaction lifecycle, vendor quirks, and parts of the spec the docs gloss over.
OCPP 2.0.1 Migration Playbook for Indian Operators: When to Move, What Breaks, What's Worth It
When does an Indian EV operator on OCPP 1.6-J really need 2.0.1? Honest cost/benefit, ISO 15118 plug-and-charge, and a migration path that won't freeze your fleet.
The javax to jakarta Break: Why Your Old Java App Is Stuck, and What It Costs to Move
One package rename in 2020 split the Java ecosystem in two, and it is the reason most legacy Java applications cannot be upgraded a version at a time. What the break is, where your app sits, and the four honest ways forward.