跳转至

ModbusRtu主站接口说明

一、使用示例

创建一条ModbusRtu主站通讯通道,在通道上创建2个站点,循环获取站点的值或写入站点的值

MRTU主站

#include "modbus_host/modbusrtu_host_ch.h"
#include "modbus_host/modbus_host_dev.h"
#include <stdio.h>
#include <unistd.h>

void read_test(const char *name, ModbusHostDev *pdev, int code, int startaddr, int size)
{
    uint16_t data[1024] = { 0 };
    printf("%s, code=%d, addr=[%04X]%d, size=%d--------------------------------------------\r\n",
        name, code, startaddr, startaddr, size);
    pdev->GetCycleReadDataBlookValue((ModbusHost::ReadCode_E)code, startaddr, data, size);
    for(int i = 0; i < size; i++) {
        printf("%s, code=%d, addr=[%04X]%d, value=[%04X]%d\r\n",
            name, code, startaddr+i, startaddr+i, data[i], data[i]);
    }
}

int main(int argc, char *argv[])
{
    // 创建通道
    ModbusRtuHostCh *pch = new ModbusRtuHostCh("/dev/ttysWK2", 115200, 'N', 8, 1);
    pch->SetBitSingleMaxPoint(4);
    pch->SetRegSingleMaxPoint(6);
    printf("创建通讯通道 [%08X]\r\n", pch);
    // 创建设备1-温湿度传感器
    ModbusHostDev *pdev1 = new ModbusHostDev(pch, 1);
    pdev1->AddCycleReadDataBlock(ModbusHost::kReadCodeBits, 0x0000, 10);
    pdev1->AddCycleReadDataBlock(ModbusHost::kReadCodeBits, 0x1000, 10, 5000);
    pdev1->AddCycleReadDataBlock(ModbusHost::kReadCodeInputBits, 0x2000, 10, 5000);
    pdev1->AddCycleReadDataBlock(ModbusHost::kReadCodeHoldRegs, 0x3000, 10, 5000);
    pdev1->AddCycleReadDataBlock(ModbusHost::kReadCodeInputRegs, 0x4000, 12, 5000);
    printf("创建设备1 [%08X]\r\n", pdev1);
    // 创建设备2-液冷机组
    ModbusHostDev *pdev2 = new ModbusHostDev(pch, 2);
    pdev2->AddCycleReadDataBlock(ModbusHost::kReadCodeBits, 0x8000, 10, 5000);
    pdev2->AddCycleReadDataBlock(ModbusHost::kReadCodeBits, 0x9000, 10, 5000);
    pdev2->AddCycleReadDataBlock(ModbusHost::kReadCodeInputBits, 0xA000, 10, 5000);
    pdev2->AddCycleReadDataBlock(ModbusHost::kReadCodeHoldRegs, 0xB000, 10, 5000);
    pdev2->AddCycleReadDataBlock(ModbusHost::kReadCodeInputRegs, 0xC000, 12, 5000);
    printf("创建设备2 [%08X]\r\n", pdev2);
    // 启动通道
    pch->Run();
    printf("启动通讯通道\r\n");

    int bits[1024] = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
    int regs[1024] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
    int ret_write = 0;

    // 等待
    for(int i = 0; i < 1000; i++) {
        sleep(10);
        uint16_t data[1024];
        // 设备1读取测试 功能码01,起始地址0x0000,点数10
        int code = ModbusHost::kReadCodeBits;
        int startaddr = 0x0000;
        int size = 10;
        read_test("pdev1", pdev1, code, startaddr, size);

        // 设备1读取测试 功能码01,起始地址0x1000,点数10
        code = ModbusHost::kReadCodeBits;
        startaddr = 0x1000;
        size = 10;
        read_test("pdev1", pdev1, code, startaddr, size);

        // 设备1读取测试 功能码02,起始地址0x2000,点数10
        code = ModbusHost::kReadCodeInputBits;
        startaddr = 0x2000;
        size = 10;
        read_test("pdev1", pdev1, code, startaddr, size);

        // 设备1读取测试 功能码03,起始地址0x3000,点数10
        code = ModbusHost::kReadCodeHoldRegs;
        startaddr = 0x3000;
        size = 10;
        read_test("pdev1", pdev1, code, startaddr, size);

        // 设备1读取测试 功能码04,起始地址0x4000,点数12
        code = ModbusHost::kReadCodeInputRegs;
        startaddr = 0x4000;
        size = 12;
        read_test("pdev1", pdev1, code, startaddr, size);
        printf("[%s][%d][%s] i=%d\r\n", __FILE__, __LINE__, __FUNCTION__, i);

        // 设备2读取测试 功能码01,起始地址0x8000,点数10
        code = ModbusHost::kReadCodeBits;
        startaddr = 0x8000;
        size = 10;
        read_test("pdev2", pdev2, code, startaddr, size);

        // 设备2读取测试 功能码01,起始地址0x9000,点数10
        code = ModbusHost::kReadCodeBits;
        startaddr = 0x9000;
        size = 10;
        read_test("pdev2", pdev2, code, startaddr, size);

        // 设备2读取测试 功能码02,起始地址0xA000,点数10
        code = ModbusHost::kReadCodeInputBits;
        startaddr = 0xA000;
        size = 10;
        read_test("pdev2", pdev2, code, startaddr, size);

        // 设备2读取测试 功能码03,起始地址0xB000,点数10
        code = ModbusHost::kReadCodeHoldRegs;
        startaddr = 0xB000;
        size = 10;
        read_test("pdev2", pdev2, code, startaddr, size);

        // 设备2读取测试 功能码04,起始地址0xC000,点数12
        code = ModbusHost::kReadCodeInputRegs;
        startaddr = 0xC000;
        size = 12;
        read_test("pdev2", pdev2, code, startaddr, size);
        printf("[%s][%d][%s] i=%d\r\n", __FILE__, __LINE__, __FUNCTION__, i);

        // 设备1写入测试 功能码15,起始地址0x0000,点数10
        ret_write = pdev1->AddWriteDataBlock(ModbusHost::kWriteCodeBits, 0x0000, bits, 10);
        printf("[%s][%d][%s] ret_write=%d\r\n", __FILE__, __LINE__, __FUNCTION__, ret_write);

        // 设备1写入测试 功能码16,起始地址0x3000,点数10
        ret_write = pdev1->AddWriteDataBlock(ModbusHost::kWriteCodeHoldRegs, 0x3000, regs, 10);
        printf("[%s][%d][%s] ret_write=%d\r\n", __FILE__, __LINE__, __FUNCTION__, ret_write);

        // 设备1写入测试 功能码05,起始地址0x8000,点数4
        ret_write = pdev2->AddWriteDataBlock(ModbusHost::kWriteCodeBit, 0x8000, bits, 4);
        printf("[%s][%d][%s] ret_write=%d\r\n", __FILE__, __LINE__, __FUNCTION__, ret_write);

        // 获取设备1和设备2的超时状态
        printf("[%s][%d][%s] pdev1->GetTimeoutStatus()=%d, pdev2->GetTimeoutStatus()=%d\r\n",
            __FILE__, __LINE__, __FUNCTION__, pdev1->GetTimeoutStatus(),pdev2->GetTimeoutStatus());
    }

    return 1;
}

