DRAFT — not indexed by search engines. Visible only via direct URL or the admin page.

OCPP MeterValues: What Gets Reported and How Often

MeterValues carries the energy data that drives billing and analytics. What it contains, how often it should fire, and where it goes wrong in production.

MeterValues is the OCPP message that carries the data your billing system, dashboards, and analytics all depend on. Every session generates a stream of MeterValues messages reporting energy delivered, current power, voltage, current, temperature, state of charge, and whatever else the charger is capable of sampling.

It’s also one of the messages where implementations vary the most. Two chargers from different OEMs charging the same vehicle at the same rate may report dramatically different MeterValues — different measurands, different frequencies, different precision. Understanding what’s being reported and how to handle the variation is essential for any team building a CSMS or working with charging data.

This article covers MeterValues in depth: structure, configuration, the common measurands, and the operational issues that cause real-world support tickets. If you’re new to the protocol, start with what OCPP is and how it works; for the message that opens the connection see the BootNotification message (coming soon).

The structure

A MeterValues message in OCPP 1.6:

{
  "connectorId": 1,
  "transactionId": 12345,
  "meterValue": [
    {
      "timestamp": "2026-06-26T19:15:00Z",
      "sampledValue": [
        {
          "value": "12.45",
          "context": "Sample.Periodic",
          "format": "Raw",
          "measurand": "Energy.Active.Import.Register",
          "unit": "kWh"
        },
        {
          "value": "11000",
          "context": "Sample.Periodic",
          "measurand": "Power.Active.Import",
          "unit": "W"
        }
      ]
    }
  ]
}

Two key levels:

  • meterValue is a list of “readings at a point in time” — usually one entry per sampling interval, but a single message can carry multiple readings if the charger batches them.
  • sampledValue within each reading is the list of measurands sampled at that timestamp. A charger reporting energy + power + voltage + current would have 4 entries.

Each sampledValue has:

  • value — the measurement as a string.
  • context — what kind of reading is this (more on this below).
  • format — Raw (numeric string) or SignedData (cryptographically signed).
  • measurand — what’s being measured (default: Energy.Active.Import.Register).
  • phase — which electrical phase (L1, L2, L3, N, L1-N, etc.) for AC.
  • location — where the measurement was taken (Inlet, Outlet, EV, Body, Cable).
  • unit — units (kWh, W, V, A, Celsius, Percent, etc.).

It’s a rich structure that handles a lot of variability. The downside is that two implementations might fill it in differently for the same physical reading.

flowchart TD
  MV[MeterValues message] --> R1[meterValue<br/>reading at timestamp]
  R1 --> S1[sampledValue<br/>Energy Register]
  R1 --> S2[sampledValue<br/>Power Import]
  R1 --> S3[sampledValue<br/>Voltage per phase]
  S1 --> F[value / context<br/>measurand / unit]
  style MV fill:#1e3a5f,stroke:#2b6cb0,color:#fff

The contexts

The context field tells you why this reading exists. Common values:

  • Sample.Periodic — routine sampling during a session. Most readings are this.
  • Sample.Clock — a reading triggered by the clock (e.g. on-the-minute), independent of transactions.
  • Transaction.Begin — the meter reading at the moment the transaction started. Crucial for billing — the end meter minus this is the total energy delivered.
  • Transaction.End — the meter reading at the moment the transaction stopped. The other anchor for billing.
  • Trigger — a reading sent in response to a TriggerMessage from the CSMS.
  • Other — vendor-specific, less common.

The Transaction.Begin and Transaction.End readings are arguably the most important. Periodic samples are nice for dashboards but the billing math anchors on the begin/end pair.

The measurands

OCPP defines a long list of possible measurands. The ones most commonly reported:

Energy.Active.Import.Register — cumulative energy delivered to the EV in this session (or sometimes lifetime), in kWh. This is THE measurand for billing.

Power.Active.Import — instantaneous power being delivered, in W.

Voltage — instantaneous voltage. Often reported per phase for AC.

Current.Import — instantaneous current. Often per phase.

Frequency — grid frequency (50 Hz in Europe, 60 Hz in North America, sometimes notable for power-quality monitoring).

Temperature — temperature in degrees Celsius. Could be the charger body, the inlet, the cable, or vehicle-reported battery temperature depending on location.

SoC (State of Charge) — battery percentage. Only reported when the vehicle communicates this via ISO 15118 (coming soon) or a similar mechanism.

Less common but useful:

