ocpp-client 0.5.0

OCPP Client Implementation. Use this library to implement an OCPP charge point
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
# ๐Ÿ”Œ OCPP Client

> **A lightweight, embedded-friendly Rust OCPP communication framework for building real charge points and CSMS integrations.**

[![Rust](https://img.shields.io/badge/rust-stable-orange.svg)](https://www.rust-lang.org/)
[![License](https://img.shields.io/badge/license-MIT%20%2F%20Apache--2.0-blue.svg)](#license)
[![Crates.io](https://img.shields.io/crates/v/ocpp-client)](https://crates.io/crates/ocpp-client)
[![Documentation](https://docs.rs/ocpp-client/badge.svg)](https://docs.rs/ocpp-client)
[![.github/workflows/ci.yaml](https://github.com/flowionab/ocpp-client/actions/workflows/ci.yaml/badge.svg)](https://github.com/flowionab/ocpp-client/actions/workflows/ci.yaml)
[![no_std](https://img.shields.io/badge/no__std-compatible-brightgreen.svg)](#features)

---

## ๐Ÿš€ Overview

**OCPP Client** is the communication layer of the **Flowion Rust OCPP ecosystem**, providing the networking and transport foundation required to build OCPP-enabled charge points and backend integrations.

The library handles the complexities of establishing and managing OCPP connections, including:

* Connection lifecycle management
* Transport handling
* Message routing
* Communication reliability

OCPP message types and protocol definitions are provided by [`ocpp-types`](https://github.com/flowionab/ocpp-types), while OCPP Client focuses on the communication layer required to exchange messages between charge points and Charge Station Management Systems (CSMS).

Designed for both cloud/server environments and resource-constrained embedded systems, OCPP Client speaks WebSocket out of the box and compiles for `no_std` + `alloc` targets. An `embassy-net`-based transport and an STM32 board scaffold ship alongside it as **experimental** crates - see [Supported Transports](#-supported-transports).

The library currently supports **OCPP 1.6J**, **OCPP 2.0.1**, and **OCPP 2.1**.

---

## โœจ Features

* ๐Ÿฆ€ Native Rust implementation
* ๐Ÿ”Œ OCPP communication layer
* โšก OCPP **1.6J support**
* ๐Ÿš€ OCPP **2.0.1 support**
* โšก OCPP **2.1 support**
* ๐ŸŒ WebSocket transport
* ๐Ÿ”’ Secure WebSocket (WSS)
* ๐Ÿ”‹ `embassy-net` transport for embedded targets (experimental)
* ๐Ÿ”„ Connection lifecycle management
* ๐Ÿ’“ Scheduled WebSocket keepalive with dead-peer detection
* ๐Ÿ“จ Message routing
* ๐Ÿงฉ Transport abstraction
* ๐Ÿชถ Lightweight runtime
* ๐Ÿ’พ `no_std` support for embedded environments
* ๐Ÿ–ฅ๏ธ `std` support enabled by default for desktop and server applications

---

## ๐Ÿ”Œ Supported Protocols

| Protocol   | Status         | Actions wired up |
| ---------- | -------------- | ---------------- |
| OCPP 1.6J  | โœ… Supported    | all 39           |
| OCPP 2.0.1 | โœ… Supported    | all 64           |
| OCPP 2.1   | โœ… Supported    | all 91           |

1.6's 39 includes the eleven actions from the security whitepaper (`SignCertificate`, `GetLog`,
`SignedUpdateFirmware` and friends), wired up in **0.4.0** when `ocpp-types` first defined them.

Every action defined by [`ocpp-types`](https://crates.io/crates/ocpp-types) for each version has a
`send_*`/`on_*` method - `tests/action_coverage.rs` fails the build otherwise, so the table can't
drift. If a method you expect is missing, check [CHANGELOG.md](CHANGELOG.md) before filing an
issue: five actions were only wired up in **0.2.1**, so a 0.2.0 build is missing
`SecurityEventNotification` (2.0.1) and `TriggerMessage`, `SetDisplayMessage`, `GetDERControl`,
`SetDERControl`, `UpdateDynamicSchedule` (2.1).

---

## ๐ŸŒ Supported Transports

| Transport                                   | Status            |
| ------------------------------------------- | ----------------- |
| WebSocket                                    | โœ… Supported       |
| Secure WebSocket (WSS), incl. mutual TLS     | โœ… Supported       |
| `embassy-net` (embedded, `no_std` + `alloc`) | ๐Ÿงช Experimental   |

The embedded transport (`crates/ocpp-transport-embassy-net`) and the NUCLEO-H723ZG firmware
scaffold (`crates/ocpp-board-stm32h723-nucleo`) compile and fully link against the real
`thumbv7em-none-eabihf` target in CI, but **neither has been run against real hardware or a real
CSMS**, and the embedded transport has no TLS. Treat them as a starting point for a board bring-up
rather than a supported deployment path. Each crate's README states its exact status.

---

## โš™๏ธ Feature Flags

OCPP Client supports both standard Rust environments and embedded systems.

By default, the `std` feature is enabled:

```toml
[dependencies]
ocpp-client = "0.x"
```

For embedded targets or `no_std` environments:

```toml
[dependencies]
ocpp-client = { version = "0.x", default-features = false }
```

This lets the same OCPP communication core compile for resource-constrained devices as well as server-side applications. Embedded users supply their own `Executor`/`Timer` implementations (e.g. backed by `embassy-executor`/`embassy-time`) and a `critical-section` backend for their target.

The optional `chrono` feature adds `From`/`Into` between `ocpp_types::OcppTimestamp` - the type every `dateTime` field uses - and `chrono::DateTime`, for applications that already keep time in chrono. It is interop only; chrono never reaches the wire.

The optional `validate` feature adds spec-conformance checking - see below.

---

## โœ… Validating payloads (`validate`)

Most of the specification's limits are in the types, so a violation cannot be built: a field the
schema bounds at `maxLength: 20` is a `heapless::String<20>`. Two categories escape that - bounds
too large to store inline (certificates, CSRs, OCSP results, which are growable `String`s), and
`minItems`/`minimum`/`maximum`/`multipleOf`, which no collection or integer type expresses at all.

The `validate` feature covers exactly those:

```toml
[dependencies]
ocpp-client = { version = "0.x", features = ["validate"] }
```

**Nothing calls it for you.** Validation is not wired into `Client::call`, deliberately: it would
need a `Validate` bound on `Action::Request`, and a trait bound that appears only when a feature is
enabled is not additive - one crate in your dependency graph turning it on would break an unrelated
crate's custom `Action` implementation. So you validate where you want it, which is one line:

```rust,ignore
use ocpp_client::ocpp_types::validate::Validate;

request.validate()?;                       // names the offending field, before it hits the wire
let response = client.call::<Reset>(request).await?;
```

The payoff is sharper on the receiving side. A schema violation the peer catches comes back as a
`CALLERROR` you cannot correlate to any field; validating locally gives you the JSON path. And
because a `ValidationError` converts straight into your version's error type, a handler can reject
a bad payload with the correct wire code:

```rust,ignore
client.on_clear_variable_monitoring(|request, _client| async move {
    request.validate()?;                   // -> Occurrence/PropertyConstraintViolation
    Ok(handle(request))
}).await;
```

That conversion is why the feature exists here rather than only upstream: `ocpp-types` classifies a
violation but leaves the wire code to "your version", and the versions disagree - OCPP 1.6J spells
it `OccurenceConstraintViolation`, with one `r`, where 2.0.1 and 2.1 spell it `Occurrence`. The
`From<ValidationError>` impls on `OCPP1_6Error`/`OCPP2_0_1Error`/`OCPP2_1Error` get that right, and
put the JSON path in `errorDetails` so a peer can match on it without parsing prose.

Out of scope, because the schemas do not state them: cross-field rules from the specification's
prose, your own `customData` payload, and 2.x's deliberately untyped `DataTransfer.data`.

---

## ๐Ÿ—๏ธ Architecture

OCPP Client separates protocol definitions from communication.

```text
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚          Your Application                  โ”‚
โ”‚          Charge Point / CSMS Logic         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       โ”‚
                       โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              ocpp-charge-point             โ”‚
โ”‚                                            โ”‚
โ”‚  Complete charge point firmware framework  โ”‚
โ”‚  Add hardware bindings and deploy          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       โ”‚
                       โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              ocpp-client                   โ”‚
โ”‚                                            โ”‚
โ”‚  OCPP communication runtime                โ”‚
โ”‚  Transport abstraction                     โ”‚
โ”‚  WebSocket / embedded transports           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       โ”‚
                       โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚               ocpp-types                   โ”‚
โ”‚                                            โ”‚
โ”‚  OCPP message types                        โ”‚
โ”‚  Protocol models                            โ”‚
โ”‚  Serialization                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

---

## ๐ŸŒ Flowion OCPP Ecosystem

OCPP Client is designed as a modular building block within the **Flowion Rust OCPP ecosystem**.

Each project has a focused responsibility, allowing developers to choose the right level of abstraction for their application.

---

### ๐Ÿ“ฆ ocpp-types

**OCPP protocol definitions and data models**

[`ocpp-types`](https://github.com/flowionab/ocpp-types) provides the foundation for working with OCPP messages in Rust.

It contains:

* OCPP message types
* Protocol models
* Serialization and deserialization
* Version-specific protocol definitions

OCPP Client builds on top of `ocpp-types` to provide communication capabilities.

---

### ๐Ÿ”Œ ocpp-client

**OCPP communication and transport layer**

This repository provides the runtime required to connect OCPP-enabled systems.

It handles:

* Connection management
* Transport abstraction
* Message routing
* WebSocket communication
* Embedded-compatible transports
* `no_std` environments

It is designed to run in both:

* ๐Ÿ–ฅ๏ธ Server environments
* ๐Ÿ”‹ Embedded charge point environments

---

### โšก ocpp-charge-point

**Complete charge point firmware framework**

[`ocpp-charge-point`](https://github.com/flowionab/ocpp-charge-point) provides a complete framework for building OCPP-enabled charge point firmware.

The goal is to make developing custom charging hardware as simple as implementing the required hardware bindings.

Developers provide hardware-specific implementations such as:

* GPIO control
* Contactor control
* Metering interfaces
* Connector handling
* LEDs and user interfaces
* Hardware drivers

while the framework handles:

* Charge point state management
* OCPP communication
* Charging workflows
* Backend communication
* Protocol integration

This allows manufacturers and developers to build custom OCPP-compatible chargers without implementing the complete protocol stack from scratch.

---

## ๐ŸŽฏ Use Cases

OCPP Client can be used for:

* ๐Ÿš— Building EV charge point firmware
* ๐Ÿญ Developing OCPP-enabled hardware
* ๐Ÿ–ฅ๏ธ Building CSMS integrations
* ๐Ÿงช Testing OCPP implementations
* ๐Ÿ”‹ Connecting embedded devices to charging platforms
* โšก Creating custom charging solutions
* ๐Ÿค– Automated integration testing

---

## ๐Ÿ“ฆ Installation

Add the dependency to your `Cargo.toml`:

```toml
[dependencies]
ocpp-client = "0.x"
```

---

## ๐Ÿš€ Quick Example

```rust
use ocpp_client::connect_1_6;
use ocpp_client::ocpp_types::v16::HeartbeatRequest;

#[tokio::main]
async fn main() {
    // `None` takes the defaults: 5s request timeout, automatic reconnect, and keepalive
    // pinging every 60s.
    let client = connect_1_6("wss://example.com/ocpp", None).await.unwrap();

    let response = client.send_heartbeat(HeartbeatRequest {}).await.unwrap();
    println!("CSMS time: {}", response.current_time);
}
```

Use `connect_2_0_1`/`connect_2_1` for those versions, or `connect` to negotiate whichever version
the server picks.

### Vendor extensions (`customData`)

2.0.1 and 2.1 hang an optional `customData` object on nearly every message. The `send_*`/`on_*`
methods use the specification's own shape - a bare `vendorId` - which is all most deployments
need. To carry your own, name the type on the action marker and go through `call`/`on`:

```rust,ignore
#[derive(serde::Serialize, serde::Deserialize)]
struct AcmeExtension {
    #[serde(rename = "vendorId")]
    vendor_id: String,
    #[serde(rename = "siteId")]
    site_id: u32,
}

let response = client.call::<Reset<AcmeExtension>>(request).await?;
```

`NoCustomData` is the other end of the trade: it accepts whatever a peer sends and discards it,
costing one byte per node instead of the field's full width - worth naming on an MCU.

---

## ๐Ÿ’“ Keepalive & `WebSocketPingInterval`

By default a client pings the CSMS every 60 seconds and, after two unanswered pings, drops the
connection and redials. Without this a half-open link - a dropped NAT entry, a mobile connection
that vanished without a FIN - is undetectable: the socket accepts writes and nothing ever comes
back, and reconnect can't help because nothing reports the connection as closed.

```rust
use ocpp_client::{ConnectOptions, KeepaliveBehavior, KeepalivePolicy, connect_1_6};
use std::time::Duration;

let options = ConnectOptions {
    keepalive: KeepaliveBehavior::Enabled(KeepalivePolicy {
        interval: Duration::from_secs(30),
        timeout: None,          // fall back to the client's request timeout
        max_missed: 2,
    }),
    ..Default::default()
};
let client = connect_1_6("wss://example.com/ocpp", Some(options)).await?;
```

Set `keepalive: KeepaliveBehavior::Disabled` if the CSMS pings the charge point instead, or if the
deployment forbids unsolicited traffic.

This crate does not implement a device model, but it owns the ping timer, so it exposes the value
for the layer that does:

| OCPP | Variable / key | Read | Write |
| ---- | -------------- | ---- | ----- |
| 2.0.1 / 2.1 | `OCPPCommCtrlr.WebSocketPingInterval` (`GetVariables`/`SetVariables`) | `client.ping_interval()` | `client.set_ping_interval(..)` |
| 1.6 (security whitepaper) | `WebSocketPingInterval` (`GetConfiguration`/`ChangeConfiguration`) | `client.ping_interval()` | `client.set_ping_interval(..)` |

Both are non-`async`, so a `GetVariables` handler can call them directly. `None` maps to the
spec's `0` (disabled) in both directions, writes take effect immediately rather than after the
current interval finishes, and a write can enable pinging on a client that started with keepalive
disabled.

---

## ๐Ÿงช Testing

OCPP Client is designed for:

* Integration testing
* Charge point development
* Embedded testing
* CSMS validation
* Automated test environments

It can be combined with simulators and real charging hardware to validate complete OCPP workflows.

---

## ๐Ÿ›ฃ๏ธ Roadmap

Planned improvements:

* ๐Ÿ”Œ Additional embedded transports
* ๐Ÿ“š More examples
* ๐Ÿงช Expanded integration tests
* ๐Ÿ”ง Improved developer tooling

---

## ๐Ÿค Contributing

Contributions are welcome!

You can help by:

* ๐Ÿ› Reporting issues
* ๐Ÿ’ก Suggesting improvements
* ๐Ÿ“ Improving documentation
* ๐Ÿ”ง Submitting pull requests

---

## ๐Ÿ“„ License

OCPP Client is dual licensed:

* MIT License
* Apache License 2.0

You may choose either license.

---

## ๐Ÿข About Flowion

**OCPP Client** is developed by **Flowion AB** as part of our effort to make EV charging development more accessible through modern, open-source tooling.

Flowion builds software solutions for electric vehicle charging using open standards such as **OCPP**, helping developers and businesses build reliable and scalable charging infrastructure.

---

## โญ Support the Project

If you find this library useful:

* โญ Star the repository
* ๐Ÿ› Report issues
* ๐Ÿ’ก Suggest improvements
* ๐Ÿค Contribute

Together we can make EV charging development easier and more accessible.