Skip to content
How to Build a CSMS: Architecture Guide for EV Charging Platforms

How to Build a CSMS: Architecture Guide for EV Charging Platforms

·12 min read
Share:

Building a CSMS (Charging Station Management System) from scratch is a significant engineering undertaking. It requires deep understanding of the OCPP protocol, WebSocket infrastructure, real-time data processing, and the operational realities of EV charging networks. But for companies where charging is a core product differentiator, building your own CSMS provides unmatched control over features, integrations, and competitive advantage.

This guide walks through the architecture, technology choices, implementation steps, and scaling considerations for building a production-grade CSMS.

Why Build a CSMS

Building your own CSMS makes sense in specific circumstances. It does not make sense for everyone.

Build When:

  • Charging is your primary product, not a supporting feature
  • You need deep integration with proprietary systems (custom billing, fleet management, energy trading)
  • Your business model requires features that no existing CSMS provides
  • You plan to operate at a scale where per-charger licensing costs exceed engineering investment
  • You need full control over data, security, and compliance

Buy When:

  • Charging supplements your core business (hospitality, retail, real estate)
  • You need to deploy within weeks, not months
  • Your team lacks OCPP protocol and WebSocket infrastructure expertise
  • You are managing fewer than 1,000 chargers

The break-even point varies, but most companies find that building becomes cost-effective between 2,000 and 10,000 chargers, depending on commercial CSMS pricing and required customization.

Architecture Overview

A production CSMS consists of several interconnected components. Each serves a distinct function, and the interfaces between them determine your system's reliability and extensibility.

Core Components

Component Responsibility Key Requirements
WebSocket Server Manage persistent connections with charge points High concurrency, low latency, connection state tracking
Message Router Parse OCPP messages and dispatch to handlers Protocol version awareness, message validation, handler registry
Device Registry Track charge points, connectors, and their state Real-time status, configuration storage, firmware tracking
Authorization Service Validate driver credentials (RFID, tokens, certificates) Low latency (<200ms), local auth list management, group authorization
Transaction Engine Manage charging session lifecycle Idempotent operations, meter value aggregation, billing event generation
Billing Engine Calculate costs and process payments Tariff management, CDR generation, payment gateway integration
Smart Charging Engine Compute and distribute charging profiles Real-time optimization, grid constraint awareness, profile stacking
OCPI Gateway Enable roaming with external networks OCPI 2.1.1/2.2.1 compliance, partner credential management, CDR exchange
Monitoring and Alerting Track system health and charger status Dashboards, anomaly detection, incident notification

High-Level Architecture

                    Load Balancer (sticky sessions)
                           |
              +------------+------------+
              |            |            |
         WS Server    WS Server    WS Server
              |            |            |
              +-----+------+------+----+
                    |             |
              Message Bus (Redis/Kafka)
                    |             |
    +-------+-------+-------+--------+-------+
    |       |       |       |        |       |
  Auth   Txn    Smart    Device   Billing  OCPI
  Svc    Engine  Charge   Registry Engine   Gateway
    |       |       |       |        |       |
    +-------+-------+-------+--------+-------+
                    |
              Database Layer
    (PostgreSQL + Redis + TimescaleDB)

Technology Choices

WebSocket Libraries

The WebSocket server is the most performance-critical component. Your choice of language and library directly impacts connection capacity and message throughput.

Language Library Strengths Considerations
Node.js ws or uWebSockets.js Excellent ecosystem, fast prototyping, native JSON handling Single-threaded event loop; use clustering for CPU-bound work
Go gorilla/websocket or nhooyr/websocket High concurrency with goroutines, low memory per connection Smaller OCPP ecosystem; more protocol-level code to write
Java Spring WebSocket or Netty Enterprise tooling, strong typing, mature ecosystem Higher memory footprint per connection, more boilerplate
Rust tokio-tungstenite Maximum performance, minimal memory Steepest learning curve, smallest OCPP ecosystem
Python websockets or FastAPI Rapid development, data science integration Lower throughput than compiled alternatives; viable for smaller scales

For most teams, Node.js with ws or Go with gorilla/websocket offers the best balance of development speed, performance, and ecosystem support.

Databases

A CSMS has distinct data access patterns that benefit from a polyglot persistence strategy.

Data Type Recommended Database Reasoning
Charge points and config PostgreSQL Relational data with complex queries, strong consistency
Transactions and CDRs PostgreSQL ACID compliance for financial data, complex reporting queries
Active sessions Redis Sub-millisecond reads for real-time authorization and session state
Meter values TimescaleDB (PostgreSQL extension) Time-series optimized storage, automatic partitioning, compression
OCPP message logs Elasticsearch or ClickHouse Full-text search across millions of messages, log analysis
Charging profiles Redis + PostgreSQL Redis for active profiles (fast reads), PostgreSQL for history

Starting with PostgreSQL for everything is a valid approach for early-stage development. Introduce specialized databases as specific performance bottlenecks emerge.

OCPP Implementation Steps

Step 1: WebSocket Server

Build the connection management layer first. This is the foundation everything else depends on.

