3、Modbus协议基础:Modbus RTU与TCP的区别、寄存器类型(线圈、离散输入、保持寄存器、输入寄存器)、功能码与数据模型

Modbus协议自1979年由Modicon(现施耐德电气)推出以来,凭借其开放、简单、可靠的特点,已成为工业自动化领域应用最广泛的通信协议之一。在ThingsBoard物联网平台接入Modbus设备时,理解RTU与TCP两种传输模式的差异、四种寄存器类型的物理含义、以及功能码与数据模型的映射关系,是完成设备接入、数据采集和控制指令下发的核心前提。本章将从协议分层、报文结构、寄存器寻址、功能码应用四个维度展开,并结合ThingsBoard网关配置的实际场景,帮助学员建立完整的Modbus协议知识框架。

Modbus协议核心知识框架 传输模式 • Modbus RTU(串行) RS-232/485,CRC16校验 1个起始位+8数据位+校验+停止 • Modbus TCP(以太网) 端口502,MBAP头,无CRC IP+TCP传输,可跨网段 → 网关需配置串口或TCP连接 寄存器数据模型 ① 线圈(Coil)— 可读写位 功能码01/05/15,地址00001-09999 ② 离散输入(Discrete Input)— 只读位 功能码02,地址10001-19999 ③ 输入寄存器(Input Register)— 只读字 功能码04,地址30001-39999 ④ 保持寄存器(Holding Register)— 读写字 功能码03/06/16,地址40001-49999 功能码与ThingsBoard映射 • 读操作:01/02/03/04 ThingsBoard attributes/timeseries • 写操作:05/06/15/16 ThingsBoard RPC命令下发 • 网关配置:Modbus Connector JSON配置映射寄存器地址 • 数据格式:INT16/UINT16/INT32 Float/BCD,字节序可配置 关键要点 • 地址映射:PLC地址(如40001)→ 协议地址(0x0000)→ ThingsBand偏移量(如40001-40001=0) • 字节序:大端/小端影响多字节数据解析,需在网关配置中指定 • 轮询策略:ThingsBoard网关按配置的pollPeriod周期性读取,写操作通过RPC触发 图例说明 传输模式:决定物理层与报文封装 寄存器模型:数据存储与访问的抽象 功能码:定义具体操作类型 注:实际设备地址范围可能因厂商而异,需查阅设备手册确认寄存器映射表。

上图展示了Modbus协议的核心知识框架。在后续内容中,我们将逐一拆解RTU与TCP的报文差异、四种寄存器的物理意义、功能码与数据模型的对应关系,并通过一个实际的温湿度传感器接入案例,演示如何在ThingsBoard中完成Modbus设备的配置与数据采集。

3.1 Modbus RTU与TCP的区别

3.1.1 核心原理:两种传输模式的本质差异

Modbus RTU(Remote Terminal Unit)和Modbus TCP(Transmission Control Protocol)是Modbus协议在两种不同物理层和传输层上的实现。RTU模式运行在串行通信链路(RS-232/RS-485)上,采用主从架构,一个主站最多可挂载247个从站设备(地址1-247,0为广播地址)。每个报文帧由地址码、功能码、数据区和CRC16校验码组成,帧间空闲时间至少为3.5个字符时间,用于帧同步。

Modbus TCP则运行在以太网TCP/IP协议栈之上,默认端口502。它去掉了CRC校验(由TCP的可靠传输保证),增加了MBAP报文头(7字节),包含事务处理标识符、协议标识符、长度和单元标识符。TCP模式支持多主站并发访问,且不受串行链路距离限制,可跨网段通信。

对比维度Modbus RTUModbus TCP
物理层RS-232/RS-485串行总线以太网(RJ45/光纤)
传输层无(直接帧传输)TCP/IP(端口502)
帧结构地址(1B)+功能码(1B)+数据(NB)+CRC16(2B)MBAP头(7B)+功能码(1B)+数据(NB)
校验方式CRC16循环冗余校验TCP校验和(无需应用层CRC)
最大从站数247个(地址1-247)理论无限制(IP地址决定)
通信方式半双工,主从轮询全双工,支持并发请求
典型应用PLC、电表、传感器(短距离)工业网关、SCADA、跨区域采集
ThingsBoard配置需配置串口参数(波特率、数据位等)需配置IP地址和端口

