Valley.Net.Protocols.MeterBus
M-Bus (meter bus, meterbus, mbus) C# project for communicating and parsing M-Bus (EN 13757-2 and EN 13757-3)
Install / Use
/learn @sympthom/Valley.Net.Protocols.MeterBusREADME
Valley.Net.Protocols.MeterBus
A modern .NET 10 library for M-Bus (Meter Bus) communication and frame parsing over TCP, UDP, and serial. Implements the EN 13757-2 (physical and link layer) and EN 13757-3 (application layer) standards.
What is M-Bus?
M-Bus (Meter-Bus) is a European standard for the remote reading of utility meters such as gas, water, and electricity. It is designed for cost-effective two-wire communication and supports both wired (EN 13757-2/3) and wireless (EN 13757-4) variants.
Typical use cases include:
- Remote reading of utility meters in residential and commercial buildings
- Centralized data collection via gateways or hand-held readers
- Alarm systems, heating control, and building automation
Architecture
┌──────────────────────────────────────────────────────────┐
│ IMBusMaster │
│ PingAsync, RequestDataAsync, ScanAsync, etc. │
├──────────────────────────────────────────────────────────┤
│ IPacketMapper │ IFrameParser / IFrameSerializer│
│ LongFrame → Packet │ bytes ↔ MBusFrame records │
├──────────────────────────────────────────────────────────┤
│ IMBusTransport │
│ ConnectAsync, SendFrameAsync, ReceiveFrameAsync │
├──────────┬──────────────┬────────────────────────────────┤
│ TCP │ UDP │ Serial │
│ Pipes │ Socket │ System.IO.Ports + PipeReader │
└──────────┴──────────────┴────────────────────────────────┘
Project Layout
| Project | Description |
|---------|-------------|
| Valley.Net.Protocols.MeterBus.Abstractions | Interfaces, record types, enums -- zero dependencies |
| Valley.Net.Protocols.MeterBus | Core implementation: FrameParser, FrameSerializer, PacketMapper, VifLookupService, MBusMaster |
| Valley.Net.Protocols.MeterBus.Transport.Tcp | TCP transport using System.IO.Pipelines |
| Valley.Net.Protocols.MeterBus.Transport.Udp | UDP transport using Socket |
| Valley.Net.Protocols.MeterBus.Transport.Serial | Serial transport wrapping System.IO.Ports with PipeReader |
Prerequisites
- .NET 10 SDK or later
Installation
dotnet add package Valley.Net.Protocols.MeterBus
dotnet add package Valley.Net.Protocols.MeterBus.Transport.Tcp # or .Udp / .Serial
Usage
With Dependency Injection
services.AddMBusCore();
services.AddSingleton<IMBusTransport>(sp =>
new TcpMBusTransport("192.168.1.135", 502));
Retrieving meter telemetry
await using var transport = new TcpMBusTransport("192.168.1.135", 502);
await transport.ConnectAsync();
await using var master = new MBusMaster(
transport,
new FrameParser(),
new FrameSerializer(),
new PacketMapper(new VifLookupService()));
var packet = await master.RequestDataAsync(0x0a);
if (packet is VariableDataPacket vdp)
{
Console.WriteLine($"Device: {vdp.DeviceType}, Records: {vdp.Records.Length}");
foreach (var record in vdp.Records)
Console.WriteLine($" {record.Units[0].Quantity}: {record.Value}");
}
Scanning for devices
var addresses = Enumerable.Range(0, 250).Select(i => (byte)i);
await foreach (var meter in master.ScanAsync(addresses))
{
Console.WriteLine($"Found meter at address {meter.Address}");
}
Parse a raw M-Bus frame
var parser = new FrameParser();
var mapper = new PacketMapper(new VifLookupService());
var bytes = "68 31 31 68 08 01 72 45 58 57 03 B4 05 ..."
.HexToBytes();
var frame = parser.Parse(bytes);
if (frame.IsSuccess)
{
var packet = mapper.MapToPacket(frame.Value!);
// Use packet...
}
Design Principles
- Immutable data -- All frames and packets are C#
recordtypes - Explicit errors --
MBusParseResult<T>instead of exceptions for parsing - Async-first --
CancellationTokeneverywhere,IAsyncEnumerablefor scanning - Zero external dependencies -- Abstractions project has no NuGet dependencies
- Dependency Injection -- All services are injectable via
IServiceCollection.AddMBusCore() - Span-based parsing --
ReadOnlySpan<byte>for zero-allocation frame parsing
Building from source
dotnet restore Valley.Net.Protocols.MeterBus.sln
dotnet build Valley.Net.Protocols.MeterBus.sln --configuration Release
dotnet test Valley.Net.Protocols.MeterBus.sln --configuration Release
Changelog
v3.0.0
- Full architectural rewrite -- Multi-project solution with clean separation of concerns
- Replaced
Valley.Net.Bindingsdependency with nativeIMBusTransportabstraction - Immutable
recordtypes for all frames (MBusFrame) and packets (MBusPacket) MBusParseResult<T>result type for explicit success/failure instead of exceptionsReadOnlySpan<byte>-basedFrameParserreplacingBinaryReader-basedMeterbusFrameSerializer- Consolidated VIF/VIFE/VIFE_FB/VIFE_FD into single
VifLookupServicewithFrozenDictionary - Async-first
MBusMasterwithCancellationTokenandIAsyncEnumerable<MeterInfo>scanning - TCP transport using
System.IO.Pipelinesfor frame boundary detection - UDP transport using raw
Socket - Serial transport wrapping
System.IO.PortswithPipeReader IServiceCollection.AddMBusCore()for DI registration- 168 unit tests using MSTest 4.x with
[DynamicData]against 80+ real meter hex files - Separate integration test project
v2.0.0
- Upgraded to .NET 10 (from .NET Standard 2.0 / .NET Framework 4.6.1)
- Added GitHub Actions CI/CD workflows (build, test, NuGet publish)
- Fixed critical bug in
SelectSlave(InvalidCastException at runtime) - Fixed event handler memory leak in
MBusMaster - Implemented
SelectSlavesecondary address padding logic - Removed ~800 lines of dead/commented-out code
- Extracted
IValueInformationFieldinterface for VIF/VIFE types - Cached VIF/VIFE dictionary lookups for improved performance
- Extracted
MBusMastercommunication pattern into reusable helper - Refactored VIFE if/else chain to table-driven approach
- Extracted value parsing into dedicated
ValueParserclass - Consolidated magic numbers into
Constants.cs - Consolidated duplicate
LengthsInBitsTable - General code cleanup and modernization
v1.0.2 (2019.10.13)
- Serial communication capability
v1.0.1 (2019.10.12)
- Bug fixes
v1.0.0 (2018.09.29)
- Initial release
License
This project is licensed under the MIT License. See LICENSE for details.
Related Skills
node-connect
348.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.9kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
348.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
348.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
