功能强大的Modbus从站模拟器使用说明

简介

本模拟器是一个 Modbus 从站应用程序,可提供模拟多个从站。您可以用它在从供应商处收到实际的从站设备之前,即可开始编程和测试。

下载地址:Modbus Slave Emulator - Modbus 从站模拟器

主要功能

  • 支持多种Modbus协议,包括:
    • Modbus RTU
    • Modbus ASCII
    • Modbus TCP/IP
    • Modbus UDP/IP
    • Modbus RTU Over TCP/IP
    • Modbus RTU Over UDP/IP
  • 支持按照业务类型对寄存器进行分组管理。
  • 支持自定义C#脚本,动态返回数据。
  • 监视串行线路或以太网上的通信数据。
  • 支持多达 28 种数据格式,包括:Signed、Unsigned、Hex、Binary、Long、Float、Double、String等。
  • 支持 Modbus 协议地址和 PLC 地址相互切换。
  • 对任意数量寄存器中的数据绘制实时图表,监控数据的变化趋势。
  • 支持同时创建多个连接网络和大量从站设备。
  • 通过多个标签页进行管理,快速在从站设备之间进行切换。
  • 通过表格的形式管理寄存器,支持添加变量名和注释,支持切换背景与前景颜色。
  • 导出/导入从站设备寄存器数据到Excel。
  • 内置字节转换工具,方便将Long、Float、Double类型数据转换为寄存器中的数据。
  • 内置数据校验工具,支持 CRC、LRC 校验。
  • 通过缓动函数模拟寄存器值的变化,带来更真实的数据仿真体验。
  • 支持丰富的 Modbus 功能码:
    • 01 (0x01) 读取线圈
    • 02 (0x02) 读取离散输入
    • 03 (0x03) 读取保持寄存器
    • 04 (0x04) 读取输入寄存器
    • 05 (0x05) 写单个线圈
    • 06 (0x06) 写单个寄存器
    • 08 (0x08) 诊断(仅限串口)
    • 11 (0x0B) 获取通讯事件计数器(仅限串口)
    • 15 (0x0F) 写入多个线圈
    • 16 (0x10) 写入多个寄存器
    • 17 (0x11) 报告服务器 ID(仅限串口)
    • 22 (0x16) 掩码写入寄存器
    • 23 (0x17) 读/写多个寄存器
    • 43 / 14 (0x2B / 0x0E) 读取设备标识

安装程序

Windows

在 Windows 上,安装本软件非常简单,双击安装程序,并选择合适的安装目录,创建桌面快捷方式,之后,程序便会正确安装在你的电脑上。

macOS

要在macOS上安装,下载最新的.dmg包,双击打开安装程序,将软件拖动到下方的程序目录即可。如果在安装时出错或者在运行软件时出错,请继续阅读下面的处理方式:

  • 安装时报错

如果在安装时报错:"已损坏,无法打开。 您应该将推出磁盘映像"。因为当前版本未进行签名,被 GateKeeper 机制拦截。通过以下步骤来暂时关闭 GateKeeper(安装后再打开) :

\begin{enumerate} 1. 在"系统偏好设置" -> "安全与隐私" 中选择 "任何来源"; 如果您的系统没有此选项,在终端中执行以下命令后再次打开此设置页,然后勾选"任何来源",参考图\ref{fig-mac}:

复制代码
sudo spctl --master-disable
  1. 之后再双击安装软件即可。 1. (可选)安装完成之后,您可以还原之前的设置,这会保护您的Mac免受恶意软件的骚扰,通过以下命令打开 GateKeeper:sudo spctl --master-enable
  • 运行时报错

如果在运行时报错:"已损坏,无法打开。 您应该将它移到废纸篓"。是因为当前版本未进行签名,被Mac GateKeeper机制拦截;打开终端输入以下命令来解除对本软件的隔离:

复制代码
$ sudo xattr -rd com.apple.quarantine /Applications/Modbus\ Slave\ Emulator.app

如果提示没有 -r 选项,检查调用的命令是否正确:

复制代码
$ which -a xattr
/Library/Frameworks/Python.framework/Versions/3.11/bin/xattr
/usr/bin/xattr

确保使用第二个命令

复制代码
$ sudo /usr/bin/xattr -rd com.apple.quarantine /Applications/Modbus\ Slave\ Emulator.app

Linux