Energy.Reactive.Import.Register — reactive power. Relevant for industrial billing.

Power.Reactive.Import — instantaneous reactive power.

Energy.Active.Export.Register / Power.Active.Export — energy/power flowing FROM the vehicle (V2G). Important for bidirectional charging.

RPM — only relevant for vehicles with reported drivetrain data (rare).

Distance — vehicle odometer if reported.

Different chargers report different subsets. A budget AC charger might only report Energy.Active.Import.Register. A modern DC fast charger might report 10-15 measurands per sample.

Sampling configuration

The CSMS controls how often MeterValues fire via configuration variables. Key ones in OCPP 1.6:

  • MeterValueSampleInterval — how often to sample during a session, in seconds. 0 means “don’t sample periodically.” Typical values: 30, 60, 300.
  • MeterValuesSampledData — comma-separated list of measurands to sample. E.g. “Energy.Active.Import.Register,Power.Active.Import,Voltage,Current.Import”.
  • ClockAlignedDataInterval — how often to send Sample.Clock readings (independent of any transaction), in seconds. 0 means “don’t.”
  • MeterValuesAlignedData — what to sample for Sample.Clock readings.
  • StopTxnSampledData — what to sample at transaction end.

In OCPP 2.0.1 the names are slightly different (and tied into the device model) but the concepts are equivalent.

You set these via SetConfiguration (1.6) or SetVariables (2.0.1). The defaults vary by hardware OEM — some come with sensible defaults, others come with everything off and you have to configure from scratch.

Sample intervals: what to choose

A practical guide.

For pure billing: 60 seconds is plenty. Some operators use 300 seconds. You really only need begin + end + occasional snapshots for trust/verification.

For real-time dashboards: 10-30 seconds. Users watching their session in an app want to see the energy counter tick up.

For high-frequency analytics (rare): 1-5 seconds. Only useful for engineering analysis or grid services.

For demand-response participation: depends on the program. Some require 1-second granularity, some accept 1-minute.

Faster sampling = more bandwidth, more storage, more processing. For a fleet of 1000 chargers each sampling every 10 seconds for 8 hours of charging per day, you’re generating ~3 million sample events per day. Storage and indexing add up.

A sensible default: 60-second periodic samples plus Transaction.Begin and Transaction.End. Adjust upward only when there’s a specific need.

The phase tagging issue

For AC three-phase chargers, MeterValues should report per-phase data. The phase field accepts:

  • L1, L2, L3 — individual line phases.
  • N — neutral.
  • L1-N, L2-N, L3-N — line-to-neutral measurements.
  • L1-L2, L2-L3, L3-L1 — line-to-line measurements.

Some chargers report per-phase voltage and current; others only report aggregate / line-to-neutral; some report L1 only and treat it as representative.

For accurate per-phase analytics (power factor, phase balance), you need per-phase data. Many operators discover late that their charger reports only aggregate and they can’t reconstruct what they wanted.

When evaluating hardware, ask about per-phase reporting if you care.

The OCPP 2.0.1 TransactionEvent model

OCPP 2.0.1 changed how meter data flows. Instead of separate MeterValues messages during a transaction, meter readings are embedded inside TransactionEvent messages of type “Updated”:

{
  "eventType": "Updated",
  "transactionInfo": { "transactionId": "abc123" },
  "timestamp": "2026-06-26T19:15:00Z",
  "meterValue": [ ... same structure as 1.6 ... ]
}

The data is identical; the message envelope is different. The benefit of the 2.0.1 approach is unification — all transaction-related events (start, update, end, energy data) flow through one message type. The downside is that legacy code transitioning from 1.6 needs to rewire its MeterValues handler.

OCPP 2.0.1 also adds:

  • Aligned data can flow through TransactionEvent or a separate MeterValuesRequest (the standalone message still exists for non-transactional readings).
  • Tariff information can flow alongside meter data, useful for in-session display of cost.

Common operational issues

A list of things that go wrong with MeterValues in production.

Missing measurands. Charger doesn’t report what you expected. Fix at provisioning: set MeterValuesSampledData explicitly per charger model.

Cumulative energy resets. Some chargers reset their session energy counter at session start (giving you 0 at begin, X at end). Some report a lifetime accumulator (so begin is 12000.0, end is 12012.4). Don’t assume one or the other; use both Transaction.Begin and Transaction.End anchors.

