SkillAgentSearch skills...

ModbusRx

ModbusRx is a modern, reactive implementation of the Modbus protocol for .NET applications. Built on the foundation of NModbus4 and leveraging Reactive Extensions (Rx.NET), it provides a powerful, observable-based API for industrial communication scenarios with comprehensive support for all major Modbus variants.

Install / Use

/learn @ChrisPulman/ModbusRx
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

ModbusRx - A Reactive Modbus Implementation

License Build Nuget NuGet

Alt

<p align="left"> <a href="https://github.com/ChrisPulman/ModbusRx"> <img alt="ModbusRx" src="https://github.com/ChrisPulman/ModbusRx/blob/main/Images/ModbusRx.png" width="200"/> </a> </p>

Overview

ModbusRx is a modern, reactive implementation of the Modbus protocol for .NET applications. Built on the foundation of NModbus4 and leveraging Reactive Extensions (Rx.NET), it provides a powerful, observable-based API for industrial communication scenarios with comprehensive support for all major Modbus variants.

Key Features

  • 🔧 Full Modbus Protocol Support: RTU, ASCII, TCP, and UDP variants
  • ⚡ Reactive Design: Built with Rx.NET for responsive, event-driven applications
  • 🏭 Master/Slave Architecture: Complete client and server implementations
  • 🚀 High Performance: Optimized for speed with memory-efficient operations
  • ✅ Comprehensive Testing: Extensive unit and integration test coverage
  • 📊 Advanced Simulation: Built-in simulation capabilities for testing and development
  • 📡 Connection Management: Automatic reconnection and health monitoring
  • 🔄 Data Type Conversions: Built-in support for float, double, and custom data types
  • 🛠️ SerialPortRx: Built on CP.IO.Ports for robust, Reactive serial communication

Supported Protocols & Target Frameworks

Protocols:

  • Modbus RTU Master/Slave (Serial)
  • Modbus ASCII Master/Slave (Serial)
  • Modbus TCP Master/Slave (Ethernet)
  • Modbus UDP Master/Slave (Ethernet)
  • Modbus TCP/UDP Server with client aggregation

Target Frameworks:

  • .NET Standard 2.0 (Cross-platform compatibility)
  • .NET 8 (Long-term support)
  • .NET 9 (Latest features)
  • .NET Framework 4.8 (Legacy support)

Installation

dotnet add package ModbusRx

Or via Package Manager Console:

Install-Package ModbusRx

Quick Start Guide

1. Basic TCP Master Operations

Simple TCP Master Connection

using ModbusRx.Device;
using CP.IO.Ports;

// Create a TCP master
var client = new TcpClientRx("192.168.1.100", 502);
using var master = ModbusIpMaster.CreateIp(client);

// Read holding registers (Function Code 03)
var registers = await master.ReadHoldingRegistersAsync(
    slaveAddress: 1, 
    startAddress: 0, 
    numberOfPoints: 10);

Console.WriteLine($"Read {registers.Length} registers: [{string.Join(", ", registers)}]");

// Write a single register (Function Code 06)  
await master.WriteSingleRegisterAsync(
    slaveAddress: 1, 
    registerAddress: 0, 
    value: 12345);

// Write multiple registers (Function Code 16)
var dataToWrite = new ushort[] { 100, 200, 300, 400, 500 };
await master.WriteMultipleRegistersAsync(
    slaveAddress: 1, 
    startAddress: 10, 
    data: dataToWrite);

Advanced TCP Master with Error Handling

using ModbusRx.Device;
using CP.IO.Ports;