下载最新的压缩包,然后执行以下步骤:

  1. 解压压缩包:

    复制代码
    $ tar -zxvf ModbusSlaveEmulator-1.0.0.0-linux-x64.tar.gz
  • 设置语言环境(或略此步骤可能导致软件启动报错):

    复制代码
    $ export LANG=en_US.UTF-8
  • 为文件添加可执行权限:

    复制代码
    $ chmod +x ./ModbusSlaveEmulator-1.0.0.0-linux-x64/ModbusSlaveEmulator
  • 最后运行程序:

    复制代码
    ./ModbusSlaveEmulator-1.0.0.0-linux-x64/ModbusSlaveEmulator

注意,请将版本号(上文中的1.0.0.0)替换为你实际下载的版本号。

创建连接

第一步是创建连接,选择"新建连接",在新建连接窗口中,可以配置要使用规约类型。共有五种不同的连接方式:

  • 串口:通过串口线(RS232 或 RS485)进行 Modbus 通信。可以使用 USB 串口转换器。
  • Modbus TCP/IP:如果需要与 Modbus TCP/IP 网络通信,请选择 TCP/IP。在这种情况下,从站 ID 与 Modbus TCP/IP 中使用的设备 ID 相同。默认端口号为 502。
  • Modbus UDP/IP:如果需要与 Modbus UDP/IP 网络通信,请选择 UDP/IP。它与 Modbus TCP/IP 类似,但使用无连接的 UDP 协议。
  • Modbus RTU over TCP/IP:通过 TCP/IP 网络而非串口线发送 RTU 消息。
  • Modbus RTU over UDP/IP:通过 UDP/IP 网络而非串口线发送 RTU 消息。

串口配置

这些参数用于配置串口设置,仅当连接类型为"串口"时可用。

  • 串口号:当前系统已存在的物理或虚拟串口号
  • 波特率:串口波特率,通信双方必须一致
  • 数据位:通信过程中传输的有效数据位数,数据位通常有5、6、7 、8
  • 奇偶校验位
  • 停止位
  • 流控
  • 消息模式:使用此选项选择 RTU 或 ASCII 模式。默认值为 RTU。

TCP/UDP配置

  • IP 地址:指定要监听的 IP 地址。默认值为 127.0.0.1。
  • 端口号:指定要监听的端口号。默认值为 502。

注意:服务器IP地址是指本模拟器绑定的本机地址,使用127.0.0.1或者本机的其他IP地址。要查看本机的IP地址,启动控制台,然后使用 ipconfig 命令:

复制代码
PS C:\Users\Alice> ipconfig
无线局域网适配器 WLAN:
   连接特定的 DNS 后缀 . . . . . . . :
   IPv4 地址 . . . . . . . . . . . . : 192.168.31.166
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   默认网关. . . . . . . . . . . . . : 192.168.31.1

添加或删除从站

你可以在模拟器中添加多个modbus从站。每个从站将独立工作。也可以删除从站。创建完连接之后,选中创建的连接,然后点击"创建从站",在"创建从站"窗口中,可以配置从站别名,从站ID,并配置寄存器组。

点击加号或减号,可以添加或删除寄存器组。在每个组中,可以配置组名、寄存器的类型、寄存器的起始地址和数量。创建完成后,点击确定,即可创建从站。

  • 从站 ID:范围从1 至 255。
  • 响应延迟:响应主站每个请求前的延迟毫秒数。默认值为 0 毫秒。
  • 使用随机值初始化寄存器:如果设置为 true,寄存器将使用随机值进行初始化。默认值为 false。
  • 地址类型:Modbus 协议地址(基数 0),PLC 地址(基数 1)
  • 配置寄存器组:您可以一次添加多个寄存器组;唯一的要求是组内寄存器地址不能重叠。
    • 组名称:设置寄存器组的名称。
    • 寄存器类型,您可以选择以下四种类型之一:
      • 01:线圈 (0x),可读写
      • 02:离散输入 (1x),只读
      • 03:保持寄存器 (4x),可读写
      • 04:输入寄存器 (3x),只读
  • 起始地址:组中第一个寄存器的地址。
  • 数量:该组寄存器的总数。

对于创建好的从站,你可以导出从站的所有寄存器组,具体操作为,在从站上右击,在弹出菜单中选择"导出从站数据",即可将本从站的所有寄存器组导入到Excel文件。在导出的Excel文件中,您可以编辑变量名、寄存器值,新建或删除寄存器组,然后重新导入到软件,操作为,在从站上右击,在弹出菜单中选择"导入从站数据",这是在软件中手动创建寄存器组的另一替代方式。

寄存器组

创建完从站之后,双击组名即可打开该寄存器组

  • 在变量名列,通过双击单元格即可编辑变量名。
  • 在寄存器值列,通过选中一行或多行(Ctrl+鼠标左键),在下方编辑寄存器值。
  • 在右击的弹出菜单中,可以配置寄存器值的编码格式,数组自动变化。