3.1.2 判断方法:如何选择RTU还是TCP

在实际项目中,选择RTU还是TCP主要取决于设备接口类型和现场网络条件。判断步骤如下:

  1. 查看设备硬件接口:如果设备提供RS-485/RS-232端子,则使用RTU;如果提供RJ45网口,则使用TCP。
  2. 评估通信距离与速率:RS-485在9600bps下最大传输距离约1200米,而以太网通过交换机可轻松扩展至数百米甚至跨区域。
  3. 考虑网络拓扑:RTU需要主站轮询,从站不能主动上报;TCP支持设备主动连接网关(ThingsBoard网关可配置为TCP客户端或服务端)。
  4. 检查ThingsBoard网关能力:ThingsBoard IoT Gateway的Modbus Connector同时支持RTU和TCP,但RTU需要网关具备串口资源,TCP则只需网络可达。
经验提醒:在ThingsBoard中配置RTU时,务必确认串口参数(波特率、数据位、停止位、校验位)与设备一致。常见的工业默认配置为9600-8-N-1(9600bps,8数据位,无校验,1停止位)。若设备手册未明确,可尝试扫描常见波特率(2400/4800/9600/19200)。

3.1.3 案例拆解:RTU与TCP报文对比

假设需要读取从站地址为1的设备保持寄存器地址0x0000(对应PLC地址40001)的当前值:

// Modbus RTU请求帧(读取保持寄存器,功能码03)
01 03 00 00 00 01 84 0A
│  │  │  │  │  │  └── CRC16低字节
│  │  │  │  │  └───── CRC16高字节
│  │  │  │  └──────── 读取数量(1个寄存器)
│  │  │  └─────────── 起始地址高字节(0x00)
│  │  └────────────── 起始地址低字节(0x00)
│  └───────────────── 功能码(03=读保持寄存器)
└──────────────────── 从站地址(0x01)

// Modbus TCP请求帧(相同功能)
00 01 00 00 00 06 01 03 00 00 00 01
│  │  │  │  │  │  │  │  │  │  │  └── 读取数量
│  │  │  │  │  │  │  │  │  │  └───── 起始地址低字节
│  │  │  │  │  │  │  │  │  └──────── 起始地址高字节
│  │  │  │  │  │  │  │  └─────────── 功能码
│  │  │  │  │  │  │  └────────────── 单元标识符(从站地址)
│  │  │  │  │  │  └───────────────── 长度(后续字节数=6)
│  │  │  │  │  └──────────────────── 协议标识符(0=Modbus)
│  │  │  │  └─────────────────────── 事务处理标识符高字节
│  │  │  └────────────────────────── 事务处理标识符低字节
│  │  └───────────────────────────── 协议标识符高字节(0)
│  └──────────────────────────────── 协议标识符低字节(0)
└─────────────────────────────────── 事务处理标识符(0x0001)

从上述报文可以看出,RTU帧总长为8字节,TCP帧总长为12字节。TCP的MBAP头增加了事务标识符,用于匹配请求与响应,这在并发通信中至关重要。在ThingsBoard网关的Modbus Connector配置中,RTU模式需要指定串口设备路径(如/dev/ttyS0),而TCP模式只需填写设备IP和端口。

3.1.4 常见误区与总结

风险提示:不要将Modbus RTU的从站地址与TCP的单元标识符混淆。在TCP模式下,单元标识符通常设置为0xFF或1,但某些网关设备要求必须匹配实际从站地址。此外,RTU的CRC校验是必须的,如果CRC错误,从站会丢弃请求帧,主站可能超时。