二、ModbusHostCh类

ModbusHostCh类为ModbusRtuHostCh类、ModbusTcpHostCh类的基类,提供Modbus主站通道的通用属性

1. 构造函数

/**
 * @brief 构造函数
 *
 * @param
 */
ModbusHostCh();

2. 析构函数

/**
 * @brief 析构函数
 */
~ModbusHostCh();

3. 获取数据区指针

获取ModbusHost通道的数据区指针

/**
 * @brief 获取数据区指针
 *
 * 获取ModbusHost通道的数据区指针
 *
 * @param 无
 *
 * @return 数据区的指针
 */
ModbusHostData *GetDataArea();
  • 参数
  • 无;
  • 返回值
  • 数据区指针;

4. 获取通讯超时状态

/**
 * @brief 获取通讯超时状态
 *
 * @param slaveid 站地址
 *
 * @return 是否超时:1-超时;0-未超时;-1-站地址错误;
 */
 int GetTimeoutStatus(int slaveid);
  • 参数
  • slaveid 站地址;
  • 返回值
  • 是否超时:1-超时;0-未超时;-1-站地址错误;

5. 设置通讯超时时间

/**
 * @brief 设置通讯超时时间
 * 
 * @param sec  超时时间的秒数;
 * @param usec 超时时间的微秒数;
 * 
 * @return 无
 */
void SetResponseTimeout(long sec, long usec);
  • 参数
  • sec:超时时间的秒数;
  • usec:超时时间的微秒数;
  • 返回值

6. 设置Modbus通讯异常的判定次数

modbus帧通讯超时次数超过此值时,认为Modbus通讯异常

