OCPP in ten minutes

Learn the whole shape of OCPP in ten minutes: the charger opens a WebSocket, either side sends a Call, the other answers. And what Flowion handles for you.

4 min readEdit this page
On this page

OCPP is smaller than its page count suggests. Strip away the message catalogue and what remains is a WebSocket, a three-element JSON array, and one rule about not talking over yourself. This page is the whole of it, with a note at each step on what Flowion takes care of.

Everything here is OCPP-J: OCPP over WebSocket, using JSON. That is what every current charger speaks.

The station is the client

The charging station opens the WebSocket, and the CSMS is the server. Stations sit behind NAT with no reachable address, so the connection has to be made outward. Once open, it is full duplex: the CSMS sends commands down the same socket the station opened.

The station's identity is the last segment of the URL it dials. On Flowion:

wss://ocpp.flowion.io/{org_slug}/{charge_point_id}
wss://ocpp.flowion.io/{org_slug}/{project_slug}/{charge_point_id}

The second form names a project; the first uses your organization's default one. Connect a charger walks through setting this up on real hardware.

Because identity is settled once, when the connection opens, individual messages never repeat it. Every message on a socket is from, or to, that one station.

Agreeing on a version

The station lists the OCPP versions it can speak in the WebSocket handshake's Sec-WebSocket-Protocol header:

http
GET /acme/CP3211 HTTP/1.1
Host: ocpp.flowion.io
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Protocol: ocpp2.0.1, ocpp1.6
Sec-WebSocket-Version: 13

The server picks one and echoes it back:

http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Protocol: ocpp2.0.1

The names are exactly ocpp1.6, ocpp2.0.1 and ocpp2.1. Flowion supports all three and answers with the newest one the station offers. If a station offers none of them, the server completes the handshake without the header and closes the connection, so a station that connects and drops at once is usually set to a version nobody agreed on.

Which OCPP version am I dealing with? covers what the choice means.

Three message types

A WebSocket is just a pipe with no notion of a request matching a response. OCPP adds a thin RPC layer on top. Every message is a JSON array whose first element says what kind it is.

TypeNumberShape
Call2[2, "<id>", "<Action>", {payload}]
CallResult3[3, "<id>", {payload}]
CallError4[4, "<id>", "<code>", "<description>", {details}]

A Call is a request. It names an Action (BootNotification, Authorize, Reset) and carries that action's arguments:

json
[2, "19223201", "BootNotification",
 {"chargePointVendor": "VendorX",
  "chargePointModel": "SingleSocketCharger"}]

The answer reuses the same id, so the sender can match them up:

json
[3, "19223201",
 {"status": "Accepted",
  "currentTime": "2013-02-01T20:53:32.486Z",
  "interval": 300}]

The id is a string of at most 36 characters, unique among the Calls that sender has issued on that connection.

Either side may send a Call. The station sends BootNotification, Authorize, StatusNotification, MeterValues. The CSMS sends Reset, UnlockConnector, RemoteStartTransaction. Same framing both ways.

A "no" is still a CallResult

A refused card, a declined reset, a station that cannot unlock a connector: all of these are successful exchanges. They come back as CallResults carrying a status field whose value happens to be no.

A CallError means something else, and only in two cases: the message could not be transported, or it arrived and was not a valid message (missing fields, a duplicate id, a payload that does not parse). It is a bug report, not a business outcome. The defined codes:

CodeMeaning
NotImplementedAction unknown to the receiver
NotSupportedAction recognised but not supported
InternalErrorReceiver failed while processing
ProtocolErrorPayload incomplete
SecurityErrorSecurity problem prevented processing
FormationViolationPayload syntactically wrong
PropertyConstraintViolationValid syntax, invalid field value
OccurenceConstraintViolationField occurrence constraints violated
TypeConstraintViolationField of the wrong data type
GenericErrorAnything else

OccurenceConstraintViolation is misspelled in the specification, and on the wire too.

Flowion keeps the two apart for you. A command the station answered no to ends as rejected, with the station's answer attached; one that never got a valid answer ends as failed. See Sending commands.

One Call at a time

Neither side may send a Call while one of its own earlier Calls is still unanswered and not yet timed out. The rule is per direction: while a station works on your Reset, it may still send you a StatusNotification, and Calls from the two sides cross all the time.

This is the rule that shapes CSMS design most. Commands to one station are effectively a queue, and a station that stops answering holds up everything behind it until a timeout fires. Flowion keeps that queue per charger, so you can send commands whenever you like and each one is delivered in turn.

Heartbeats and pings

OCPP has a Heartbeat message, and WebSocket has its own Ping and Pong frames. Ping and Pong keep a connection alive and spot a dead peer. What they cannot carry is a timestamp, and that is the real job of the Heartbeat response: clock sync. A station with no battery-backed clock relies on the CSMS for the time, and a station with the wrong time writes the wrong timestamps into its session records.

Flowion answers every heartbeat with the current time, and hands each station its heartbeat interval when it boots.

Reconnecting

A station sends BootNotification when it boots, not every time its connection drops and comes back: the server already knows who it is from the handshake. So a BootNotification always means something. The station really did restart, or its details changed. Flowion keeps a history of these boot reports per charger, so a firmware change or a restart loop is easy to spot.

That is the whole protocol

You now know the transport, the framing, the error model and the concurrency rule. Everything else in OCPP is the catalogue of Actions and their payloads, and Flowion's gateway speaks that catalogue for you. You work with chargers, sessions and commands in the API reference instead.

Next: The vocabulary, the words you will meet in a charger's manual and logs.