总结:RTU适合串行链路、设备数量少、距离短的场景;TCP适合以太网环境、需要高速率或跨区域采集的场景。在ThingsBoard中,两种模式均通过Modbus Connector配置,但RTU需要额外的串口资源,TCP则依赖网络连通性。

3.2 寄存器类型与数据模型

3.2.1 核心原理:四种寄存器的物理意义

Modbus协议定义了四种寄存器类型,分别对应不同的数据存储区域和访问权限。理解这四种类型是正确配置ThingsBoard数据映射的基础。

  • 线圈(Coil):可读写的位(bit)数据,用于控制输出,如继电器开关、电机启停。地址范围在PLC中通常为00001-09999,协议地址从0x0000开始。
  • 离散输入(Discrete Input):只读的位数据,用于读取开关状态,如限位开关、按钮状态。地址范围10001-19999,协议地址从0x0000开始。
  • 输入寄存器(Input Register):只读的16位(字)数据,用于读取模拟量输入,如温度传感器、压力变送器的测量值。地址范围30001-39999,协议地址从0x0000开始。
  • 保持寄存器(Holding Register):可读写的16位数据,用于读取或设置设备参数,如设定温度、PID参数、累计量等。地址范围40001-49999,协议地址从0x0000开始。
寄存器类型数据宽度读写属性PLC地址范围协议地址偏移典型功能码ThingsBoard用途
线圈(Coil)1 bit读/写00001-099990x0000-0x270F01(读)、05(写单)、15(写多)RPC控制(开关)
离散输入(Discrete Input)1 bit只读10001-199990x0000-0x270F02(读)遥信采集(状态)
输入寄存器(Input Register)16 bit只读30001-399990x0000-0x270F04(读)遥测采集(模拟量)
保持寄存器(Holding Register)16 bit读/写40001-499990x0000-0x270F03(读)、06(写单)、16(写多)遥测+遥控(参数)

3.2.2 判断方法:地址映射与偏移计算

在ThingsBoard中配置Modbus设备时,需要将PLC地址转换为协议地址。转换规则为:协议地址 = PLC地址 - 地址偏移量。例如,PLC地址40001对应的协议地址为0x0000(40001-40001=0),PLC地址30001对应的协议地址也为0x0000(30001-30001=0)。但需要注意,不同厂商的设备可能采用不同的地址映射方式,有些设备手册直接给出协议地址(如0x0000),有些则给出PLC地址(如40001)。

  1. 查阅设备手册:找到寄存器映射表,确认每个参数对应的寄存器类型和地址。
  2. 确定地址格式:如果手册给出的是40001格式,则协议地址=40001-40001=0;如果给出的是0x0000格式,则直接使用。
  3. 在ThingsBoard中配置:在Modbus Connector的JSON配置中,address字段填写协议地址(十六进制或十进制均可),registerType字段填写寄存器类型(COIL、DISCRETE、INPUT、HOLDING)。
实战场景:某温湿度传感器使用保持寄存器存储测量值,手册标注温度地址为40001,湿度地址为40002。在ThingsBoard中配置时,温度对应的协议地址为0x0000,湿度为0x0001。如果设备返回的数据为INT16类型,则温度值=原始值/10(假设精度为0.1°C)。

3.2.3 案例拆解:多寄存器数据解析

假设一个流量计使用两个连续的保持寄存器存储累计流量(32位浮点数),地址为40003和40004。在ThingsBoard中需要配置为:

{
  "deviceName": "FlowMeter",
  "attributes": [
    {"tag": "total_flow", "registerType": "HOLDING", "address": 2, "count": 2, "type": "FLOAT", "byteOrder": "BIG_BIG"}
  ]
}

这里address为2(对应PLC地址40003),count为2(读取两个寄存器),type为FLOAT(32位浮点数),byteOrder为BIG_BIG(大端字节序,即高字节在前)。如果设备采用小端字节序,则需配置为LITTLE_LITTLE。错误的字节序会导致解析出的数值完全错误,这是最常见的配置错误之一。

3.2.4 常见误区与总结