Key requirements:

  • Accept WebSocket connections with OCPP subprotocol negotiation (ocpp1.6, ocpp2.0.1)
  • Extract charge point identity from the URL path (e.g., /ocpp/CP001)
  • Maintain a connection registry mapping charge point IDs to active WebSocket connections
  • Implement WebSocket Ping/Pong for connection health monitoring
  • Handle disconnection detection and cleanup
Connection URL pattern:
  wss://your-csms.com/ocpp/{chargePointId}

Subprotocol negotiation:
  Client requests: ocpp1.6, ocpp2.0.1
  Server selects: ocpp1.6 (or whichever version to use)

Step 2: BootNotification Handling

BootNotification is the first OCPP message every charger sends after connecting. Your handler must:

  1. Parse the charge point vendor, model, serial number, and firmware version
  2. Decide whether to accept, reject, or set the charger to pending status
  3. Return the current time (chargers use this for clock synchronization) and heartbeat interval
  4. Register or update the charge point in the device registry
  5. Trigger any pending configuration changes or firmware updates for this charger

Accept unknown chargers in development; require pre-registration in production. The Pending status is useful for chargers that need manual approval or additional configuration before going live.

Step 3: Authorization

Every charging session begins with authorization. Build the authorization service to handle multiple methods:

  • RFID tags: Look up idTag in the database, check expiry and group membership
  • Remote start: The CSMS initiates charging via RemoteStartTransaction (driver uses an app)
  • Local Auth List: Maintain a cached list of authorized tags on the charger for offline operation
  • Plug and Charge: ISO 15118 certificate-based authorization (OCPP 2.0.1)

Authorization latency directly impacts driver experience. Target sub-200ms response times. Use Redis caching for frequently used tags and implement the Local Authorization List for resilience during network outages.

Step 4: Transaction Management

Transactions are the core business object of a CSMS. Handle them with the reliability they demand.

Transaction lifecycle (OCPP 1.6):

  1. Charger sends StartTransaction with connectorId, idTag, meterStart, and timestamp
  2. CSMS validates authorization, creates transaction record, returns transactionId
  3. Charger sends periodic MeterValues with energy readings, power measurements, and SoC
  4. Charger sends StopTransaction with meterStop, timestamp, and stop reason
  5. CSMS finalizes transaction, calculates cost, generates billing record

Critical implementation details:

  • transactionId assignment must be unique and sequential
  • Handle duplicate StartTransaction messages idempotently (network retries)
  • Process MeterValues asynchronously to avoid blocking the WebSocket handler
  • Implement transaction recovery for sessions interrupted by disconnection
  • Store raw meter values alongside computed totals for audit purposes

Step 5: Smart Charging

Smart charging adds real-time power management to your CSMS. Start with basic load balancing and iterate toward advanced optimization.

Implementation progression:

  1. Static load balancing: Set a fixed ChargePointMaxProfile per site that divides capacity equally
  2. Dynamic load balancing: Integrate with smart meters to adjust profiles based on real-time site consumption
  3. Priority-based allocation: Factor in driver priority, departure time, and SoC to optimize distribution
  4. Peak shaving: Monitor demand charges and throttle charging during peak windows
  5. Demand response: Integrate grid operator signals for external constraint management

Charging profiles use a stack-level priority system. Higher stack levels override lower ones. Ensure your implementation correctly composites overlapping profiles, which is one of the most error-prone areas of OCPP smart charging.

Step 6: OCPI Integration

OCPI (Open Charge Point Interface) enables roaming -- allowing drivers from other networks to use your chargers and vice versa.

Core OCPI modules to implement:

Module Purpose
Locations Publish your charge point locations, capabilities, and real-time status
Sessions Share active charging session data with the roaming partner
CDRs Exchange Charge Detail Records for billing reconciliation
Tariffs Publish pricing information for roaming drivers
Tokens Exchange driver authorization tokens between networks
Commands Allow remote start/stop from partner networks

OCPI is a REST API (not WebSocket), so it integrates as a separate service alongside your OCPP-facing infrastructure. At minimum, implement Credentials (mandatory for all OCPI connections), Locations, Tokens, Sessions, and CDRs for a functional roaming connection. You cannot do roaming with just Locations and CDRs — Tokens are required for authorization exchange, and Sessions for real-time tracking.

Scaling Considerations

WebSocket Connection Scaling

Each WebSocket connection consumes memory (typically 10-50 KB per connection, depending on your implementation). For 10,000 chargers, budget 100-500 MB of memory just for connection state.

Horizontal scaling strategy:

  • Deploy multiple WebSocket server instances behind a load balancer
  • Use sticky sessions (source IP or cookie-based) so each charger always connects to the same instance
  • Implement a shared session store (Redis) so any instance can handle CSMS-initiated commands
  • When a charge point reconnects, it may hit a different instance; design for this

Database Scaling

MeterValues are the highest-volume data in a CSMS. A single charger sending meter values every 60 seconds generates 525,600 rows per year. At 10,000 chargers, that is 5.2 billion rows annually.

