上车!速解modbus协议

Part1前言

Modbus是一种通信协议,用于在工业自动化领域中的电子设备之间进行通信。它最初是由Modicon在1979年创建的,现在已经成为一种工业标准,被广泛应用于工业控制系统、PLC(可编程逻辑控制器)和其他自动化设备。
Modbus协议主要分为三种模式:Modbus RTU、Modbus ASCII和Modbus TCP。其中,Modbus RTU是一种串行通信模式,基于二进制通信,是应用最广泛的模式之一。在RTU协议中,设备分为两种工作模式:RTU Slave Mode和RTU Master Mode。
在RTU Slave Mode下,设备被动地等待RTU Master的请求,并响应请求中包含的命令。在该模式下,设备的主要任务是接收RTU Master发送的命令、执行这些命令,并将执行结果发送回RTU Master。在RTU Slave Mode下,设备通常会被编址,以便RTU Master可以识别它们。在命令请求中,RTU Master会指定要与哪个被编址的设备通信,并提供要执行的命令和参数。设备接收到请求后,会根据请求中提供的地址和命令执行相应的操作,并将执行结果返回给RTU Master。
在RTU Master Mode下,设备主动向RTU Slave发送命令,并等待响应。在该模式下,设备的主要任务是发送命令、接收响应,并解析响应中包含的数据。
在RTU Master Mode下,设备通常不需要编址,因为它主动发起通信。设备向RTU Slave发送命令时,会包含要执行的命令和参数,以及RTU Slave的地址。RTU Slave接收到请求后,会执行相应的操作,并将执行结果发送回RTU Master。设备接收到响应后,会解析响应中包含的数据,并根据需要进行后续处理。

d9741e2ae575b80cfef66defe1f3062a.png

Part2Modbus Rtu介绍

Modbus Rtu通常由帧起始符、地址码、功能码、数据域、校验码、帧结束符组成。帧起始符和帧结束符在示例数据帧中没有显示,通常由通信设备硬件电路自动添加。
帧起始符:Modbus RTU协议帧的起始符为一个11位长的标志位(3.5个字符),用于同步传输的时钟。
地址码:地址码表示Modbus RTU设备的从站地址或广播地址,用于指定数据帧所要访问的设备或广播数据。
功能码:功能码用于定义数据帧所要进行的操作类型,包括读取线圈状态、读取离散输入状态、读取保持寄存器、读取输入寄存器、写单个线圈、写单个保持寄存器等。
数据域:数据域是Modbus RTU协议帧中的核心部分,用于传输数据信息。根据功能码的不同,数据域可以包含读取或写入的数据信息,以及校验码等。
校验码:校验码用于检验Modbus RTU协议帧中数据的准确性和完整性,通常使用CRC校验码或LRC校验码。
帧结束符:帧结束符表示一个Modbus RTU协议帧的结束,通常是一个长度为3.5个字符的静默时间,用于指示下一帧数据的开始。

48227d1a4533d30d13dee46cd5fc021d.png

Part3python协议对接

为了能够快速的进行协议研究,我们首先基于python的库进行研究。地址https://github.com/ljean/modbus-tk

c87edafe3ce2defdd043337bc4ff4802.png

slave创建

import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus as modbus
from modbus_tk import modbus_tcp, hooks
import sys


def on_handle_write_single_register_request(args):
    response = args[1]
    print(response)


# 配置从站参数
server = modbus_tcp.TcpServer(address='127.0.0.1', port=502)
# 启动从站
server.start()

hooks.install_hook('modbus.Slave.handle_write_single_register_request',
                   on_handle_write_single_register_request)
slave_1 = server.add_slave(1)
slave_1.add_block('0', cst.HOLDING_REGISTERS, 0, 255)
slave_1.set_values('0', 0, 1234)


# 响应主站请求
while True:
    name = input()
    break

    # 停止从站
server.stop()

slave主要功能有两点:
1、设置寄存器的值,主站查询的时候,直接会应答 ,示例如slave_1.set_values('0', 0, 1234)
2、接收主站的请求,并进行相关处理,这里通过绑定一个回调函数的形式来实现。