风险提示:不要混淆“输入寄存器”和“保持寄存器”。输入寄存器是只读的,通常用于模拟量输入;保持寄存器是可读写的,用于参数设置。如果错误地将保持寄存器配置为输入寄存器,ThingsBoard将无法读取数据。另外,线圈和离散输入都是位数据,但线圈可写,离散输入只读。

总结:四种寄存器类型构成了Modbus的数据模型基础。在ThingsBoard中,正确识别寄存器类型和地址偏移是数据采集的前提。建议在配置前先使用Modbus调试工具(如Modbus Poll)验证设备地址和数据类型,再迁移到ThingsBoard网关配置中。

3.3 功能码与数据模型映射

3.3.1 核心原理:功能码定义操作类型

功能码是Modbus报文中的第二个字节,用于指示主站希望从站执行的操作。常用的功能码包括:

  • 01 (0x01):读线圈状态,返回线圈的当前值(0或1)。
  • 02 (0x02):读离散输入状态。
  • 03 (0x03):读保持寄存器,可一次读取多个寄存器。
  • 04 (0x04):读输入寄存器。
  • 05 (0x05):写单个线圈,强制线圈为ON或OFF。
  • 06 (0x06):写单个保持寄存器。
  • 15 (0x0F):写多个线圈。
  • 16 (0x10):写多个保持寄存器。

在ThingsBoard的Modbus Connector中,功能码通常由registerType和操作类型(读/写)自动决定。例如,读取保持寄存器时自动使用功能码03,写入保持寄存器时使用功能码06或16。

3.3.2 判断方法:根据数据模型选择功能码

在配置ThingsBoard设备时,需要根据数据模型确定功能码:

  1. 读取位数据:如果是线圈,使用功能码01;如果是离散输入,使用功能码02。
  2. 读取字数据:如果是保持寄存器,使用功能码03;如果是输入寄存器,使用功能码04。
  3. 写入位数据:写单个线圈用05,写多个线圈用15。
  4. 写入字数据:写单个保持寄存器用06,写多个保持寄存器用16。

ThingsBoard的RPC功能允许通过服务器下发命令,例如控制继电器(写线圈)或修改设定值(写保持寄存器)。在配置RPC时,需要指定functionCode字段(如05或06),并确保设备支持相应的功能码。

3.3.3 案例拆解:ThingsBoard中配置读写操作

以下是一个完整的ThingsBoard Modbus Connector配置示例,包含读取和写入操作:

{
  "server": {
    "type": "tcp",
    "host": "192.168.1.100",
    "port": 502,
    "timeout": 5000
  },
  "devices": [
    {
      "deviceName": "PLC_1",
      "unitId": 1,
      "attributes": [
        {"tag": "temperature", "registerType": "HOLDING", "address": 0, "count": 1, "type": "INT16"},
        {"tag": "humidity", "registerType": "HOLDING", "address": 1, "count": 1, "type": "UINT16"}
      ],
      "timeseries": [
        {"tag": "pressure", "registerType": "INPUT", "address": 0, "count": 1, "type": "INT16"}
      ],
      "rpc": [
        {"tag": "setRelay", "registerType": "COIL", "address": 0, "type": "BOOL", "functionCode": 5},
        {"tag": "setSetpoint", "registerType": "HOLDING", "address": 2, "type": "INT16", "functionCode": 6}
      ]
    }
  ]
}

在这个配置中:

  • attributes:读取保持寄存器0和1,分别映射为温度和湿度,作为设备属性。
  • timeseries:读取输入寄存器0,映射为压力,作为遥测数据。
  • rpc:定义了两个RPC命令,setRelay使用功能码05写线圈,setSetpoint使用功能码06写保持寄存器。

当ThingsBoard服务器下发RPC命令时,网关会构造相应的Modbus请求帧并发送给设备。例如,调用setRelay且参数为true时,网关发送:01 05 00 00 FF 00 8C 3A(RTU模式),其中FF00表示线圈ON。

3.3.4 常见误区与总结

