The two messages
// Read
[2, "19223230", "GetConfiguration", { "key": ["HeartbeatInterval", "MeterValueSampleInterval"] }]
[3, "19223230", {
"configurationKey": [
{ "key": "HeartbeatInterval", "readonly": false, "value": "900" },
{ "key": "MeterValueSampleInterval", "readonly": false, "value": "60" }
],
"unknownKey": []
}]
// Write
[2, "19223231", "ChangeConfiguration", { "key": "HeartbeatInterval", "value": "300" }]
[3, "19223231", { "status": "Accepted" }]Calling GetConfiguration with no keys returns everything the charger exposes, which is the right first move when onboarding an unfamiliar model.
The four write outcomes
| Status | Means | You must |
|---|---|---|
Accepted | Applied immediately | Nothing |
RebootRequired | Staged, active after restart | Schedule a Reset — the value is NOT live |
Rejected | Refused — bad value or read-only key | Check the value against the key's constraints |
NotSupported | This charger has no such key | Record as a model capability gap |
RebootRequired is the one that catches people. The change is stored but not in effect. A platform that treats it as success believes the fleet is configured a way it is not, and the discrepancy surfaces weeks later as inconsistent behaviour. Track staged changes explicitly and reconcile after the reboot actually happens.Keys that matter
| Key | Controls | Watch out for |
|---|---|---|
HeartbeatInterval | Heartbeat period | Data cost across a fleet |
MeterValueSampleInterval | MeterValues frequency | Too fine floods; too coarse loses billing granularity |
MeterValuesSampledData | Which measurands are sent | Missing Energy.Active.Import.Register breaks billing |
ClockAlignedDataInterval | Clock-aligned sampling | Fleet-wide synchronised bursts if set identically |
ConnectionTimeOut | Wait before releasing an unstarted session | Too long blocks connectors |
LocalAuthorizeOffline | Offline authorisation | The offline policy decision |
LocalPreAuthorize | Authorise from local list before asking | Faster starts, weaker central control |
AuthorizationCacheEnabled | Whether verdicts are cached | Revocation exposure |
StopTransactionOnEVSideDisconnect | Stop when the vehicle unplugs | Disabling it leaves sessions open |
TransactionMessageAttempts | Retry count for queued messages | Too low loses offline transactions |
ResetRetries | Reset retry attempts | Boot-loop risk |
MeterValuesSampledData deserves attention. It decides which measurands the charger sends, and a unit configured without the cumulative energy register produces sessions you cannot bill. Verify it during commissioning for every model.
Configuration drift
On any fleet of scale, configuration diverges. Firmware updates reset keys to vendor defaults, engineers change values on site to fix a problem and never document it, replacement units arrive with different defaults, and different procurement batches of the same model ship differently.
Treating configuration as fleet state rather than installation state means:
- Define a desired configuration per charger model, in version control, not in a spreadsheet.
- Poll actual configuration on a schedule — weekly is usually enough.
- Diff actual against desired and alert on drift.
- Re-apply configuration after every firmware update, without exception. Updates frequently reset keys.
- Record configuration alongside transactions. When billing looks wrong for one charger, the sampling configuration at the time is usually the explanation.
OCPP 2.0.1: the variable model
2.0.1 replaces flat key-value configuration with a structured component and variable model.
| 1.6J | 2.0.1 |
|---|---|
GetConfiguration | GetVariables |
ChangeConfiguration | SetVariables |
| Flat string keys | Component + Variable, with attribute types |
| Value is always a string | Typed values with characteristics and limits |
The structured model is genuinely better — a variable belongs to a named component such as a specific EVSE or connector, so you can configure one connector differently from another, which 1.6J's flat namespace cannot express. It also exposes each variable's type, unit and permitted range, so a platform can validate before writing rather than discovering Rejected.
For a mixed fleet, abstract both behind one internal configuration interface. Nothing above the protocol edge should need to know which model a given charger uses.
Production lessons
- Never treat
RebootRequiredas applied. Track it as staged and reconcile after restart. - Re-apply configuration after every firmware update.
- Poll and diff against a versioned desired state. Drift is guaranteed otherwise.
- Verify
MeterValuesSampledDataat commissioning — a missing energy register makes sessions unbillable. - Stagger
ClockAlignedDataIntervalor the whole fleet reports simultaneously and your ingestion sees synchronised spikes. - Store configuration history per charger. It is the first thing to check when one unit behaves differently from its siblings.
- Never change configuration mid-transaction unless you know the model handles it — some abort the session.
Frequently asked questions
The change was stored but is not in effect until the charger restarts. Treating it as success means believing the fleet is configured a way it is not, so staged changes should be tracked explicitly and reconciled after the reboot happens.
HeartbeatInterval and MeterValueSampleInterval for data volume, MeterValuesSampledData for billing correctness, LocalAuthorizeOffline and AuthorizationCacheEnabled for offline behaviour, and TransactionMessageAttempts for whether offline transactions survive.
Firmware updates reset keys to vendor defaults, engineers change values on site without documenting them, replacement units ship with different defaults, and procurement batches of the same model can differ. Configuration should be polled and diffed against a versioned desired state.
GetConfiguration and ChangeConfiguration become GetVariables and SetVariables, and flat string keys become a structured component-and-variable model with typed values. This lets you configure individual connectors differently and validate values before writing.