hooks.install_hook('modbus.Slave.handle_write_single_register_request',
                   on_handle_write_single_register_request)

master创建

import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_tcp

# 建立TCP连接
master = modbus_tcp.TcpMaster(host='127.0.0.1', port=502)
master.set_timeout(5.0)

# 读取保持寄存器数据
start_address = 0
number_of_registers = 9
values = master.execute(1, cst.READ_HOLDING_REGISTERS,
                        start_address, number_of_registers)

# 打印保持寄存器数据
print(values)

start_address = 1
ret = master.execute(1, cst.WRITE_SINGLE_REGISTER,
                     start_address, output_value=8)

# 关闭连接
master.close()

master主要功能也是两点:
1、读取子站寄存器的值,通过values = master.execute(1, cst.READ_HOLDING_REGISTERS, start_address, number_of_registers)
2、写入子站寄存器值,通过ret = master.execute(1, cst.WRITE_SINGLE_REGISTER, start_address, output_value=8)这里写入之后,在上面的子站中,就是回调函数那里进行处理。

Part4C++对接

由于虚幻引擎运行时,不支持python对接,所以我们还需要寻找一个C的库来方便集成。这里推荐下面的库https://github.com/stephane/libmodbus

5844dcf0cc49ab1866cf86df4f83f799.png

这个库支持windows编译,编译步骤如下:
1、在src/win32目录下,运行nodejs脚本cscript configure.js
2、拷贝modbus-version.h文件到win32文件夹下面
3、添加ws2_32.lib依赖库
4、将其编译为动态库即可
默认的编译,会有不少函数定义的告警,这里需要在预编译处理的地方增加一个dll导出的宏。示例如下

b7f4d59bb72581f6c44725a061b86cdb.png

编译好之后,就可以使用了。这里针对不同寄存器的操作,需要调用不同的函数。这里的寄存器对应于协议里面的就是功能码。如下所示

/*
      * modbus_read_bits  读线圈  fc=1
      * modbus_read_input_bits 读输入线圈 fc=2
      * modbus_read_registers 读取保持寄存器 fc=3
      * modbus_read_input_registers 读输入寄存器 fc=4
      * modbus_write_bit 写一位数据(强置单线圈) fc=5
      * modbus_write_register 写单寄存器(预置单寄存器) fc=6
      * modbus_write_bits 写多位数据(强置多线圈) fc=15
      * modbus_write_and_read_registers 读/写多个寄存器 fc=23
     */

这里写了一个master的主站程序,示例如下:

#include <modbus.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
  modbus_t *ctx = nullptr;
  uint16_t tab_reg[64];
  int rc = 0;

  // 创建TCP连接的Master上下文环境
  ctx = modbus_new_tcp("192.168.0.1", 502);
  if (ctx == NULL)
  {
    fprintf(stderr, "Unable to create modbus context\n");
    return -1;
  }

  // 连接到Slave设备
  if (modbus_connect(ctx) == -1)
  {
    fprintf(stderr, "Unable to connect to modbus server\n");
    modbus_free(ctx);
    return -1;
  }

  // 读取Slave设备上的寄存器数据
  rc = modbus_read_registers(ctx, 0, 64, tab_reg);
  if (rc == -1)
  {
    fprintf(stderr, "Unable to read registers\n");
    modbus_close(ctx);
    modbus_free(ctx);
    return -1;
  }

  // 输出读取到的数据
  for (int i = 0; i < 64; i++)
  {
    printf("reg[%d]=%d\n", i, tab_reg[i]);
  }

  // 断开与Slave设备的连接
  modbus_close(ctx);
  modbus_free(ctx);

  return 0;
}

Part5总结

本文主要介绍了基于python和C++的开源库,来实现modbus协议。

Part6Inveta团队

Inveta团队由研发、美术设计、建模等组成。团队介绍:
https://www.inveta.cn/about.html
团队开源项目:
https://github.com/inveta

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值