风险提示:功能码05和06只能操作单个线圈或寄存器,如果RPC需要同时写入多个参数,应使用功能码15或16。另外,某些设备不支持广播地址(0),在配置unitId时务必使用实际从站地址。

总结:功能码是Modbus协议的操作指令,与寄存器类型紧密关联。在ThingsBoard中,通过registerType和functionCode的组合,可以灵活实现数据采集和控制下发。建议在配置前使用Modbus调试工具验证功能码的正确性,避免因功能码不匹配导致通信失败。

3.4 综合实战:温湿度传感器接入ThingsBoard

3.4.1 场景描述与目标

某工厂需要将一台RS-485接口的温湿度传感器接入ThingsBoard平台,实现实时监控和超限报警。传感器采用Modbus RTU协议,从站地址为1,波特率9600,8数据位,无校验,1停止位。传感器寄存器映射如下:

参数寄存器类型PLC地址协议地址数据类型精度
温度保持寄存器400010x0000INT160.1°C
湿度保持寄存器400020x0001UINT160.1%RH
设备状态离散输入100010x0000BOOL-

3.4.2 实施步骤

  1. 硬件连接:将传感器RS-485 A/B线连接到ThingsBoard网关的RS-485接口,确认网关串口设备路径(如/dev/ttyUSB0)。
  2. 配置Modbus Connector:在ThingsBoard IoT Gateway的配置文件中添加Modbus连接器,设置串口参数和轮询周期。
  3. 定义设备映射:按照上述寄存器映射表,在JSON配置中定义attributes(温度和湿度)和timeseries(设备状态)。
  4. 测试通信:使用Modbus Poll工具模拟读取,确认数据正确。
  5. 部署上线:重启网关,在ThingsBoard界面查看设备数据。
// Modbus Connector配置片段
{
  "name": "modbus",
  "type": "modbus",
  "configuration": {
    "server": {
      "type": "rtu",
      "port": "/dev/ttyUSB0",
      "baudRate": 9600,
      "dataBits": 8,
      "stopBits": 1,
      "parity": "NONE",
      "timeout": 3000
    },
    "devices": [
      {
        "deviceName": "TempHumiditySensor",
        "unitId": 1,
        "pollPeriod": 5000,
        "attributes": [
          {"tag": "temperature", "registerType": "HOLDING", "address": 0, "count": 1, "type": "INT16", "multiplier": 0.1},
          {"tag": "humidity", "registerType": "HOLDING", "address": 1, "count": 1, "type": "UINT16", "multiplier": 0.1}
        ],
        "timeseries": [
          {"tag": "device_status", "registerType": "DISCRETE", "address": 0, "count": 1, "type": "BOOL"}
        ]
      }
    ]
  }
}

3.4.3 结果与复盘

配置完成后,ThingsBoard每5秒轮询一次传感器数据。温度值通过multiplier字段自动乘以0.1,得到实际的摄氏温度。设备状态作为遥测数据上报,可用于监控传感器在线状态。在实施过程中,需要注意以下几点:

  • 串口权限:确保网关用户对串口设备有读写权限(如将用户加入dialout组)。
  • 轮询周期:过短的轮询周期会增加总线负载,建议根据设备响应时间调整(一般500ms-5s)。
  • 数据精度:如果传感器返回的是无符号整数,使用UINT16类型;如果可能为负值(如温度),使用INT16类型。
经验总结:在ThingsBoard中,multiplier字段可以用于数据缩放,但要注意数据类型转换的顺序。例如,INT16原始值乘以0.1后,结果可能为浮点数,ThingsBoard会自动处理。如果设备返回的是BCD编码,则需要使用type: "BCD"进行解码。

通过本章的学习,学员应能够区分Modbus RTU与TCP的适用场景,掌握四种寄存器类型的物理含义和地址映射方法,理解功能码与数据模型的关系,并能在ThingsBoard中完成Modbus设备的配置与数据采集。在实际项目中,建议先使用调试工具验证通信,再迁移到ThingsBoard网关,以降低排障成本。