对于寄存器组中寄存器地址的格式,软件支持以十六进制或十进制显示。在从站上右击,在弹出菜单中选择寄存器地址的显示格式。

更新数据

Modbus从站模拟器支持多种编码格式:

  • 16 位有符号整数
  • 16 位无符号整数
  • 十六进制字节数组
  • 二进制位串
  • 32 位有符号整数
  • 32 位无符号整数
  • 32 位浮点数
  • 64 位双精度浮点数
  • 最常用编码的字符串

通过选择一个或多个寄存器,您可以方便的编辑这些值。

  • 当选中单个寄存器时(表格中的一行),Unsigned-Signed-Hex-Binary标签页和String标签页可用,在Unsigned-Signed-Hex-Binary标签页中,将单个寄存器解释为有符号十六进制数、无符号十六进制数、原始的2字节十六进制数组、16位的位串。选中任何一个输入框,即可编辑该类型的值。String标签页同理,您可以直接输入字符串,软件将自动寄存器的值。
  • 当选中连续的两个寄存器,Long标签页和Float标签页可用,两个寄存器的组合将被解释为32位有符号数或者32位浮点数。
  • 当选中连续的两个寄存器,Double标签页可用,四个寄存器的组合将被解释为64位浮点数。

实时绘图

选择一行或多行寄存器,然后从右键菜单中选择实时图表选项。共有五种图表选项:

  • 作为 unsigned 绘制
  • 作为 signed 绘制
  • 作为 long 绘制
  • 作为 float 绘制
  • 作为 double 绘制

通信报文

选中连接,然后点击"通信细节",这里我们可以监控Modbus通信的报文,

从站脚本

在Modbus Slave Emulator中,可以使用自定义C#脚本。选中从站,右击,在弹出菜单中选择"编辑脚本"。编写脚本时,你需要实现下面这个抽象类,当主站(客户端)发起读写寄存器请求时,执行脚本中对应的方法。

复制代码
namespace ModbusSlaveEmulator.Script
{
    public abstract class DefaultHandler
    {
        // 下面四种数组,代表了用户在UI上配置的每种类型的寄存器
        // 从站配置了多个就访问多少,数组索引不要超限
        // 比如,配置保持寄存器,起始地址10 (base 0),数量为100
        // 那么应该使用HoldingRegisters[10],HoldingRegisters[11],...,HoldingRegisters[109]
        public ModbusSlaveEmulator.Script.ReadOnlyArray<bool> Coils; //线圈状态
        public ModbusSlaveEmulator.Script.ReadOnlyArray<bool> DiscreteInputs; //离散输入状态
        public ModbusSlaveEmulator.Script.ReadOnlyArray<ushort> HoldingRegisters; //保持寄存器
        public ModbusSlaveEmulator.Script.ReadOnlyArray<ushort> InputRegisters; //输入寄存器

        /// <summary>
        /// 读线圈状态
        /// </summary>
        /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
        /// <param name="numberOfPoints">读取的数量</param>
        /// <returns>返回的数组长度应该为numberOfPoints</returns>
        public virtual bool[] ReadCoils(ushort startAddress, ushort numberOfPoints)
        {
            return null;
        }

        /// <summary>
        /// 写线圈状态
        /// </summary>
        /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
        /// <param name="points">要写入的数据</param>
        public virtual void WriteCoils(ushort startAddress, bool[] points)
        {
        }

        /// <summary>
        /// 读离散输入状态
        /// </summary>
        /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
        /// <param name="numberOfPoints">读取的数量</param>
        /// <returns>返回的数组长度应该为numberOfPoints</returns>
        public virtual bool[] ReadDiscreteInputs(ushort startAddress, ushort numberOfPoints)
        {
            return null;
        }

        /// <summary>
        /// 读保持寄存器
        /// </summary>
        /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
        /// <param name="numberOfPoints">读取的数量</param>
        /// <returns>返回的数组长度应该为numberOfPoints</returns>
        public virtual ushort[] ReadHoldingRegisters(ushort startAddress, ushort numberOfPoints)
        {
            return null;
        }

        /// <summary>
        /// 写保持寄存器
        /// </summary>
        /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
        /// <param name="points">要写入的数据</param>
        public virtual void WriteHoldingRegisters(ushort startAddress, ushort[] points)
        {
        }

        /// <summary>
        /// 读输入寄存器
        /// </summary>
        /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
        /// <param name="numberOfPoints">读取的数量</param>
        /// <returns>返回的数组长度应该为numberOfPoints</returns>
        public virtual ushort[] ReadInputRegisters(ushort startAddress, ushort numberOfPoints)
        {
            return null;
        }
    }
}

