Central system and charge point emulator for OCPP 1.5 (SOAP), 1.6 and 2.0.1 (JSON/WebSocket) in C++20.
Built on A-POST-OL — a high-performance C++20 framework with a single epoll event loop for HTTP, WebSocket, and PostgreSQL.
One command:
curl -fsSL https://raw.githubusercontent.com/apostoldevel/ocpp-cs/master/install.sh | bashOr manually:
git clone --recursive https://github.com/apostoldevel/ocpp-cs.git
cd ocpp-cs
docker compose upOpen in your browser:
- http://localhost:9220 — Web UI (no login required)
- http://localhost:9220/docs/ — Swagger UI (REST API)
Connect your charging station — set the Central System URL in your station's configuration:
ws://YOUR_SERVER_IP:9220/ocpp/YOUR_STATION_ID
The station will appear in the Web UI automatically after it connects. The protocol version (1.6 or 2.0.1) is determined by the Sec-WebSocket-Protocol header sent by the station.
That's it. The container runs a fully functional Central System with a built-in charge point emulator — no database, no external services, no configuration needed.
| Feature | Details |
|---|---|
| Central System | OCPP 1.5 (SOAP/HTTP), 1.6 and 2.0.1 (JSON/WebSocket) |
| Schema Validation | All incoming messages validated against official OCPP JSON Schemas |
| Charge Point Emulator | Built-in OCPP 1.6 and 2.0.1 stations, auto-connect on startup |
| Web UI | Dashboard, station management, OCPP commands, live message log |
| REST API | OpenAPI spec + Swagger UI at /docs/ |
| One-command install | curl | bash — Docker setup in 30 seconds |
| Integration | Webhook or PostgreSQL — your choice |
The Central System supports 22 OCPP 2.0.1 messages — full charging session lifecycle, device management, provisioning, and availability:
| Direction | Messages |
|---|---|
| CP → CSMS (9) | BootNotification, Heartbeat, StatusNotification, Authorize, TransactionEvent, MeterValues, NotifyReport, FirmwareStatusNotification, DataTransfer |
| CSMS → CP (12) | RequestStartTransaction, RequestStopTransaction, Reset, SetVariables, GetVariables, ChangeAvailability, UnlockConnector, TriggerMessage, ClearCache, GetBaseReport, GetReport, GetTransactionStatus |
| Both (1) | DataTransfer |
Key differences from OCPP 1.6:
- 3-tier model — Station → EVSE → Connector (instead of flat connector list)
- TransactionEvent — single message replaces StartTransaction, StopTransaction, and MeterValues
- Device Model — SetVariables/GetVariables replace ChangeConfiguration/GetConfiguration
- Schema validation — all messages validated against official OCPP 2.0.1 JSON Schemas
- Spec-compliant details — correct triggerReason→stoppedReason mapping, connectorId validation, remoteStartId tracking, scheduled availability changes
The built-in emulator includes 5 OCPP 2.0.1 stations with different configurations:
| Station | Model | EVSEs | Connectors |
|---|---|---|---|
| CP201 | Virtual-201 | 2 | CCS2, CCS1 |
| CP202 | SingleDC-201 | 1 | CCS1 |
| CP203 | TripleDC-201 | 3 | CCS2, CCS1, ChaoJi |
| CP204 | DualCable-201 | 2 | 2 per EVSE (CCS2+CCS1, CCS2+ChaoJi) |
| CP205 | QuadAC-201 | 4 | Type2, Type2, Type1, Type1 |
Connect your charge point to the demo server:
| Protocol | Address |
|---|---|
| WebSocket (OCPP 1.6) | ws://ws.ocpp-css.com/ocpp |
| SOAP (OCPP 1.5) | http://soap.ocpp-css.com/Ocpp |
Web UI: http://cs.ocpp-css.com (login: demo / demo, RFID: demo)
docker pull apostoldevel/cs
docker run -p 9220:9220 --rm --name cs apostoldevel/csgit clone --recursive https://github.com/apostoldevel/ocpp-cs.git && cd ocpp-cs
docker compose up| Variable | Description |
|---|---|
WEBHOOK_URL |
Webhook endpoint URL (enables webhook mode) |
WEBHOOK_AUTH |
Auth scheme: Basic, Bearer, or Off (default) |
WEBHOOK_USERNAME |
Username for Basic auth |
WEBHOOK_PASSWORD |
Password for Basic auth |
WEBHOOK_TOKEN |
Token for Bearer auth |
Example with webhook:
docker run -p 9220:9220 --rm --name cs \
-e WEBHOOK_URL=http://your-server/api/v1/ocpp/parse \
-e WEBHOOK_AUTH=Basic \
-e WEBHOOK_USERNAME=ocpp \
-e WEBHOOK_PASSWORD=ocpp \
apostoldevel/csBefore building, you can edit:
| File | Purpose |
|---|---|
docker/conf/default.json |
Server settings, webhook endpoint |
docker/www/config.js |
Web UI server address |
docker/conf/sites/default.json |
Allowed hostnames |
Example sites/default.json for your server:
{
"hosts": ["cs.example.com", "cs.example.com:9220", "192.168.1.100:9220", "localhost:9220"]
}- C++20 compiler: GCC 12+ or Clang 16+
- CMake 3.25+
libssl-dev,zlib1g-devlibpq-dev(optional, only withWITH_POSTGRESQL)
sudo apt-get install build-essential libssl-dev zlib1g-dev make cmake gcc g++git clone --recursive https://github.com/apostoldevel/ocpp-cs.git && cd ocpp-cs
./configure # release build
cmake --build cmake-build-release --parallel $(nproc)
sudo cmake --install cmake-build-release./configure --debug
cmake --build cmake-build-debug --parallel $(nproc)
mkdir -p logs
./cmake-build-debug/cs -p . -c conf/default.json| Option | Default | Description |
|---|---|---|
INSTALL_AS_ROOT |
ON | Install to system dirs (/usr/sbin/, /etc/cs/) |
WITH_POSTGRESQL |
ON | PostgreSQL integration. Disable for standalone mode |
WITH_SSL |
ON | TLS, JWT, OAuth 2.0 |
Standalone build (no database):
./configure --release --without-postgresql --without-sslThe simplest integration method. Configure in conf/default.json:
{
"webhook": {
"enable": true,
"url": "http://your-server/api/v1/ocpp/parse",
"authorization": "Basic",
"username": "ocpp",
"password": "ocpp"
}
}The Central System forwards all charge point-initiated messages (Authorize, BootNotification, StartTransaction, StopTransaction, TransactionEvent, etc.) to your endpoint as JSON:
{
"identity": "EM-A0000001",
"uniqueId": "25cf07c9ae20a0566d1043587b5790a6",
"action": "BootNotification",
"payload": {
"firmwareVersion": "1.0.0.1",
"chargePointModel": "CP_EM",
"chargePointVendor": "Apostol",
"chargePointSerialNumber": "202206040001"
},
"account": "AC0001"
}Your server responds in the same format with the OCPP-compliant payload:
{
"identity": "EM-A0000001",
"uniqueId": "25cf07c9ae20a0566d1043587b5790a6",
"action": "BootNotification",
"payload": {
"status": "Accepted",
"interval": 600,
"currentTime": "2024-10-22T23:08:58.205Z"
}
}Fields:
identity— charge point identifieruniqueId— request IDaction— OCPP action namepayload— OCPP dataaccount— optional user account (extracted from the connection URL:ws://host/ocpp/EM-A0000001/AC0001)
For direct database integration, create the ocpp schema with these functions:
Core:
ocpp.Parse(pIdentity, pUniqueId, pAction, pPayload, pAccount, pVersion)— unified dispatcher for 1.6 and 2.0.1ocpp.ParseXML— SOAP 1.5 message parserocpp.ChargePointList,ocpp.TransactionList,ocpp.ReservationListocpp.JSONToSOAP,ocpp.SOAPToJSON
OCPP 2.0.1 (called from ocpp.Parse when pVersion = '2.0.1'):
ocpp.BootNotification201— registers station withocpp_versionocpp.Authorize201— nestedidToken.idTokenextractionocpp.StatusNotification201— withevse_id,connector_statusocpp.TransactionEvent— Started/Updated/Ended via single functionocpp.MeterValues201— withevse_idocpp.NotifyReport— device model report logging
The Central System calls these functions during charge point communication, passing data in JSON format. All business logic is implemented in PL/pgSQL. The pVersion parameter (default '1.6') enables version-specific handling within ocpp.Parse.
The built-in emulator creates virtual charge points for development and testing.
Emulator configs are in conf/cp/ — each subfolder contains a configuration.json for one emulated station.
Default configs create 4 stations (CP1–CP4) with flat connector lists. Configuration uses ConnectorIds array.
The CP201 station demonstrates the 3-tier model with EVSEs and connectors:
{
"OcppVersion": "2.0.1",
"ChargePointVendor": "Emulator",
"ChargePointModel": "Virtual-201",
"Evses": [
{"evseId": 1, "connectors": [{"connectorId": 1, "type": "cCCS2"}]},
{"evseId": 2, "connectors": [{"connectorId": 1, "type": "cCCS1"}]}
]
}Set "OcppVersion": "2.0.1" to use the OCPP 2.0.1 protocol. If absent, defaults to "1.6".
Enable in conf/default.json:
{
"module": {
"ChargePoint": {"enable": true}
}
}Emulator-only mode (disable Central System):
{
"process": {
"master": false
}
}sudo systemctl start cs
sudo systemctl stop cs
sudo systemctl status cs| Signal | Action |
|---|---|
| TERM, INT | fast shutdown |
| QUIT | graceful shutdown |
| HUP | reload configuration, start new workers |
| WINCH | graceful worker shutdown |