try
{
    var client = new TcpClientRx("192.168.1.100", 502)
    {
        ReadTimeout = 5000,   // 5 second timeout
        WriteTimeout = 5000
    };
    
    using var master = ModbusIpMaster.CreateIp(client);
    
    // Configure transport settings
    master.Transport!.ReadTimeout = 5000;
    master.Transport.Retries = 3;
    master.Transport.WaitToRetryMilliseconds = 1000;
    
    // Read all data types
    var coils = await master.ReadCoilsAsync(1, 0, 16);
    var discreteInputs = await master.ReadInputsAsync(1, 0, 16);
    var holdingRegisters = await master.ReadHoldingRegistersAsync(1, 0, 10);
    var inputRegisters = await master.ReadInputRegistersAsync(1, 0, 10);
    
    Console.WriteLine($"Coils: {string.Join("", coils.Select(c => c ? "1" : "0"))}");
    Console.WriteLine($"Discrete Inputs: {string.Join("", discreteInputs.Select(d => d ? "1" : "0"))}");
    Console.WriteLine($"Holding Registers: [{string.Join(", ", holdingRegisters)}]");
    Console.WriteLine($"Input Registers: [{string.Join(", ", inputRegisters)}]");
}
catch (ModbusException ex)
{
    Console.WriteLine($"Modbus Error: {ex.Message}");
}
catch (TimeoutException ex)
{
    Console.WriteLine($"Timeout Error: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"General Error: {ex.Message}");
}

2. Reactive Masters with Automatic Connection Management

TCP and UDP

using ModbusRx.Reactive;
using System.Reactive.Linq;

// Reactive TCP master
var tcp = Create.TcpIpMaster("192.168.1.100", 502);
var tcpSub = tcp
    .ReadHoldingRegisters(startAddress: 0, numberOfPoints: 10, interval: 1000)
    .Subscribe(result =>
    {
        if (result.error == null && result.data != null)
        {
            Console.WriteLine($"TCP: [{string.Join(", ", result.data)}]");
        }
    });

// Reactive UDP master
var udp = Create.UdpIpMaster("192.168.1.101", 502);
var udpSub = udp
    .ReadHoldingRegisters(startAddress: 0, numberOfPoints: 10, interval: 1000)
    .Subscribe(result =>
    {
        if (result.error == null && result.data != null)
        {
            Console.WriteLine($"UDP: [{string.Join(", ", result.data)}]");
        }
    });

Reactive Serial RTU/ASCII Masters

using ModbusRx.Reactive;
using System.IO.Ports;
using System.Reactive.Linq;

// Reactive Serial RTU master
var rtu = Create.SerialRtuMaster("COM3", 19200, 8, Parity.None, StopBits.One);
var rtuSub = rtu
    .ReadHoldingRegisters(slaveAddress: 1, startAddress: 0, numberOfPoints: 10, interval: 500)
    .Subscribe(result =>
    {
        if (result.error == null && result.data != null)
        {
            Console.WriteLine($"RTU: [{string.Join(", ", result.data)}]");
        }
    });


// Convenience overload (defaults slave 1)
var rtuQuickSub = rtu
    .ReadHoldingRegisters(startAddress: 0, numberOfPoints: 5, interval: 1000)
    .Subscribe();

// Reactive Serial ASCII master
var ascii = Create.SerialAsciiMaster("COM4", 9600, 7, Parity.Even, StopBits.One);
var asciiSub = ascii
    .ReadCoils(slaveAddress: 1, startAddress: 0, numberOfPoints: 8, interval: 1000)
    .Subscribe(result =>
    {
        if (result.error == null && result.data != null)
        {
            Console.WriteLine($"ASCII Coils: {string.Join("", result.data.Select(c => c ? "1" : "0"))}");
        }
    });

3. UDP Master Operations

using ModbusRx.Device;
using CP.IO.Ports;
using System.Net;

// Create UDP master
var client = new UdpClientRx();
var endPoint = new IPEndPoint(IPAddress.Parse("192.168.1.100"), 502);
client.Connect(endPoint);

using var master = ModbusIpMaster.CreateIp(client);

// UDP operations are similar to TCP
var registers = await master.ReadHoldingRegistersAsync(1, 0, 10);
Console.WriteLine($"UDP Read: [{string.Join(", ", registers)}]");

// Write coils (Function Code 15)
var coilData = new bool[] { true, false, true, true, false, false, true, false };
await master.WriteMultipleCoilsAsync(1, 0, coilData);

4. Serial RTU Master

using CP.IO.Ports;
using ModbusRx.Device;
using System.IO.Ports;

// Configure serial port
using var port = new SerialPortRx("COM1")
{
    BaudRate = 9600,
    DataBits = 8,
    Parity = Parity.None,
    StopBits = StopBits.One,
    Handshake = Handshake.None
};

await port.OpenAsync();

// Create RTU master
using var master = ModbusSerialMaster.CreateRtu(port);

try
{
    // Read coils
    var coils = await master.ReadCoilsAsync(
        slaveAddress: 1, 
        startAddress: 0, 
        numberOfPoints: 16);
    
    Console.WriteLine($"Coils: {string.Join("", coils.Select(c => c ? "1" : "0"))}");
    
    // Read/Write multiple registers (Function Code 23)
    var writeData = new ushort[] { 1000, 2000, 3000 };
    var readData = await master.ReadWriteMultipleRegistersAsync(
        slaveAddress: 1,
        startReadAddress: 0,
        numberOfPointsToRead: 5,
        startWriteAddress: 10,
        writeData: writeData);
    
    Console.WriteLine($"Read/Write Result: [{string.Join(", ", readData)}]");
}
finally
{
    await port.CloseAsync();
}

5. Serial ASCII Master

using CP.IO.Ports;
using ModbusRx.Device;
using System.IO.Ports;

// Configure serial port for ASCII
using var port = new SerialPortRx("COM2")
{
    BaudRate = 9600,
    DataBits = 7,           // ASCII typically uses 7 data bits
    Parity = Parity.Even,   // ASCII typically uses even parity
    StopBits = StopBits.One
};

await port.OpenAsync();

// Create ASCII master
using var master = ModbusSerialMaster.CreateAscii(port);

// ASCII operations are identical to RTU in terms of function calls
var registers = await master.ReadHoldingRegistersAsync(1, 0, 10);
Console.WriteLine($"ASCII Read: [{string.Join(", ", registers)}]");

Advanced Server Implementations

1. Creating a Comprehensive Modbus Server

using ModbusRx.Device;
using ModbusRx.Data;

// Create and configure a server
using var server = new ModbusServer();

// Start multiple protocol endpoints
var tcpSubscription = server.StartTcpServer(502, unitId: 1);
var udpSubscription = server.StartUdpServer(503, unitId: 1);

// Enable simulation mode for testing
server.SimulationMode = true;

// Start the server
server.Start();

// Load initial test data
server.LoadSimulationData(
    holdingRegisters: new ushort[] { 1, 2, 3, 4, 5, 100, 200, 300, 400, 500 },
    inputRegisters: new ushort[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 },
    coils: 
View on GitHub
GitHub Stars20
CategoryCustomer
Updated8d ago
Forks3

Languages

C#

Security Score

95/100

Audited on Mar 15, 2026

No findings