Metering protocols: DLMS/COSEM, OBIS, Modbus, MQTT (and NFE CHINT register map)
Traceable explainer: how metering data actually moves, where DLMS/COSEM, OBIS, Modbus, MQTT and HTTP each sit, and how NFE's live CHINT registers map to the OBIS standard vocabulary. Compiled 2026-08-10. Companion to "NFE metering strategy + Glen Street-EMS".
The layer cake (mental model)
Networking is layers. Each solves one problem and talks only to the layer above and below.
| Layer | Job | Examples |
|---|---|---|
| Link | move bits over one physical medium | RS485, Ethernet, WiFi |
| Network (IP) | address and route between any machines | IP |
| Transport (TCP) | reliable, ordered byte-stream between two programs | TCP, UDP |
| Application | what the two programs actually say | HTTP, MQTT, Modbus, DLMS |
Key point: MQTT, HTTPS and DLMS/COSEM are all application-layer. MQTT and HTTPS run on top of TCP (transport) on top of IP (network). MQTT is NOT a transport or network protocol.
- MQTT: application, over TCP, port 1883 (8883 with TLS)
- HTTPS: HTTP + TLS over TCP, port 443
- DLMS/COSEM: application; rides serial (HDLC) OR TCP/IP directly
- Modbus RTU (NFE's meter to Pi link): application, straight over RS485 serial, no IP at all (why it is the simplest thing on site)
Three separate jobs, often confused
- Vocabulary (what a value means): OBIS / COSEM for meters, SunSpec for inverters.
- Local interrogation (talk to one device): Modbus or DLMS.
- Messaging (move data between many nodes, up and down): MQTT or HTTP.
You put COSEM-modelled data inside an MQTT message. They are not competitors: MQTT is the envelope, COSEM is the language on the page.
MQTT vs HTTPS
- HTTPS is request/response, pull: client asks, server answers, one round trip. The server cannot easily push to a device.
- MQTT is publish/subscribe through a broker, push: a device holds one persistent TCP socket to a broker and publishes to a topic; subscribers get pushed the message instantly. Tiny 2-byte header, QoS levels, retained last value, last-will (detect a dead node), pushes both ways (telemetry up, commands down), survives flaky low-bandwidth links.
- The Street-EMS village dashboard uses MQTT: it pushes "TURN OFF SSR0" down to a specific cabinet on topic
openami/StreetPoleEMS_<id>/cmdwhile readings stream up.
MQTT over the internet? Yes, it is just TCP. Devices make an outbound connection to a broker, so no inbound port-forwarding through the site modem, ideal for field gear on cellular.
Why not DLMS/COSEM over HTTPS? It buys nothing: DLMS already has its own TCP transport and its own message-level crypto (AES-128-GCM). And DLMS is a point-to-point session with one meter, not a fan-in/out telemetry bus. So the natural split is DLMS or Modbus to talk to the meter locally, MQTT to move the results around.
DLMS/COSEM, unpacked
- COSEM (Companion Specification for Energy Metering) is the data model: every value is an object of an interface class (Register, Profile Generic for load profiles, Clock, Disconnect Control for the relay, etc.).
- OBIS (OBject Identification System) names each object with a six-group code A-B:C.D.E.F. Example:
1-0:1.8.0= total active energy imported (what you bill). - DLMS (Device Language Message Specification) is the protocol: GET an attribute, SET an attribute, ACTION a method, inside an authenticated, encrypted association.
- Standardised as IEC 62056. Certified by the DLMS User Association.
The distinction: Modbus register vs COSEM "register"
A meter does NOT contain Modbus registers and COSEM registers side by side. They are two alternative languages for the same physical measurements. A given meter speaks one (or, on high-end dual-interface meters, both via separate ports).
- Modbus register = a numbered 16-bit memory slot at an address (e.g.
0x2004). Dumb storage. It is meaningless without the vendor manual telling you "0x2004 is active power". The meaning lives in a PDF, not in the data. - COSEM Register (object) = a self-describing object that carries its own unit and scaler, addressed by a global OBIS code that means the same thing on every compliant meter. (COSEM has an interface class literally named "Register", class_id 3 - hence the word collision. It is an object, not a memory slot.)
NFE's CHINT DDSU666 / DTSU666 are Modbus-only meters: inside them there are only Modbus registers, no COSEM, no OBIS. The OBIS column below is a translation NFE applies in software (in the logger / EMS / OpenAMI layer); the meter itself never knows about OBIS. The value of doing it: the meaning stops living in the CHINT manual and becomes the industry-standard, self-describing vocabulary.
NFE CHINT register to OBIS map
Single-phase DDSU666 (tenant meters, one phase). Source: nfe-modbus-energy-logger/src/meter_reader.py and the OpenEMS Meter.Chint.DDSU666 driver.
| Quantity | Modbus register (float) | OBIS code | Meaning |
|---|---|---|---|
| Voltage | 0x2000 | 1-0:32.7.0 | Instantaneous voltage L1 |
| Current | 0x2002 | 1-0:31.7.0 | Instantaneous current L1 |
| Active power | 0x2004 | 1-0:1.7.0 | Instantaneous active power (import +) |
| Power factor | 0x200A | 1-0:13.7.0 | Power factor |
| Frequency | 0x200E | 1-0:14.7.0 | Frequency |
| Import energy | 0x4000 | 1-0:1.8.0 | Total active energy imported (billing) |
| Export energy (invert cfg) | 0x4000 | 1-0:2.8.0 | Total active energy exported |
Three-phase DTSU666 (aggregate meter_100):
| Quantity | Modbus register(s) (float) | OBIS code(s) | Meaning |
|---|---|---|---|
| Voltage L1/L2/L3 | 0x2006 (x3) | 32.7.0 / 52.7.0 / 72.7.0 | Instantaneous phase voltages |
| Current L1/L2/L3 | 0x200C (x3) | 31.7.0 / 51.7.0 / 71.7.0 | Instantaneous phase currents |
| Active power total + L1/L2/L3 | 0x2012 (x4) | 1.7.0 / 21.7.0 / 41.7.0 / 61.7.0 | Instantaneous active power |
| Power factor | 0x202A | 13.7.0 | Power factor total |
| Frequency | 0x2044 | 14.7.0 | Frequency |
| Import energy | 0x4000 | 1-0:1.8.0 | Total active energy imported |
Note: the DDSU666 driver reads active power as already scaled kW; the DTSU666 needs per-manual scaling (Table 11: V/10, I x0.001, P x0.0001, PF x0.001, F x0.01). Minor code note: the logger's DTSU REGISTERS dict lists PF at 0x2020 but read() polls 0x202A; 0x202A is the one in use.
Why two OpenEMS drivers (meter and inverter)
Two physical devices with different proprietary Modbus maps means two drivers:
- CHINT meter (
MeterChintDdsu666Impl) reads 0x2000+; implements theElectricityMeterNature. - SRNE inverter (
SrneEssImpl) reads a totally different map at 0x0100 (SoC), 0x0101 (voltage), 0x0102 (current); implements theSymmetricEss/HybridEssNatures.
OpenEMS "Natures" (ElectricityMeter, SymmetricEss) are abstract interfaces both drivers map into, so the rest of OpenEMS sees a uniform API regardless of vendor. That is the local equivalent of what OBIS/COSEM and SunSpec do industry-wide.
The real prize of the standards is not one driver for meter plus inverter. It is one driver per device TYPE that works across every compliant vendor: any DLMS meter uses the same meter driver, any SunSpec inverter the same inverter driver. That removes the per-vendor Modbus-map work NFE did for both the CHINT and the SRNE. OpenEMS Natures give that uniformity on the edge (after the driver); DLMS and SunSpec give it at the wire (removing the driver work).
How OpenAMI sits on top
OpenAMI (IEEE Smart Village) is the actionable, utility-facing telemetry layer above a village feeder. It does not reinvent meter semantics: it composes OBIS/DLMS/COSEM (meters), SunSpec (inverters/DER), MQTT (transport), IEEE 2030.5 and 1547 (DER control). Its value-add is two-way, village-edge, policy-driven control and a billing cache, run cloudlessly and cheaply.
Sources
Element room "Smart Village Energy Interest Group"; isv.wiki; nfe-modbus-energy-logger/src/meter_reader.py; OpenEMS drivers io.openems.edge.meter.chint.ddsu666 and io.openems.edge.ess.srne; IEC 62056 (DLMS/COSEM); DLMS User Association Blue Book (OBIS).