Modbus-TCP和 Modbus-RTU 互转实现思路

1. Modbus 简介

Modbus 属于串行通信协议,数据一位一位顺序依次传送 。

  • Modbus 属于应用层协议,只定义了数据包组织结构和内容的公共、格式。依赖其他物理层和数据链路层来传输数据。
  • Modbus 目前分别定义了基于串口传输的 Modbus-RTU 和基于以太网传输的 Modbus-TCP 数据传输规则。
  • Modbus 的工作模式是主从通信,在所有节点中,其中一个为 Master 节点,其余为 Slave 节点。(Master 至少且只有 1 个)Modbus 使用请求/应答机制。所以不能同步通信,属于半双工。
  • Modbus-TCP 模式下,主站被称为客户端(Client),从站被称为服务器(Server)IANA 互联网编号分配管理机构给 Modbus 协议赋予了 TCP 端口号为 502。
  • Modbus 数据类型有:
    • 输出线圈(0x1 Coils),1 Bit,数值范围 ON 或 OFF,可读可写。地址范围:00001 - 09999
    • 输入离散量(0x2 Discrete Input),1 Bit,数值范围 ON 或 OFF,只读。地址范围:10001 - 19999
    • 保持寄存器(0x3 Holding Registers),16 Bit 的寄存器,可读可写。地址范围:30001 - 39999
    • 输入寄存器(0x4 Input Registgers),16 Bit 的寄存器,只读。地址范围:40001 - 49999

2. Modbus-RTU 报文格式

在这里插入图片描述

3. Modbus-TCP 报文格式

在这里插入图片描述

ModbusRTU如何判断开始与结束

  • 整个报文帧必须以连续的字符流发送。如果两个字符之间的空闲间隔大于 1.5 个字符时间,则报文帧被认为不完整应该被接收节点丢弃。

  • ModbusRTU协议中,需要用时间间隔来判断一帧报文的开始和结束,协议规定的时间为3.5个字符周期

  • 在一帧报文开始前,必须有大于3.5个字符周期的空闲时间,一帧报文结束后,也必须要有3.5个字符周期的空闲时间,否则就会出现粘包的请况。3.5个字符周期是一个具体时间,与波特率有关。

4. Modbus-RTU 转 Modbus-TCP

uint8_t *rtu_to_tcp(uint8_t *data, uint32_t seq, uint32_t len, size_t *out_len)
{
    size_t pdu_len = len - 2;
    size_t total_len = pdu_len + 6;
    size_t total_len = pdu_len;
    
    uint8_t *result = (uint8_t *)malloc(total_len);
    if (!result) {
         printf("rtu_to_tcp: Momery alloc failed\n");
        *out_len = 0;
         return NULL;
    }
    result[0] = (seq >> 8) & 0xFF;
    result[1] = seq & 0xFF;
    result[2] = 0x00;
    result[3] = 0x00;
    result[4] = (pdu_len + 0) >> 8;
    result[5] = (pdu_len + 0) & 0xFF;

    memcpy(result , data, pdu_len);

    *out_len = total_len;

    return result;
}

5. Modbus-TCP 转 Modbus-RTU

uint8_t * tcp_to_rtu(uint8_t *data, size_t len, size_t *out_len)
{
    uint8_t pdu_len = data[5];
    if (pdu_len + 6 != len) {
        printf("Invalid PDU len\n");
        return NULL;
    }

    uint8_t *pdu_with_crc = (uint8_t *)malloc(pdu_len + 0);
    if (!pdu_with_crc) {
         printf("Momery alloc failed\n");
         return NULL;
    }

    memcpy(pdu_with_crc, data + 6, pdu_len);

    uint16_t crc = modbus_crc16(pdu_with_crc, pdu_len);
    uint8_t crc_bytes[2];
    crc_bytes[0] = crc & 0xFF;
    crc_bytes[1] = (crc >> 8) & 0xFF;

    memcpy(pdu_with_crc + pdu_len, crc_bytes, 2);

    *out_len = pdu_len + 0;
    return pdu_with_crc;
}
  • modbus_crc 函数实现可以参考这里

6. Modbus 常用功能码

在这里插入图片描述

7. Modbus 调试软件使用说明

Modbus 调试软件获取

在这里插入图片描述

Modbus-Poll

在这里插入图片描述

Modbus-Slave

在这里插入图片描述

Modbus 调试软件配置

在这里插入图片描述

8. Modbus 结构体

_modbus

struct _modbus {
    /* Slave address */
    int slave;                                                 // 从站地址
    /* Socket or file descriptor */
    int s;   // Socket 套接字(Modbus-TCP) or 串口句柄(Modbus-RTU)
    int debug;
    int error_recovery;
    struct timeval response_timeout;        // 响应超时设置
    struct timeval byte_timeout;                // 字节超时设置
    const modbus_backend_t *backend;
    void *backend_data;
}

modbus_backend_t

