OCPP-RS
OCPP-RS is a Rust library for implementing the Open Charge Point Protocol (OCPP) in Rust.
It supports OCPP 1.6 and OCPP 2.1 (2.0.1-compatible additive schemas).
- Full implementation of OCPP 1.6 and OCPP 2.1 message payloads
- Batteries included: OCPP-J parse/serialize for both versions
- CallResult typing via
PendingCalls/ action-name correlation (no untagged guessing) #![no_std]+alloc— library code never usesstd(baremetal-friendly). You must provide a global allocator (e.g.embedded-alloc,linked_list_allocator, or your RTOS heap).- Inspired by a python ocpp library; 2.1 layout inspired by rust-ocpp
no_std / firmware
The published library (src/) is #![no_std] + alloc end-to-end: zero std:: usage, and dependencies are built with default-features = false (chrono without clock/std). Integration tests and example/ may use std for host runners only — that does not leak into firmware consumers.
You must provide a global allocator on baremetal (e.g. embedded-alloc, linked_list_allocator, or your RTOS heap).
# Typical embedded consumer
[]
= { = "0.3", = false }
# plus your allocator crate / #[global_allocator]
Sanity checks:
# Library must build under #![no_std] (host is enough to catch std usage in src/)
# Optional: freestanding target (requires rustup target)
# Chrono must not enable std/clock via feature unification
|
# expect: chrono … alloc,serde (not std/clock)
Usage
[]
= "^0.3"
Upgrading from 0.2.x: see guides/migration-0.3.md.
Particularities
OCPP 1.6 CallResult
CALLRESULT has no action on the wire. Blind parse yields CallResultRaw. Correlate with PendingCalls (same idea as 2.1):
use ;
use TypedMessage;
use PendingCalls;
use TypedCallResult;
let mut pending = new;
let _wire = pending.send_call?;
let typed = pending.deserialize_typed?;
assert!;
- Sticky sessions or Redis
messageId → action name+resolve_with_action_namefor load-balanced deploys - Datetime: always parse RFC3339; serialize defaults to
%.3fZ(optionaldatetime_serialize_rfc3339) — guides/datetime-features.md - Last resort:
try_resolve_unique/probe_candidates(often ambiguous for{})
Details: v16::pending, migration guide.
OCPP 2.1
Same CallResult correlation model under v21::pending. Framing includes types 2–6
(CALL, CALLRESULT, CALLERROR, CALLRESULTERROR, SEND).
use ;
use HeartbeatRequest;
use TypedMessage;
use PendingCalls;
use TypedCallResult;
let mut pending = new;
let call = new;
// For Redis / multi-node: store call.action_kind() (== "Heartbeat") with the message id
assert_eq!;
let _wire = pending.send_call?;
let typed = pending.deserialize_typed?;
assert!;
Load-balanced example (2.1)
use resolve_with_action_name;
use ;
let CallResult = deserialize_to_message? else ;
let typed = resolve_with_action_name?;
Example (1.6 CALL)
use ;
use Action;
let incoming_text = "[2, \"19223201\", \"BootNotification\", { \"chargePointVendor\": \"VendorX\", \"chargePointModel\": \"SingleSocketCharger\" }]";
let incoming_message = deserialize_to_message;
if let Ok = incoming_message
Sending a CALLRESULT:
use CallResultRaw;
use ;
let response = CallResult;
let json = serialize_message?;
Contributing
Contributions are welcome. Please add/update tests with behavior changes.
Roadmap
- Harden OCPP 2.1 field coverage against Part 3 schemas / errata
- Fuzzing for v16 and v21 wire parsers