/**
 * @brief 设置Modbus通讯异常的判定次数
 *
 * Modbus帧通讯超时次数超过此值时,认为Modbus通讯异常
 *
 * @param count 次数;
 * 
 * @return 无
 */
void SetTimeoutMaxCount(uint32_t count);
  • 参数
  • count:次数;
  • 返回值

7. 轮询帧时间间隔 毫秒

/**
 * @brief 轮询帧时间间隔
 *
 * 每2帧通讯报文的之间的间隔时间
 *
 * @param ms 间隔时间,毫秒;
 * 
 * @return 无
 */
void SetPollFrameIntervalMs(uint32_t ms);
  • 参数
  • ms:每2帧通讯报文的间隔时间,毫秒;
  • 返回值

8. 轮询总时间间隔 毫秒

/**
 * @brief 轮询总时间间隔
 *
 * 所有帧通讯报文之间的间隔时间,即所有报文轮训一遍后等待一定时间后再进行下次轮训
 *
 * @param ms 所有帧通讯报文的间隔时间,毫秒;
 * 
 * @return 无
 */
void SetPollTotalIntervalMs(uint32_t ms);
  • 参数
  • ms:所有帧通讯报文的间隔时间,毫秒;
  • 返回值

9. 设置是否打印报文

/**
 * @brief 设置是否打印报文(通过printf输出到命令行中)
 * 
 * @param debug 是否开启,0-关闭;1-开启;
 * 
 * @return 无
 */
void SetDebug(int debug);
  • 参数
  • debug :是否开启,0-关闭;1-开启;
  • 返回值

10. 设置线圈单帧最大读点个数

/**
 * @brief 线圈单帧最大读点个数
 *
 * @param point 点数
 *
 * @return 无
 */
void SetBitSingleMaxPoint(int point);

11. 设置寄存器单帧最大读点个数

/**
 * @brief 寄存器单帧最大读点个数
 *
 * @param point 点数
 *
 * @return 无
 */
void SetRegSingleMaxPoint(int point);

12. 设置收发报文内容回调函数

/**
 * @brief 设置收发报文内容回调函数
 *
 * @param *s_callback 发送报文回调函数(参数,报文内容,报文长度);
 * @param *r_callback 接收报文回调函数(参数,报文内容,报文长度);
 * @param arg         回调参数;
 * 
 * @return 无
 */
void set_MessageCallback(
        void (*s_callback)(void *, const uint8_t *, int),
        void (*r_callback)(void *, const uint8_t *, int),
        void *arg = NULL);
  • 参数
  • *s_callback:发送报文回调函数(参数,报文内容,报文长度);
  • *r_callback:接收报文回调函数(参数,报文内容,报文长度);
  • arg:回调参数;
  • 返回值

13. 设置收发报文内容回调使能

/**
 * @brief 设置收发报文内容回调使能
 *
 * @param en 是否使能:0-未使能;1-使能;
 * 
 * @return 无
 */
void set_MessagrCallbackEn(int en);
  • 参数
  • en:是否使能:0-未使能;1-使能;
  • 返回值

14. 获取指定设备地址的通讯计数信息

/**
 * @brief 获取指定设备地址的通讯计数信息
 *
 * @param slave                 指定的Modbus设备站地址,1~255;
 * @param *total_recvok_count   输出参数,总接收成功(通讯正常)的帧数量
 * @param *total_recvfail_count 输出参数,总接收失败(通讯异常)的帧数量
 * @param *curr_timout_count    输出参数,当前接收成功(通讯正常)的帧数量
 * 
 * @return 是否获取成功:1-获取成功;-1-获取失败;
 */
int get_CommunicatCount(int slave,
        uint64_t *total_recvok_count, uint64_t *total_recvfail_count,
        uint64_t *curr_timout_count = NULL);
  • 参数
  • slave:指定的Modbus设备站地址,1~255;
  • *total_recvok_count:输出参数,总接收成功(通讯正常)的帧数量
  • *total_recvfail_count:输出参数,总接收失败(通讯异常)的帧数量
  • *curr_timout_count:输出参数,当前接收成功(通讯正常)的帧数量
  • 返回值
  • 是否获取成功:1-获取成功;-1-获取失败;

三、ModbusRtuHostCh

ModbusRtuHostCh类,继承自ModbusHostCh类,用于创建ModbusRtu主站通讯通道

1. 构造函数

/**
 * @brief 构造函数
 *
 * @param *device  串口的文件描述符;
 * @param baud     波特率;
 * @param parity   校验位,可选'N'(无校验)、'O'(奇校验)、'E'(偶校验);
 * @param data_bit 数据位,可选5、6、7、8;
 * @param stop_bit 停止位,可选1、2;
 */