typedef struct _modbus_backend {
    unsigned int backend_type;
    unsigned int header_length;
    unsigned int checksum_length;
    unsigned int max_adu_length;
    int (*set_slave) (modbus_t *ctx, int slave);
    int (*build_request_basis) (modbus_t *ctx, int function, int addr,
                                int nb, uint8_t *req);
    int (*build_response_basis) (sft_t *sft, uint8_t *rsp);
    int (*prepare_response_tid) (const uint8_t *req, int *req_length);
    int (*send_msg_pre) (uint8_t *req, int req_length);
    ssize_t (*send) (modbus_t *ctx, const uint8_t *req, int req_length);
    int (*receive) (modbus_t *ctx, uint8_t *req);
    ssize_t (*recv) (modbus_t *ctx, uint8_t *rsp, int rsp_length);
    int (*check_integrity) (modbus_t *ctx, uint8_t *msg,
                            const int msg_length);
    int (*pre_check_confirmation) (modbus_t *ctx, const uint8_t *req,
                                   const uint8_t *rsp, int rsp_length);
    int (*connect) (modbus_t *ctx);
    void (*close) (modbus_t *ctx);
    int (*flush) (modbus_t *ctx);
    int (*select) (modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length);
    void (*free) (modbus_t *ctx);
} modbus_backend_t;

modbus_mapping_t

  • Modbus 各区 寄存器 集合的指针。
typedef struct {
    int nb_bits;
    int start_bits;
    int nb_input_bits;
    int start_input_bits;
    int nb_input_registers;
    int start_input_registers;
    int nb_registers;
    int start_registers;
    uint8_t *tab_bits;
    uint8_t *tab_input_bits;
    uint16_t *tab_input_registers;
    uint16_t *tab_registers;
} modbus_mapping_t;

9. Modbus 一些函数说明

modbus_new_rtu()

modbus_t *modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);
  • 功能:
    函数应该分配和初始化一个modbus_t结构,以便在串行线上以RTU模式通信。

  • 参数:

    • device:参数指定操作系统处理的串口的名称,
      例如。“/ dev / ttyS0”或“/ dev/ttyUSB0”。
    • baud:指定通信的波特率,例如。9600、19200、57600、115200等
    • parity:奇偶校验参数可以有以下值之一: N没有 E为偶校验 O为奇校验
    • data_bits参数指定数据的位数,允许的值为5、6、7和8。
    • stop_bits参数指定stop的位,允许的值是1和2。
      一旦modbus_t结构被初始化,你必须用modbus_set_slave(slave_addr)设置你的设备的从机,并用modbus_connect(slave_addr)连接到串行总线。
  • 返回值:

    • 如果成功,函数将返回一个指向modbus_t结构体的指针。否则,它将返回NULL并将errno设置为EINVAL(给出了一个无效参数)

modbus_new_tcp()

modbus_t *modbus_new_tcp(const char *ip, int port);
  • 功能:

    • modbus_new_tcp() 函数应该分配和初始化一个 modbus_t 结构体来与Modbus TCP IPv4服务器通信。
  • 参数:

    • ip指定客户端想要建立连接的服务器的ip地址。NULL 值可以用来监听服务器模式下的任何地址。
    • port参数是要使用的 TCP 端口。将端口设置为MODBUS_TCP_DEFAULT_PORT,使用默认的 502 端口。使用大于或等于 1024 的端口号很方便,因为它不需要拥有管理员权限。
  • 返回:

    • 如果成功,函数将返回一个指向 modbus_t 结构体的指针。否则,它将返回 NULL 并将 errno 设置为 EINVAL。

modbus_connect()

int modbus_connect(modbus_t *ctx);
  • 功能: 建立到Modbus服务器、网络或总线的连接。

modbus_close()

void modbus_close(modbus_t *ctx);
  • 功能: 函数应该关闭与上下文中设置的后端建立的连接。

modbus_flush()

int modbus_flush(modbus_t *ctx);
  • 功能: 函数将丢弃接收到的数据,但不将数据读到套接字或与上下文ctx相关的文件描述符中。

modbus_rtu_set_customer_rts()

int modbus_rtu_set_customer_rts(modbus_t *ctx, void *(set_rts)(modbus_t *ctx, int on));
  • 功能: 设置传输前后设置RST PIN要调用的自定义函数。
  • 示例:(我使用 RS485 需要配置控制引脚)
static void custom_rts_rtu(modbus_t *ctx, int on) 
{
    if (on) {
        rs485_set_gpio_value(RS485_CTRL_GPIO, GPIO_OUT_MODE); 
    } else {
        rs485_set_gpio_value(RS485_CTRL_GPIO, GPIO_IN_MODE); 
    }   
}

    if (serial_mode == RS485_MODE) {
        if (modbus_rtu_set_custom_rts(rtu_ctx, custom_rts_rtu) == -1) {
            printf("Set custom rts fun failed %s \n", modbus_strerror(errno));
            return -1;
        }
        modbus_rtu_set_rts(rtu_ctx, MODBUS_RTU_RTS_UP);
    }

modbus_mapping_new()

modbus_mapping_free()

modbus_receive()

modbus_reply()

modbus_get_header_length()

modbus_send_raw_request()

