m-bus-parser
For contributing see CONTRIBUTING.md, for change history see CHANGELOG.md.
An open-source parser (decoder/deserializer) for the wired and wireless M-Bus protocol, written in Rust.
M-Bus (Meter-Bus) is a European standard (EN 13757-2 physical and link layer, EN 13757-3 application layer) for remote reading of water, gas, electricity, and heat meters. — Wikipedia
- Try it live: maebli.github.io/m-bus-parser
- Spec: m-bus.com/documentation · OMS specification
Features
- Parses wired M-Bus (EN 13757-2/-3) and wireless M-Bus (wMBus) frames
- Eight harmonized output formats:
table,json,yaml,csv,mermaid,xml,annotated, andannotated-text - A versioned canonical schema with exact decimal values, provenance, partial-decode diagnostics, and stable error codes
- Responsive, Unicode-aware tables for narrow terminals and browser cards
- AES-128 decryption for encrypted wMBus frames (mode 5 / mode 7)
no_stdcompatible — runs on embedded targets (manufacturer lookup and output formats requirestd)- Available as a Rust library, CLI, WebAssembly (npm) and Python bindings
Deployments
Web app (WebAssembly)
Paste a hex frame at maebli.github.io/m-bus-parser and get instant output in any format, including a rendered Mermaid diagram. Frames can be shared via URL.
Source: wasm/
CLI
Source: cli/
Python bindings
Source: python/
CLI Usage
m-bus-parser-cli parse [OPTIONS]
Options:
-d, --data <DATA> Raw M-Bus frame as a hex string
-f, --file <FILE> File containing a hex frame
-t, --format <FORMAT> table, json, yaml, csv, mermaid, xml, annotated, annotated-text
-k, --key <KEY> AES-128 decryption key (32 hex characters)
--width <WIDTH> Table width (auto-detected on an interactive terminal)
--no-enrichment Omit manufacturer enrichment
Input hex is strict: use compact hexadecimal or complete byte tokens separated by whitespace, colons, or hyphens.
68 3D 3D 68 ... (space-separated)
683D3D68... (plain hex)
0x68:0x3D:0x3D:0x68 (prefixed byte tokens)
Table output (default)
The table automatically selects a wide, compact, or vertical-card layout and
never exceeds the detected terminal width. Use --width 44 to request an exact
maximum explicitly.
Other formats
# JSON
# YAML
# CSV
# Mermaid diagram source (renders in the web app)
# Wired-compatible and wireless XML
# Byte annotations as JSON or human-readable text
# With AES-128 decryption key
Library Usage
Add to Cargo.toml:
[]
= { = "0.4", = ["std", "serde"] }
Parse a wired frame
use ;
use MbusData;
use parse_application_layer;
let frame_bytes: = vec!;
let frame = try_from?;
if let LongFrame = frame
Parse application-layer data records
When the link and transport headers have already been removed, parse the DIF/VIF records directly:
use parse_data_records;
let data = ;
for record in parse_data_records
Decode and render with typed APIs
use ;
let hex = "68 3D 3D 68 08 01 72 ...";
let decoded = decode_hex?;
println!;
let table = render_hex?;
serialize_mbus_data remains as a string compatibility wrapper. New code
should use the typed APIs so invalid input and unsupported options remain
machine-readable OutputError values.
no_std usage
The core parsing types are no_std compatible. Disable default features:
[]
= { = "0.4", = false }
An embedded example (Cortex-M) is in examples/cortex-m/.
Output Formats
| Format | Flag | Description |
|---|---|---|
table |
default | Width-aware human-readable table |
json |
-t json |
Canonical schema as JSON |
yaml |
-t yaml |
Canonical schema as YAML |
csv |
-t csv |
Stable tidy-record CSV |
mermaid |
-t mermaid |
Layer-oriented Mermaid flowchart |
xml |
-t xml |
Wired libmbus-compatible and wireless XML |
annotated |
-t annotated |
Byte-segment annotation envelope |
annotated-text |
-t annotated-text |
Human-readable byte annotations |
Protocol Coverage
Frame types
| Type | CI bytes | Status |
|---|---|---|
| Long frame | 0x72, 0x76, 0x7A | Supported |
| Short frame | — | Supported |
| Control frame | — | Supported |
| Single character | — | Supported |
| Wireless frame | wMBus link layer | Supported |
CI field types
Implemented
ResponseWithVariableDataStructure(CI: 0x72, 0x76, 0x7A)ResponseWithFixedDataStructure(CI: 0x73)ApplicationLayerShortTransport(CI: 0x7D)ApplicationLayerLongTransport(CI: 0x7E)ExtendedLinkLayerI(CI: 0x8A)ResetAtApplicationLevel
Not yet implemented
Returns ApplicationLayerError::Unimplemented for: SendData, SelectSlave, SynchronizeSlave, baud-rate commands, ExtendedLinkLayerII/III, COSEM/OBIS data, and various transport/network layer types.
Most common value information unit codes are supported. Contributions for additional CI types and VIF codes are welcome.
Frame Structure
Wireless Link Layer

Wired Link Layer (Long Frame)

Application Layer

Value Information Block

Related Projects
| Language | Project |
|---|---|
| C | libmbus by rscada |
| Java | jMbus |
| C# | Valley.Net.Protocols.MeterBus |
| JS | tmbus |
| Python | pyMeterBus |