Sampling stops mid-session. Charger disconnects from the network, no MeterValues for several minutes, then they resume. The data has gaps. Your billing must handle this — use Transaction.End as ground truth, not the sum of periodic samples. (See Heartbeat and the connection lifecycle (coming soon) for how these drops are detected.)

Time precision varies. Some chargers send timestamps to the millisecond. Some only to the second. Don’t depend on sub-second precision for ordering.

Inconsistent units. Spec says kWh for energy and W for power. Some chargers send Wh or kW instead. Validate units on ingest; convert if needed.

Phase aggregation confusion. Charger reports L1 only, you assume it’s three-phase aggregate. Your per-phase analytics are wrong.

Pre-paid metering issues. Some chargers have an internal “pre-paid” counter that drifts from the actual meter. If you bill on the OCPP-reported value, you may overpay or underpay. For pre-paid charger fleets, special handling needed.

Vendor-specific extensions. Some OEMs add custom measurands not in the standard. Your CSMS should accept and ignore unknown measurands gracefully rather than crashing.

Auditability and regulated markets

In jurisdictions with calibration/measurement regulation (Germany’s MessEV, similar elsewhere), MeterValues need to be:

  • Sourced from a calibrated meter inside the charger.
  • Reported with the SignedData format for tamper-evidence.
  • Accompanied by appropriate metadata (calibration date, public key, etc.).

OCPP’s SignedData support handles this. The implementation complexity is significant — you need a proper PKI for the charger’s signing key, you need the back-end to validate signatures, you need the audit trail to be queryable for regulatory inspection.

For non-regulated markets, regular Raw-format MeterValues are sufficient. Don’t add SignedData complexity unless you need it.

Storage and analytics design

If you’re building a CSMS, the data design around MeterValues matters.

Hot store: recent samples (last 30 days), indexed for per-session queries. PostgreSQL with proper indexes is fine here.

Warm store: aggregated daily/hourly summaries by charger, by session, by tariff. Often a separate analytical store (ClickHouse, BigQuery, Snowflake).

Cold store: raw samples archived for compliance and rare audit needs. S3 or similar object store.

Don’t keep raw samples forever in your hot store. A 100-charger fleet with 30-second sampling generates ~10 GB per year of raw sample data. A 10,000-charger fleet is 1 TB+ per year. Aggregate, archive, expire.

Use time-series databases for monitoring. InfluxDB, TimescaleDB, or similar are well-suited for the high-write, time-indexed nature of meter samples.

The honest summary

MeterValues is one of the messages where the spec is fine and the implementation variation is large. Plan for charger-to-charger differences in what’s reported and how often. Anchor billing on Transaction.Begin and Transaction.End, treat periodic samples as supplementary data for dashboards and analytics. Set sample intervals based on your actual operational needs, not maximum granularity. Most “billing is wrong” tickets in EV charging trace back to MeterValues handling — invest in this layer carefully.

Quick check

Q1. Which measurand is the primary field for billing energy delivered?
Q2. Why might summing Sample.Periodic values give a different total than end meter minus start meter?
Q3. How does OCPP 2.0.1 transport meter readings during a transaction?
Q4. What is the recommended way to handle a mid-session gap where MeterValues stopped arriving?
Q5. When is SignedData format for MeterValues genuinely required?

Frequently asked questions

How often should MeterValues fire during a session?

For most billing purposes, every 30-60 seconds is plenty. Some operators sample faster (5-15 seconds) for real-time dashboards. The CSMS controls the interval via configuration (MeterValueSampleInterval in 1.6). Faster is more data, more storage, more bandwidth — diminishing returns past ~10s sampling for most use cases.

Which measurands are commonly reported?

Energy.Active.Import.Register (cumulative energy delivered, the most important field), Power.Active.Import (current power), Voltage and Current per phase, Temperature, SoC (if reported by the vehicle via ISO 15118 or the charger). Some chargers report 10+ measurands, some report 2-3.

Why does my session show less energy than the meter reading shows?

Usually a reading-context confusion. The total session energy is end-meter minus start-meter, both with context Transaction.Begin and Transaction.End. If you sum Sample.Periodic values you may get a different number because sampling can miss the very start and end of the session.

What is the difference between MeterValues in OCPP 1.6 and TransactionEvent in 2.0.1?

In 1.6, MeterValues is a separate message sent during a transaction. In 2.0.1, meter readings are embedded inside TransactionEvent messages of type Updated. The data is similar; the message envelope is different. The 2.0.1 model is cleaner because all transaction-related events flow through one message type.

Found this useful? Share it.