modbus_receive_confirmation()

modbus_set_bits_from_bytes()

  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
支持Modbus TCP转Modubus RTURTU可以为RS232或者RS485。 支持RTU做主站或者Modubs TCP做主站。 支持多主机访问支持,最多连接100个TCP客户端。 支持所有的Modubus功能码和寄存器范围。 同时也可以做普通串口服务器使用。 ZLAN5142 概述 ZLAN5142 Modbus网关是上海卓岚信息科技有限公司开发的一款在Modbus TCPModbus RTU之间进行协议转化的转换器,其中Modbus RTU可以是RS485也可以是RS232接口。 ZLAN5142具备了强大的多主机访问支持功能和RS485总线冲突控制功能。所谓多主机功能是指,ZLAN5142作为Modbus TCP服务器时可以支持多达100个TCP客户端同时连接,多个TCP连接可以随机性的访问,ZLAN5142能够分别对不同的连接给出正确应答,保证数据稳定不丢包。相对于RS485总线只能够有一个主机进行访问,ZLAN5142实现了“多主机”功能。ZLAN5142实现了RS485总线的冲突控制,防止了多主机时的串口总线抢占问题。 另外,将ZLAN5142的“转化协议”选择为“无”,也可以当作普通的透明传输的串口服务器使用。但是不同于普通的串口服务器例如ZLAN5102,ZLAN5142即使作为普通串口服务器模式下也可以支持“多主机”功能,可解决多个计算机或主站如何同时监控一个串口设备的难题。 特点 支持Modbus TCP转Modubus RTURTU可以为RS232或者RS485。 支持RTU做主站或者TCP做主站。 支持多主机访问支持,最多连接100个TCP客户端。 支持1200~460800波特率。 支持RS485总线冲突调度机制,防止总线冲突。 支持所有的Modubus功能码和寄存器范围。 可作为普通的透传串口服务器使用,具备ZLAN5102相同功能。 小于1W的低功耗设计。 内嵌485防雷保护功能,适合室外485通信。 支持DNS,满足通过域名实现通信的要求。支持DHCP。 2KV网络浪涌保护。 外壳采用抗辐射的SECC板,保证在高电磁辐射区也能够正常工作。 规格 网络界面 以太网 10/100 Mbps, RJ45 保护 内建2KV电磁隔离 串口界面 界面 RS-232/485 串口数 1 校验位 None, Even, Odd, Space, Mark 数据位 5~9 停止位 1,2 流控 RTS/CTS,DTR/DCR, XON/XOFF 速率 1200bps~460800bps 软件特性 协议 TCP,HTTP,UDP,ICMP,ARP,IP,DNS,DHCP 虚拟串口平台 Windows 95/98/ME/NT/2000/XP/WIN7/WIN8 配置方式 Web浏览器、卓岚ZLVirCom、串口类AT命令 电器特性 电压输入 DC9~24V,100mA 机械特性 外壳材料 抗电磁材料SECC钢板 尺寸 长×宽×高=9.4cm×6.5cm×2.5cm 工作环境 工作温度,湿度 -45~85℃,5~95% RH 储存温度,湿度 -45~165℃,5~95% RH W
首先,Modbus-RTU是一种串行通信协议,而Modbus-TCP是一种基于TCP/IP协议Modbus协议。下面我将分别介绍如何设计Modbus-RTU从站协议Modbus-TCP从站协议。 1. 设计Modbus-RTU从站协议 Modbus-RTU从站协议的数据帧格式如下: | 地址码 | 功能码 | 数据 | CRC校验 | 其中,地址码是从站的地址,功能码用于区分不同的数据操作类型,数据包括读取或写入的数据,CRC校验用于检查数据传输的正确性。 对于主站(客户端)获取数据,需要实现以下步骤: 1)主站向从站发送读取数据的请求,请求数据的地址和数量。 2)从站收到请求后,将请求中的地址和数量解析出来,并将相应的数据打包发送给主站。 3)主站收到从站的数据后,解析数据并进行处理。 具体的实现细节可以根据实际需求进行设计。 2. 设计Modbus-TCP从站协议 Modbus-TCP从站协议的数据帧格式如下: | 事务标识符 | 协议标识符 | 长度 | 单元标识符 | 功能码 | 数据 | 其中,事务标识符是一个唯一的标识符,用于区分不同的请求和响应;协议标识符用于标识Modbus协议;长度表示数据的长度;单元标识符用于标识从站;功能码用于区分不同的数据操作类型;数据包括读取或写入的数据。 对于主站(客户端)获取数据,需要实现以下步骤: 1)主站向从站发送读取数据的请求,请求数据的地址和数量。 2)从站收到请求后,将请求中的地址和数量解析出来,并将相应的数据打包发送给主站。 3)主站收到从站的数据后,解析数据并进行处理。 具体的实现细节可以根据实际需求进行设计。 总的来说,设计Modbus-RTU从站协议Modbus-TCP从站协议,需要对数据帧格式进行定义,并实现请求和响应的处理逻辑。同时,需要注意数据传输的正确性和安全性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值