ModbusRtuHostCh(const char *device, int baud = 115200,
            char parity = 'N', int data_bit = 8, int stop_bit = 1);
  • 参数
  • *device:串口的文件描述符;
  • baud:波特率;
  • parity:校验位,可选'N'(无校验)、'O'(奇校验)、'E'(偶校验);
  • data_bit:数据位,可选5、6、7、8;
  • stop_bit:停止位,可选1、2;
  • 返回值
  • 返回值

2. 析构函数

/**
 * @brief 析构函数
 */
~ModbusRtuHostCh();

3. 启动函数

启动ModbusRtu主站读取数据

/**
 * @brief 启动函数
 *
 * 启动ModbusRtu主站读取数据
 *
 * @param 无
 *
 * @return 无
 */
void Run();
  • 参数
  • 返回值

四、ModbusHostDev类

Modbus主站模式下的站点类

1. 构造函数

构造函数,用于创建主站模式下的站点

/**
 * @brief 构造函数
 *
 * @param pch ModbusHost通道
 * @param slave 站地址
 */
ModbusHostDev(ModbusHostCh *pch, int slave = 1);
  • 参数

  • pch: ModbusHost通道

  • slave:站地址;
  • 返回值

  • 返回值

2. 析构函数

/**
 * @brief 析构函数
 */
~ModbusHostDev();

3. 获取站点ID

/**
 * @brief 获取站点ID(站地址)
 *
 * @return 站地址
 */
int GetSlaveId();
  • 参数
  • 无;
  • 返回值
  • 站地址

4. 添加回读数据块

/**
 * @brief 添加循环回读数据块
 *
 * @param code      功能码;
 * @param startaddr 点的起始地址,一般为0~65535;
 * @param nb        点的个数;
 * @param cycle     该数据块通讯周期
 *
 * @return 是否添加成功:
 *          1 :添加成功;
 *          -1:站地址错误;
 *          -2:添加失败(数据越界);
 *          -3:添加失败(已存在冲突的数据块);
 */
int AddCycleReadDataBlock(ModbusHost::ReadCode_E code, int startaddr, int nb, int cycle = 1000);
  • 参数

  • code 功能码;

  • addr 点的起始地址,一般为0~65535;
  • nb 点的个数;
  • cycle 该数据块通讯周期
  • 返回值

  • 是否添加成功:1-添加成功;

    -1:站地址错误;
    
    -2:添加失败(数据越界);
    
    -3:添加失败(已存在冲突的数据块);
    

5. 获取回读数据区多个连续数据点的值

/**
 * @brief 获取回读数据区多个连续数据点的值
 *
 * 获取指定数据类型、多个连续点地址的值
 *
 * @param code      功能码;
 * @param startaddr 点的起始地址,一般为0~65535;
 * @param *point    返回参数,点的值;
 * @param nb        点的数量;
 *
 * @return 是否获取成功:1-获取成功;-1-返回参数为空;-2-未找到数据块;
 */
int GetCycleReadDataBlookValue(ModbusHost::ReadCode_E code, int startaddr, uint16_t *point, int nb = 1);
  • 参数

  • code 功能码;

  • addr 点的起始地址,一般为0~65535;
  • *value 返回参数,点的值;
  • nb 点的个数;
  • 返回值

  • 是否获取成功:1-获取成功;-1-返回参数为空;-2-未找到数据块;

6. 添加写数据块

/**
 * @brief 添加写数据块
 *
 * @param code      功能码;
 * @param startaddr 点的起始地址,一般为0~65535;
 * @param *value    点的值;
 * @param nb        点的个数;
 *
 * @return 是否添加成功:
 *          1 :添加成功;
 *          -1:站地址错误;
 *          -2:添加失败(数据越界);
 */
int AddWriteDataBlock(ModbusHost::WriteCode_E code, int startaddr, int *value, int nb = 1);
  • 参数

  • code 功能码;

  • startaddr 点的起始地址,一般为0~65535;

  • *value 点的值;
  • nb 点的个数;
  • 返回值

  • 是否添加成功:

    • 1 :添加成功;
    • -1:站地址错误;
    • -2:添加失败(数据越界);

7. 获取通讯超时状态

/**
 * @brief 获取通讯超时状态
 *
 * @return 通讯状态,0-未超时;1-超时;
 */
int GetCommTimeOutFlag();
  • 参数
  • 返回值
  • 通讯状态,0-未超时;1-超时;