下面是一个实现上面接口的例子,注意:DefaultHandler的实现类必须命名为Handler:

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;

/// <summary>
/// ModbusSlaveEmulator.Script.DefaultHandler 实现示例
/// </summary>
public class Handler : ModbusSlaveEmulator.Script.DefaultHandler
{
    /// <summary>
    /// 读线圈状态
    /// </summary>
    /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
    /// <param name="numberOfPoints">读取的数量</param>
    /// <returns>返回的数组长度应该为numberOfPoints</returns>
    public override bool[] ReadCoils(ushort startAddress, ushort numberOfPoints)
    {
        bool[] array = new bool[numberOfPoints];
        for (int i = 0; i < array.Length; i++)
        {
            if (i % 2 == 0)
            {
                //返回在UI表格里配置的数据值,并取反
                array[i] = !Coils[startAddress + i];
            }
            else
            {
                array[i] = false;
            }
        }

        return array;
    }

    /// <summary>
    /// 写线圈状态
    /// </summary>
    /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
    /// <param name="points">要写入的数据</param>
    public override void WriteCoils(ushort startAddress, bool[] points)
    {
        //ignore
    }

    /// <summary>
    /// 读离散输入状态
    /// </summary>
    /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
    /// <param name="numberOfPoints">读取的数量</param>
    /// <returns>返回的数组长度应该为numberOfPoints</returns>
    public override bool[] ReadDiscreteInputs(ushort startAddress, ushort numberOfPoints)
    {
        bool[] array = new bool[numberOfPoints];
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = true;
        }

        return array;
    }

    /// <summary>
    /// 读保持寄存器
    /// </summary>
    /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
    /// <param name="numberOfPoints">读取的数量</param>
    /// <returns>返回的数组长度应该为numberOfPoints</returns>
    public override ushort[] ReadHoldingRegisters(ushort startAddress, ushort numberOfPoints)
    {
        ushort[] array = new ushort[numberOfPoints];
        for (int i = 0; i < array.Length; i++)
        {
            //返回在UI表格里配置的数据值,并+3
            array[i] = (ushort) (HoldingRegisters[startAddress + i] + 3);
        }

        return array;
    }

    /// <summary>
    /// 写保持寄存器
    /// </summary>
    /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
    /// <param name="points">要写入的数据</param>
    public override void WriteHoldingRegisters(ushort startAddress, ushort[] points)
    {
        //ignore
    }

    /// <summary>
    /// 读输入寄存器
    /// </summary>
    /// <param name="startAddress">寄存器起始地址(Protocol Address Base 0)</param>
    /// <param name="numberOfPoints">读取的数量</param>
    /// <returns>返回的数组长度应该为numberOfPoints</returns>
    public override ushort[] ReadInputRegisters(ushort startAddress, ushort numberOfPoints)
    {
        ushort[] array = new ushort[numberOfPoints];
        for (int i = 0; i < array.Length; i++)
        {
            //返回在UI表格里配置的数据值,并+1
            array[i] = (ushort) (InputRegisters[startAddress + i] + 1);
        }

        return array;
    }
}
相关推荐
会周易的程序员2 小时前
告别 matiec 与 Docker:aiDgePLC Editor 全面拥抱 STVM 字节码编译
物联网·网关·electron·软plc·iec61131·stvm·open plc
沐欣工作室_lvyiyi2 小时前
基于物联网的智慧路灯监控系统设计(论文+源码)
单片机·物联网·智能路灯
QYRdata2 小时前
权威数据披露:物联网边缘框架2026-2032年复合增长率达9.2%,增长动能凸显
物联网
sibylyue2 小时前
物联网MQTT
物联网
会周易的程序员4 小时前
aiDgeController软PLC控制通讯协议文档
c++·物联网·网关·iot·ipc·进程间通讯
做萤石二次开发的哈哈4 小时前
海康班班通交互一体机技能接入实战:ISAPI透传+OTAP双协议封装,Web/App/小程序教学管理应用一站生成
前端·物联网·小程序·交互·萤石开放平台·蓝海aiot一站式工作台·aiot开发
希艾席帝恩4 小时前
数字孪生平台与数据内容工具对比:山海鲸可视化VS镝数
大数据·人工智能·物联网·低代码·信息可视化·数字化转型
绿蕉5 小时前
输电线路的“数字孪生”守护者:支持蜂窝物联网通信的智能激光雷达点云监测装置
物联网
D_codingXuChu17 小时前
2026物联网应用开发服务商:D-coding定制开发指南
物联网·开发经验·d-coding