Scaling approaches:

  • Use TimescaleDB for automatic time-based partitioning and compression
  • Implement data retention policies (raw values for 90 days, aggregated values for 7 years)
  • Separate read and write workloads with read replicas
  • Partition transaction tables by date range
  • Archive completed transactions to cold storage after billing reconciliation

Message Throughput

A production CSMS must handle bursts of simultaneous messages. Common burst patterns:

  • Morning peak: Hundreds of chargers sending StatusNotification as drivers arrive at work
  • CSMS restart: All connected chargers send BootNotification simultaneously after reconnection
  • Firmware update: Mass FirmwareStatusNotification messages during a fleet-wide update

Use a message queue (Redis Streams, Apache Kafka, or RabbitMQ) between the WebSocket layer and message handlers to absorb these bursts without dropping messages or overloading downstream services.

How OCPPLab Accelerates CSMS Development

Building a CSMS without simulated chargers means you cannot test until you have physical hardware. This creates a painful feedback loop: code for days, deploy, connect a charger, discover a bug, repeat.

OCPPLab eliminates this bottleneck by providing virtual charge points that behave like real chargers throughout your entire development cycle:

  • Develop against realistic behavior: Connect virtual chargers to your CSMS from day one. Test BootNotification handling before you write transaction logic.
  • Validate every message flow: Exercise all OCPP message types including edge cases (offline authorization, partial meter values, interrupted transactions) without waiting for specific charger hardware.
  • Load test early: Spin up 1,000 virtual chargers to find connection management bugs, database bottlenecks, and memory leaks before they surface in production.
  • Test smart charging: Simulate multiple EVs with different battery levels, power demands, and departure times to validate your load balancing algorithms produce correct charging profiles.
  • Regression testing: Build automated test suites that run virtual chargers through critical scenarios on every deploy, catching OCPP protocol regressions before they reach production.
  • Multi-version testing: Test against both OCPP 1.6 and 2.0.1 charger behavior simultaneously, ensuring your dual-version CSMS handles protocol differences correctly.

Teams using OCPPLab report cutting their CSMS development cycle by months because they can test continuously rather than waiting for hardware availability.

Frequently Asked Questions

How long does it take to build a CSMS?

A minimum viable CSMS supporting BootNotification, Authorization, and basic transaction management takes 2-4 months for a small team (2-3 engineers). A production-grade CSMS with smart charging, OCPI roaming, billing, and monitoring typically requires 12-18 months. Ongoing maintenance, OCPP compliance updates, and feature development are continuous after launch.

What team do I need?

At minimum: 1-2 backend engineers with WebSocket and real-time systems experience, 1 engineer with OCPP protocol expertise (or willingness to deeply study the specification), and 1 DevOps/infrastructure engineer for deployment and monitoring. For a full-featured CSMS, add frontend engineers for the operator dashboard, a data engineer for analytics, and QA engineers for protocol compliance testing.

Should I support OCPP 1.6 and 2.0.1?

Yes. OCPP 1.6 is still the most widely deployed version, and many chargers in the field will run 1.6 for years. OCPP 2.0.1 is required for new installations in many markets and provides critical features (improved transaction model, device management, ISO 15118 integration). Build your message router to handle both versions from the start, with version-specific message handlers that share common business logic.

What is the biggest technical challenge?

WebSocket connection management at scale. Maintaining tens of thousands of persistent connections, handling reconnection storms gracefully, ensuring message ordering, and routing CSMS-initiated commands to the correct server instance are all non-trivial engineering problems. Most teams underestimate this and focus too early on business logic.

Can I use an existing OCPP library instead of building from scratch?

Absolutely. Libraries like ocpp (Python), node-ocpp (Node.js), and various Java OCPP libraries handle protocol-level concerns (message parsing, validation, routing) so you can focus on business logic. Evaluate libraries for protocol completeness, maintenance activity, and community size before committing.

How do I handle charger firmware differences?

Different charger manufacturers interpret the OCPP specification differently. Some send optional fields, others omit them. Some use slightly different enum values or timestamp formats. Build your message handlers defensively: validate but accept variations, log discrepancies, and maintain a charger compatibility matrix. Testing against multiple virtual charger configurations in OCPPLab helps identify these differences before production deployment.

Last updated:

Test your OCPP implementation today

Deploy 1000+ virtual charge points in minutes. No hardware needed.

Get OCPP & EV charging insights

Protocol updates, testing best practices, and industry news. No spam.

Join 1,000+ developers testing with us

Stop Paying for Hardware You Don't Need

Deploy 10,000 virtual chargers in 2 minutes. Catch bugs before production, cut months from QA cycles, and save 80% on testing infrastructure.

Simulate vendors
Emulate ABB, EVBox, Wallbox and more with realistic firmware behaviors.
Test at scale
Run hundreds of virtual points to validate performance and autoscaling.
Integrate safely
Verify edge-cases, firmware updates, and recovery flows before field deployment.
No credit card required • Deploy your first virtual charger in 2 minutes • Contact sales for enterprise plans