OCPP protocol specifics

  • Charge point is a WebSocket client, not a server
  • CSMS URL includes CP identity: ws://<csms>/ocpp/1.6/<cpId>
  • ocpp1.6 subprotocol header is required — CSMS rejects without it
  • Text frames only, no binary

Go patterns

Infinite for loop for read loop — not iterating a collection; blocking on each Read waiting for the next message.

Buffered channels between goroutines — prevents the read loop from blocking if the consumer is slow.

Non-blocking send with select/default — drop the message rather than block.

sync.Mutex around the connection — lock only around the assignment, not during network calls. Locking during network I/O causes deadlocks.

context.WithTimeout for dial and write — bounds operations that could hang indefinitely.

Nil guard before using a connection that may not be initialised.

Warning

Always unlock on all code paths — An early return on error inside a lock will deadlock. Lock only the critical section, not the surrounding network call.

Design principles

  • Transport layer is dumbwebsocket.go deals in raw []byte only, knows nothing about OCPP message structure
  • Shutdown order — cancel context before closing the connection so the read loop exits cleanly rather than receiving an unexpected error
  • Channel ownership — channel lives on the struct; callers just read from it