网络高级大锅饭(附代码)

Modbus RTU

  1. 与Modbus TCP的区别

在一般工业场景使用modbus RTU的场景还是更多一些,modbus RTU基于串行协议进行收发数据,包括RS232/485等工业总线协议。

与modbus TCP不同的是RTU没有报文头MBAP字段,但是在尾部增加了两个CRC检验字节(CRC16),因为网络协议中自带校验,所以在TCP协议中不需要使用CRC校验码。

RTU和TCP的总体使用方法基本一致,只是在创建modbus对象时有所不同,TCP需要传入网络socket信息;而RTU需要传入串口相关信息。

  1. Modbus RTU特点

ModbusRTU也是主从问答协议,由主机发起,一问一答

设置串口参数:

波特率:9600

8位数据位

1位停止位

无流控

  1. ModbusRTU协议格式:

ModbusRTU协议数据帧包含四部分:地址码、功能码、数据、校验码

从机ID:1-247

功能码:同Modbus TCP 协议

数据: 起始地址、数量、数据

校验码:2字节,对地址码、功能码、数据部分进行校验,调用函数生成

  1. 报文详解:

03功能码:

主机->从机: 01 03 00 00 00 01 84 0A

01:从机ID

03:功能码

00 00:起始地址

00 01:数量

84 0A:校验码

从机->主机:01 03 02 00 14 b8 44

01:从机ID

03:功能码

02:字节计数

00 14:数据

b8 44 :校验码

modbus rtu实现03功能码---读保持寄存器

#include "Crc_Calc.h"
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

int main(int argc,char const *argv[])
{
    int fd=open("/dev/ttS1",O_RDWR);
    if(fd<0)
    {
        perror("open err");
        return -1;
    }

    //串口初始化
    uart_init(fd);
    //往串口中发送数据,定义除校验码的带有03功能码的数组
    uint8_t req[8]={0x01,0x03,0x00,0x00,0x00,0x01};
    //根据数组产生CRC校验码,大小两个字节
    uint16_t crc=GetCRC16(req,6);
    //把生成的CRC校验码放到要发送的03功能码数组中
    req[6]=crc>>8;
    req[7]=crc;
    //往串口文件中写入拼好的功能码数组
    write(fd,req,sizeof(req));
    //接受从机回应--即往串口文件读取信息
    uint8_t data[32]={0};
     int ret=read(fd,data,sizeof(data));
    for(int i=0;i<ret;i++)
    printf("%#x",data[i]);
    printf("\n");
  //关闭
      close(fd);
    return 0;  
}

modbus slave客户端的数据:

 执行结果采用gcc *.c 编译,sudo ./a.out执行

Modbus库

  1. 函数接口

modbus_t*   modbus_new_tcp(const char *ip, int port)
功能:以TCP方式创建Modbus实例,并初始化
参数:
    ip   :ip地址
    port:端口号
返回值:成功:Modbus实例
      失败:NULL
int modbus_set_slave(modbus_t *ctx, int slave)
功能:设置从机ID
参数:
    catx   :Modbus实例
    slave:从机ID
返回值:成功:0
       失败:-1
int   modbus_connect(modbus_t *ctx)
功能:和从机(slave)建立连接
参数:
    ctx:Modbus实例
返回值:成功:0
       失败:-1
void   modbus_free(modbus_t *ctx)
功能:释放Modbus实例
参数:ctx:Modbus实例
void   modbus_close(modbus_t *ctx)
功能:关闭套接字
参数:ctx:Modbus实例
int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
功能:读取线圈状态,可读取多个连续线圈的状态(对应功能码为0x01
参数:
    ctx   :Modbus实例
    addr :寄存器起始地址
    nb    :寄存器个数
    dest :得到的状态值
int  modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
功能:读取输入状态,可读取多个连续输入的状态(对应功能码为0x02
参数:
    ctx   :Modbus实例
    addr :寄存器起始地址
    nb   :寄存器个数
    dest :得到的状态值
返回值:成功:返回nb的值
int  modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
功能:读取保持寄存器的值,可读取多个连续保持寄存器的值(对应功能码为0x03
参数:
    ctx   :Modbus实例
    addr :寄存器起始地址
    nb    :寄存器个数
    dest :得到的寄存器的值
返回值:成功:读到寄存器的个数
       失败:-1
int   modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
功能:读输入寄存器的值,可读取多个连续输入寄存器的值(对应功能码为0x04
参数:
    ctx   :Modbus实例
    addr :寄存器起始地址
    nb    :寄存器个数
    dest :得到的寄存器的值
返回值:成功:读到寄存器的个数
       失败:-1
int  modbus_write_bit(modbus_t *ctx, int addr, int status);
功能:写入单个线圈的状态(对应功能码为0x05
参数:
    ctx     :Modbus实例
    addr  :线圈地址
    status:线圈状态
返回值:成功:1
      失败:-1
int  modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);
功能:写入多个连续线圈的状态(对应功能码为15
参数:
    ctx     :Modbus实例
    addr  :线圈地址
    nb     :线圈个数
    src    :多个线圈状态
返回值:成功:nb
      失败:-1
int  modbus_write_register(modbus_t *ctx, int addr, int value);
功能:  写入单个寄存器(对应功能码为0x06
参数: 
    ctx    :Modbus实例
    addr  :寄存器地址
    value :寄存器的值 
返回值:成功:1
       失败:-1
int  modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);
功能:写入多个连续寄存器(对应功能码为16
参数:
    ctx    :Modbus实例
    addr  :寄存器地址
    nb     :寄存器的个数
    src    :多个寄存器的值 
返回值:成功:nb
      失败:-1


  1. 编程流程
  1. 创建实例 modbus_new_tcp
  2. 设置从机ID modbus_set_slave
  3. 建立连接 modbus_connect
  4. 寄存器的操作(根据功能码自行选择)
  5. 关闭套接字 modbus_close
  6. 释放实例 modbus_free

示例代码:

执行结果如下:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值