Most of a charger's OCPP behaviour is controlled by configuration keys: how often it sends meter values, what it does offline, whether it stops a session when the cable is pulled. Getting these right per hardware model is a large part of making a fleet behave consistently. This part covers reading and writing configuration, the standard keys worth knowing, TriggerMessage for requesting messages on demand, and DataTransfer for vendor extensions. It is part 8 of the series.
GetConfiguration
[2, "csms-gc-401", "GetConfiguration", {"key": ["HeartbeatInterval", "MeterValueSampleInterval", "VendorFooBar"]}][
3,
"csms-gc-401",
{
"configurationKey": [
{ "key": "HeartbeatInterval", "readonly": false, "value": "300" },
{ "key": "MeterValueSampleInterval", "readonly": false, "value": "60" }
],
"unknownKey": ["VendorFooBar"]
}
]key is an optional array. Omit it to get every key the charger has, including vendor-specific ones. That full dump is worth storing on first connection and after each firmware update; diffing it across firmware versions shows you what a vendor changed. GetConfigurationMaxKeys tells you how many keys you can request in one call.
Each entry has key, readonly and an optional value. Values are always strings, even for integers and booleans, which are sent as "true" and "false". Lists (CSL, comma-separated lists) are single strings like "Energy.Active.Import.Register,SoC".
ChangeConfiguration
[2, "csms-cc-402", "ChangeConfiguration", {"key": "HeartbeatInterval", "value": "900"}]
[3, "csms-cc-402", {"status": "Accepted"}]| status | Meaning |
|---|---|
Accepted | Change applied |
Rejected | Key is known but the value is not acceptable, or the key is read-only |
RebootRequired | Change stored, but only takes effect after a reboot |
NotSupported | Key is not known to the charger |
One key per message. key is a CiString50 and value a CiString500. Note that value must be a string: sending "value": 900 as a number is a TypeConstraintViolation on strict chargers and silently works on lenient ones, which is how this bug survives testing.
Standard keys worth knowing
OCPP 1.6 defines many standard keys, some required and some optional, grouped by feature profile. These are the ones you will touch most often.
| Key | What it controls | See |
|---|---|---|
HeartbeatInterval | Seconds between heartbeats | Part 2 |
WebSocketPingInterval | Seconds between WebSocket pings; 0 disables | Part 2 |
ConnectionTimeOut | Seconds to wait for a cable after authorization | Part 7 |
MeterValueSampleInterval / MeterValuesSampledData | Transaction sampling interval and measurands | Part 6 |
ClockAlignedDataInterval / MeterValuesAlignedData | Clock-aligned sampling | Part 6 |
StopTxnSampledData / StopTxnAlignedData | Contents of transactionData in StopTransaction | Part 5 |
AuthorizeRemoteTxRequests | Whether remote starts are authorized first | Part 4 |
LocalAuthorizeOffline / LocalPreAuthorize | Use of cache and local list | Part 4 |
AllowOfflineTxForUnknownId | Allow unknown tags while offline | Part 4 |
AuthorizationCacheEnabled / LocalAuthListEnabled | Enable cache and local list | Part 4 |
StopTransactionOnEVSideDisconnect | Whether unplugging at the vehicle ends the transaction | Part 5 |
UnlockConnectorOnEVSideDisconnect | Whether the charger-side lock releases when the vehicle side is unplugged | |
StopTransactionOnInvalidId / MaxEnergyOnInvalidId | Behaviour when a started tag turns out invalid | Part 5 |
TransactionMessageAttempts / TransactionMessageRetryInterval | Retry policy for transaction messages | Part 5 |
MinimumStatusDuration | Debounce for status changes | Part 3 |
NumberOfConnectors | Read-only connector count | |
SupportedFeatureProfiles | Read-only list of supported profiles | Part 1 |
ChargeProfileMaxStackLevel, ChargingScheduleAllowedChargingRateUnit, ChargingScheduleMaxPeriods, MaxChargingProfilesInstalled | Smart charging capabilities | Part 9 |
Reading the response carefully
- A key that appears in
unknownKeyis not supported. Do not keep trying to set it on every boot. readonly: truekeys cannot be changed withChangeConfiguration; aRejectedfor them is expected.- A key that is returned without
valueis known but has no value set, which is different from an empty string. - Vendor-specific keys show up alongside standard ones in the full dump. They often control important behaviour (cable lock timing, display language, the CSMS URL itself), so document them per model rather than ignoring them.
- Some firmware returns keys with different capitalisation from the spec. Keys are case-insensitive strings, so compare case-insensitively.
Configuration as code
A pattern that works well on a mixed fleet is to treat configuration as desired state:
- Define a baseline profile of key values, with per-model overrides where hardware needs different values.
- On
BootNotification(or duringPending, see part 2), read the full configuration withGetConfiguration. - Diff against the desired state and send
ChangeConfigurationfor each difference, one at a time. - Record results.
NotSupportedandRejectedgo into a per-model exceptions list rather than being retried on every boot.
This catches chargers that were factory reset, swapped by a field technician, or changed by someone using a vendor tool on site.
TriggerMessage
TriggerMessage (Remote Trigger profile) asks the charger to send a specific message now, rather than waiting for its normal schedule.
[2, "csms-tm-403", "TriggerMessage", {"requestedMessage": "MeterValues", "connectorId": 1}]
[3, "csms-tm-403", {"status": "Accepted"}]
[2, "mv-9055", "MeterValues", {"connectorId": 1, "transactionId": 880214, "meterValue": [{"timestamp": "2026-10-26T11:02:13Z", "sampledValue": [{"value": "1302200", "context": "Trigger"}]}]}]
[3, "mv-9055", {}]requestedMessage can be BootNotification, DiagnosticsStatusNotification, FirmwareStatusNotification, Heartbeat, MeterValues or StatusNotification. connectorId is optional. The response is Accepted, Rejected or NotImplemented, and the triggered message is sent after the response. Useful cases:
StatusNotificationafter a CSMS restart or a suspected missed status.MeterValuesto refresh a live session display when the driver opens the app.Heartbeatas a cheap "are you really there?" check.BootNotificationto finish provisioning aPendingcharger.FirmwareStatusNotificationorDiagnosticsStatusNotificationto check progress of an update or upload (part 10).
DataTransfer
DataTransfer is OCPP's extension mechanism. Either side can send it, with a vendor identifier, an optional message identifier and an optional data string.
[
2,
"dt-77",
"DataTransfer",
{
"vendorId": "com.vendorx",
"messageId": "CableTemperature",
"data": "{\"connectorId\":1,\"celsius\":41.5}"
}
][3, "dt-77", {"status": "Accepted"}]vendorId (CiString255) identifies the extension owner; a reversed DNS name is the convention. messageId is a CiString50. data has no length limit in the spec and no defined format; it is a string, so JSON payloads are usually embedded as escaped JSON as above, though some chargers send a JSON object directly in that field, which is not schema-compliant. The response status is Accepted, Rejected, UnknownMessageId or UnknownVendorId, with an optional data string.
Vendors use DataTransfer for things the 1.6 core does not cover: display messages, tariff text, extra diagnostics, payment terminal results. Handle unknown vendor IDs by returning UnknownVendorId rather than a CALLERROR, and keep each vendor's extensions behind an adapter so they do not leak into your core transaction logic. Many of the things vendors added this way became standard messages in OCPP 2.0.1; see OCPP 2.0.1 display messages and costs.
Key takeaways
- Configuration values are strings: integers, booleans and lists included.
- Handle
RebootRequiredexplicitly and verify changes by reading them back. - Manage configuration as desired state per model, reconciled on boot.
- Use
TriggerMessageinstead of waiting for periodic messages, and keepDataTransferextensions isolated per vendor.
Previous: Part 7, remote commands. Next: Part 9, smart charging. The OCPP 2.0.1 replacement for configuration keys is the device model.

