深入浅出ModbusTcp

一:概念可参考
https://blog.csdn.net/sgmcumt/article/details/87435191
二:仿真工具下载
https://modbustools.com/download.html
使用以及注册什么的,这个大家可以百度或者去b站上面搜索一下相应的视频即可。
三:例程参考
提供了两个例程供大家参考,一个是主站,这个相当于socket编程里面的客户端,另一个是从站,这个相当于服务端。

从站的话,这里是使用了libmodbus库,具体的版本以及下载方法。
(1)3.1.6接口说明
https://libmodbus.org/docs/v3.1.6/
(2)下载
https://www.libmodbus.org/download/

安装方法:
(a)先解压,用tar -zxvf + 文件包名
(b)执行./configure
这个命令其实就是在运行一些脚本,安装和系统匹配的一些东西,
©sudo make 进行编译
(d)sudo make install 安装
安装的意思就是把编译好的东西拷贝一份到系统的某个具体的目录下,这个可以参考Makefile文件看看到底做了什么。

从站:

#include "ModbusTcpMaster.h"
int main(int argc, char **argv)
{
    /*创建一个ModbusTCP 对象*/
    ModbusTcpMaster mb = ModbusTcpMaster("127.0.0.1", 502);

    /*设置从站地址*/
    mb.modbus_set_slave_id(1);

    /*对从站发起连接*/
    mb.modbus_connect();

    /*读线圈(function code 0x01)*/
    bool read_coil; /*tab_bits可读写*/
    mb.modbus_read_coils(0, 1, &read_coil);

    /*读输入位(discrete input function code 0x02)*/
    bool read_bits; /*tab_input_bits只读*/
    mb.modbus_read_input_bits(0, 1, &read_bits);

    /*读保持寄存器(function code 0x03)*/
    uint16_t read_holding_regs[1]; /*tab_registers可读写*/
    mb.modbus_read_holding_registers(0, 1, read_holding_regs);

    /*读输入寄存器(function code 0x04)*/
    uint16_t read_input_regs[1]; /*tab_input_registers只读*/
    mb.modbus_read_input_registers(0, 1, read_input_regs);

    /*写单线圈(function code 0x05)*/
    mb.modbus_write_coil(0, true);/*tab_bits可读写*/

    /*写单寄存器(function code 0x06)*/
    mb.modbus_write_register(0, 123);/*tab_registers可读写*/

    /*写多线圈(function code 0x0F)*/
    bool write_cols[4] = {true, true, true, true};/*tab_bits可读写*/
    mb.modbus_write_coils(0, 4, write_cols);

    /*写多寄存器(function code 0x10)*/
    uint16_t write_regs[4] = {123, 123, 123};/*tab_registers可读写*/
    mb.modbus_write_registers(0, 4, write_regs);

    /*关闭连接以及释放内存*/
    mb.modbus_close();
    return 0;
}
/*g++ ModbusTcpMaster.cpp main.cpp --std=c++11 -o ModbusTcpMaster
事务 长度  地址 code 起始高 起始低 数量高 数量低
0x00000000 0x0006 0x01 0x03 0x00 0x6B 0x00 0x02
    uint8_t d1 = 0x01;
    uint8_t d2 = 0x02;
    uint16_t wd = ((uint16_t)d2 << 8) | d1;
    d1 d2 d3 ... dn-1 ... dn

    uint16_t d1 = 0x01;
    uint16_t d2 = 0x02;
    uint32_t wd = ((uint16_t)d2 << 16) | d1;
    d1 d2 d3 ... dn-1 ... dn
*/
#include "ModbusTcpMaster.h"
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Constructor
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
ModbusTcpMaster::ModbusTcpMaster(std::string host, uint16_t port = 502)
{
    HOST = host;        /*Host IP*/
    PORT = port;        /*Host PORT(default 502)*/
    _slaveid = 1;
    _msg_id = 1;
    _connected = false;
    err = false;
    err_no = 0;
    error_msg = "";
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Set the slave address
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
void ModbusTcpMaster::modbus_set_slave_id(int id)
{
    _slaveid = id;
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Connect from the station
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
bool ModbusTcpMaster::modbus_connect()
{
    if(HOST.empty() || PORT == 0)
    {
        LOG("Missing Host and Port\n");
        return false;
    }
    else
    {
        LOG("Found Proper Host %s and Port %d\n", HOST.c_str(), PORT);
    }

#ifdef _WIN32
    if (WSAStartup(0x0202, &wsadata))
    {
        return false;
    }
#endif

    _socket = socket(AF_INET, SOCK_STREAM, 0);
    if (!X_ISVALIDSOCKET(_socket))
    {
        LOG("Error Opening Socket\n");
#ifdef _WIN32
        WSACleanup();
#endif
        return false;
    }
    else
    {
        LOG("Socket Opened Successfully\n");
    }

#ifdef WIN32
    const DWORD timeout = 20;
#else
    struct timeval timeout
    {
    };
    /*after 20 seconds connect() will timeout*/
    timeout.tv_sec = 20;
    timeout.tv_usec = 0;
#endif

    /*设置套接字属性以及填充结构体*/
    setsockopt(_socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout));
    setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout));
    _server.sin_family = AF_INET;
    _server.sin_addr.s_addr = inet_addr(HOST.c_str());
    _server.sin_port = htons(PORT);

    if(!X_ISCONNECTSUCCEED(connect(_socket, (SOCKADDR *)&_server, sizeof(_server))))
    {
        LOG("Connection Error\n");
#ifdef _WIN32
        WSACleanup();
#endif
        return false;
    }
    LOG("Connected\n");
    _connected = true;
    return true;
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      close Connect
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
void ModbusTcpMaster::modbus_close() const
{
    X_CLOSE_SOCKET(_socket);
#ifdef _WIN32
    WSACleanup();
#endif
    LOG("Socket Closed\n");
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Modbus Request Builder
 * @param      to_send   Message Buffer to Be Sent
 * @param      address   Reference Address
 * @param      func      Modbus Functional Code
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
void ModbusTcpMaster::modbus_build_request(uint8_t *to_send, uint16_t address, int func) const
{
    to_send[0] = (uint8_t)(_msg_id >> 8u);
    to_send[1] = (uint8_t)(_msg_id & 0x00FFu);
    to_send[2] = 0;
    to_send[3] = 0;
    to_send[4] = 0;
    to_send[6] = (uint8_t)_slaveid;
    to_send[7] = (uint8_t)func;
    to_send[8] = (uint8_t)(address >> 8u);
    to_send[9] = (uint8_t)(address & 0x00FFu);
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Write Request Builder and Sender
 * @param      address   Reference Address
 * @param      amount    Amount of data to be Written
 * @param      func      Modbus Functional Code
 * @param      value     Data to Be Written
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
int ModbusTcpMaster::modbus_write(uint16_t address, uint16_t amount, int func, const uint16_t *value)
{
    int status = 0;
    uint8_t *to_send = nullptr;
    if(func == WRITE_COIL || func == WRITE_REG)
    {
        to_send = new uint8_t[12];
        modbus_build_request(to_send, address, func);
        to_send[5] = 6;
        to_send[10] = (uint8_t)(value[0] >> 8u);
        to_send[11] = (uint8_t)(value[0] & 0x00FFu);
        status = modbus_send(to_send, 12);
    }
    else if(func == WRITE_REGS)
    {
        to_send = new uint8_t[13 + 2 * amount];
        modbus_build_request(to_send, address, func);
        to_send[5] = (uint8_t)(7 + 2 * amount);
        to_send[10] = (uint8_t)(amount >> 8u);
        to_send[11] = (uint8_t)(amount & 0x00FFu);
        to_send[12] = (uint8_t)(2 * amount);
        for (int i = 0; i < amount; i++)
        {
            to_send[13 + 2 * i] = (uint8_t)(value[i] >> 8u);
            to_send[14 + 2 * i] = (uint8_t)(value[i] & 0x00FFu);
        }
        status = modbus_send(to_send, 13 + 2 * amount);
    }
    else if(func == WRITE_COILS)
    {
        to_send = new uint8_t[14 + (amount - 1) / 8];
        modbus_build_request(to_send, address, func);
        to_send[5] = (uint8_t)(7 + (amount + 7) / 8);
        to_send[10] = (uint8_t)(amount >> 8u);
        to_send[11] = (uint8_t)(amount & 0x00FFu);
        to_send[12] = (uint8_t)((amount + 7) / 8);
        for(int i = 0; i < (amount + 7) / 8; i++)
            to_send[13 + i] = 0; /*init needed before summing*/
        for(int i = 0; i < amount; i++)
        {
            to_send[13 + i / 8] += (uint8_t)(value[i] << (i % 8u));
        }
        status = modbus_send(to_send, 14 + (amount - 1) / 8);
    }
    delete[] to_send;
    return status;
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Read Request Builder and Sender
 * @param      address   Reference Address
 * @param      amount    Amount of data to be Written
 * @param      func      Modbus Functional Code
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
int ModbusTcpMaster::modbus_read(uint16_t address, uint16_t amount, int func)
{
    uint8_t to_send[12];
    modbus_build_request(to_send, address, func);
    to_send[5] = 6;
    to_send[10] = (uint8_t)(amount >> 8u);
    to_send[11] = (uint8_t)(amount & 0x00FFu);
    return modbus_send(to_send, 12);
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Read Holding Registers / MODBUS FUNCTION 0x03
 * @param      address   Reference Address
 * @param      amount    Amount of Registers to Read
 * @param      buffer     Buffer to Store Data Read from Registers
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
int ModbusTcpMaster::modbus_read_holding_registers(uint16_t address, uint16_t amount, uint16_t *buffer)
{
    if(_connected)
    {
        modbus_read(address, amount, READ_REGS);
        uint8_t to_rec[MAX_MSG_LENGTH];
        ssize_t k = modbus_receive(to_rec);
        if(k == -1)
        {
            set_bad_con();
            return BAD_CON;
        }
        modbuserror_handle(to_rec, READ_REGS);
        if (err)
            return err_no;
        for (auto i = 0; i < amount; i++)
        {
            buffer[i] = ((uint16_t)to_rec[9u + 2u * i]) << 8u;
            buffer[i] += (uint16_t)to_rec[10u + 2u * i];
        }
        return 0;
    }
    else
    {
        set_bad_con();
        return BAD_CON;
    }
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Read Input Registers / MODBUS FUNCTION 0x04
 * @param      address   Reference Address
 * @param      amount    Amount of Registers to Read
 * @param      buffer    Buffer to Store Data Read from Registers
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
int ModbusTcpMaster::modbus_read_input_registers(uint16_t address, uint16_t amount, uint16_t *buffer)
{
    if(_connected)
    {
        modbus_read(address, amount, READ_INPUT_REGS);
        uint8_t to_rec[MAX_MSG_LENGTH];
        ssize_t k = modbus_receive(to_rec);
        if(k == -1)
        {
            set_bad_con();
            return BAD_CON;
        }
        modbuserror_handle(to_rec, READ_INPUT_REGS);
        if(err)
            return err_no;
        for(auto i = 0; i < amount; i++)
        {
            buffer[i] = ((uint16_t)to_rec[9u + 2u * i]) << 8u;
            buffer[i] += (uint16_t)to_rec[10u + 2u * i];
        }
        return 0;
    }
    else
    {
        set_bad_con();
        return BAD_CON;
    }
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Read Coils / MODBUS FUNCTION 0x01
 * @param      address   Reference Address
 * @param      amount    Amount of Coils to Read
 * @param      buffer    Buffer to Store Data Read from Coils
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
int ModbusTcpMaster::modbus_read_coils(uint16_t address, uint16_t amount, bool *buffer)
{
    if(_connected)
    {
        if(amount > 2040)
        {
            set_bad_input();
            return EX_BAD_DATA;
        }
        modbus_read(address, amount, READ_COILS);
        uint8_t to_rec[MAX_MSG_LENGTH];
        ssize_t k = modbus_receive(to_rec);
        if(k == -1)
        {
            set_bad_con();
            return BAD_CON;
        }
        modbuserror_handle(to_rec, READ_COILS);
        if(err)
            return err_no;
        for(auto i = 0; i < amount; i++)
        {
            buffer[i] = (bool)((to_rec[9u + i / 8u] >> (i % 8u)) & 1u);
        }
        return 0;
    }
    else
    {
        set_bad_con();
        return BAD_CON;
    }
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Read Input Bits(Discrete Data) / MODBUS FUNCTION 0x02
 * @param      address   Reference Address
 * @param      amount    Amount of Bits to Read
 * @param      buffer    Buffer to store Data Read from Input Bits
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
int ModbusTcpMaster::modbus_read_input_bits(uint16_t address, uint16_t amount, bool *buffer)
{
    if(_connected)
    {
        if(amount > 2040)
        {
            set_bad_input();
            return EX_BAD_DATA;
        }
        modbus_read(address, amount, READ_INPUT_BITS);
        uint8_t to_rec[MAX_MSG_LENGTH];
        ssize_t k = modbus_receive(to_rec);
        if(k == -1)
        {
            set_bad_con();
            return BAD_CON;
        }
        if(err)
            return err_no;
        for(auto i = 0; i < amount; i++)
        {
            buffer[i] = (bool)((to_rec[9u + i / 8u] >> (i % 8u)) & 1u);
        }
        modbuserror_handle(to_rec, READ_INPUT_BITS);
        return 0;
    }
    else
    {
        return BAD_CON;
    }
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Write Single Coils / MODBUS FUNCTION 0x05
 * @param      address    Reference Address
 * @param      to_write   Value to be Written to Coil
 * @author     seer-txj
 * @version    v1
 * @return     0/bad
 * @date       2021/10/3
 **************************************************************/
int ModbusTcpMaster::modbus_write_coil(uint16_t address, const bool &to_write)
{
    if(_connected)
    {
        int value = to_write * 0xFF00;
        modbus_write(address, 1, WRITE_COIL, (uint16_t *)&value);
        uint8_t to_rec[MAX_MSG_LENGTH];
        ssize_t k = modbus_receive(to_rec);
        if(k == -1)
        {
            set_bad_con();
            return BAD_CON;
        }
        modbuserror_handle(to_rec, WRITE_COIL);
        if (err)
            return err_no;
        return 0;
    }
    else
    {
        set_bad_con();
        return BAD_CON;
    }
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Write Single Register / MODBUS FUNCTION 0x06
 * @param      address   Reference Address
 * @param      value     Value to Be Written to Register
 * @author     seer-txj
 * @version    v1
 * @return     0/bad
 * @date       2021/10/3
 **************************************************************/
int ModbusTcpMaster::modbus_write_register(uint16_t address, const uint16_t &value)
{
    if(_connected)
    {
        modbus_write(address, 1, WRITE_REG, &value);
        uint8_t to_rec[MAX_MSG_LENGTH];
        ssize_t k = modbus_receive(to_rec);
        if(k == -1)
        {
            set_bad_con();
            return BAD_CON;
        }
        modbuserror_handle(to_rec, WRITE_COIL);
        if (err)
            return err_no;
        return 0;
    }
    else
    {
        set_bad_con();
        return BAD_CON;
    }
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Write Multiple Coils / MODBUS FUNCTION 0x0F
 * @param      address  Reference Address
 * @param      amount   Amount of Coils to Write
 * @param      value    Values to Be Written to Coils
 * @author     seer-txj
 * @version    v1
 * @return     0/bad
 * @date       2021/10/3
 **************************************************************/
int ModbusTcpMaster::modbus_write_coils(uint16_t address, uint16_t amount, const bool *value)
{
    if(_connected)
    {
        uint16_t *temp = new uint16_t[amount];
        for(int i = 0; i < amount; i++)
        {
            temp[i] = (uint16_t)value[i];
        }
        modbus_write(address, amount, WRITE_COILS, temp);
        delete[] temp;
        uint8_t to_rec[MAX_MSG_LENGTH];
        ssize_t k = modbus_receive(to_rec);
        if(k == -1)
        {
            set_bad_con();
            return BAD_CON;
        }
        modbuserror_handle(to_rec, WRITE_COILS);
        if(err)
            return err_no;
        return 0;
    }
    else
    {
        set_bad_con();
        return BAD_CON;
    }
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Write Multiple Registers / MODBUS FUNCION 0x10
 * @param      address Reference Address
 * @param      amount  Amount of Value to Write
 * @param      value   Values to Be Written to the Registers
 * @author     seer-txj
 * @version    v1
 * @return     0/bad
 * @date       2021/10/3
 **************************************************************/
int ModbusTcpMaster::modbus_write_registers(uint16_t address, uint16_t amount, const uint16_t *value)
{
    if(_connected)
    {
        modbus_write(address, amount, WRITE_REGS, value);
        uint8_t to_rec[MAX_MSG_LENGTH];
        ssize_t k = modbus_receive(to_rec);
        if(k == -1)
        {
            set_bad_con();
            return BAD_CON;
        }
        modbuserror_handle(to_rec, WRITE_REGS);
        if(err)
            return err_no;
        return 0;
    }
    else
    {
        set_bad_con();
        return BAD_CON;
    }
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Data Sender
 * @param      to_send Request to Be Sent to Server
 * @param      length  Length of the Request
 * @author     seer-txj
 * @version    v1
 * @return     Size of the request
 * @date       2021/10/3
 **************************************************************/
ssize_t ModbusTcpMaster::modbus_send(uint8_t *to_send, size_t length)
{
    _msg_id++;
    return send(_socket, (const char *)to_send, (size_t)length, 0);
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Data Receiver
 * @param      buffer Buffer to Store the Data Retrieved
 * @author     seer-txj
 * @version    v1
 * @return     Size of Incoming Data
 * @date       2021/10/3
 **************************************************************/
ssize_t ModbusTcpMaster::modbus_receive(uint8_t *buffer) const
{
    return recv(_socket, (char *)buffer, 1024, 0);
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Error Code Handler
 * @param      msg   Message Received from the Server
 * @param      func  Modbus Functional Code
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
void ModbusTcpMaster::modbuserror_handle(const uint8_t *msg, int func)
{
    err = false;
    error_msg = "NO ERR";
    if(msg[7] == func + 0x80)
    {
        err = true;
        switch (msg[8])
        {
        case EX_ILLEGAL_FUNCTION:
            error_msg = "1 Illegal Function";
            break;
        case EX_ILLEGAL_ADDRESS:
            error_msg = "2 Illegal Address";
            break;
        case EX_ILLEGAL_VALUE:
            error_msg = "3 Illegal Value";
            break;
        case EX_SERVER_FAILURE:
            error_msg = "4 Server Failure";
            break;
        case EX_ACKNOWLEDGE:
            error_msg = "5 Acknowledge";
            break;
        case EX_SERVER_BUSY:
            error_msg = "6 Server Busy";
            break;
        case EX_NEGATIVE_ACK:
            error_msg = "7 Negative Acknowledge";
            break;
        case EX_MEM_PARITY_PROB:
            error_msg = "8 Memory Parity Problem";
            break;
        case EX_GATEWAY_PROBLEMP:
            error_msg = "10 Gateway Path Unavailable";
            break;
        case EX_GATEWYA_PROBLEMF:
            error_msg = "11 Gateway Target Device Failed to Respond";
            break;
        default:
            error_msg = "UNK";
            break;
        }
    }
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      Error Code type
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
void ModbusTcpMaster::set_bad_con()
{
    err = true;
    error_msg = "BAD CONNECTION";
}
/***************************************************************
 * @file       ModbusTcpMaster.cpp
 * @brief      input Error
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/3
 **************************************************************/
void ModbusTcpMaster::set_bad_input()
{
    err = true;
    error_msg = "BAD FUNCTION INPUT";
}
#ifndef MODBUSTCP_H
#define MODBUSTCP_H

#include <cstring>
#include <stdint.h>
#include <string>
#include <iostream>
#include <thread>
using namespace std;
#define ENABLE_MODBUSTCP_LOGGING debuglog

/*调试输出*/
#ifdef ENABLE_MODBUSTCP_LOGGING
#include <cstdio>
#define LOG(fmt, ...) printf("[ModbusTcp_DebugLog]" fmt, ##__VA_ARGS__)
#else
#define LOG(...) (void)0
#endif

/*如果是windows平台则要加载相应的静态库和头文件*/
#ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
using X_SOCKET = SOCKET;
using ssize_t = int;

#define X_ISVALIDSOCKET(s) ((s) != INVALID_SOCKET)
#define X_CLOSE_SOCKET(s) closesocket(s)
#define X_ISCONNECTSUCCEED(s) ((s) != SOCKET_ERROR)

/*linux平台*/
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using X_SOCKET = int;

#define X_ISVALIDSOCKET(s) ((s) >= 0)
#define X_CLOSE_SOCKET(s) close(s)
#define X_ISCONNECTSUCCEED(s) ((s) >= 0)
#endif
/*类型重定义*/
using SOCKADDR = struct sockaddr;
using SOCKADDR_IN = struct sockaddr_in;

#define    MAX_MSG_LENGTH    260
#define    BAD_CON           -1

/*功能码*/
#define    READ_COILS         0x01      //读线圈
#define    READ_INPUT_BITS    0x02      //读离散输入状态
#define    READ_REGS          0x03      //读保持寄存器
#define    READ_INPUT_REGS    0x04      //读输入寄存器
#define    WRITE_COIL         0x05      //写线圈状态
#define    WRITE_REG          0x06      //写单个保持寄存器
#define    WRITE_COILS        0x0F      //写多个线圈
#define    WRITE_REGS         0x10      //写多个保持寄存器

/*异常码*/
#define    EX_ILLEGAL_FUNCTION    0x01    //Function Code not Supported
#define    EX_ILLEGAL_ADDRESS     0x02    //Output Address not exists
#define    EX_ILLEGAL_VALUE       0x03    //Output Value not in Range
#define    EX_SERVER_FAILURE      0x04    //Slave Deive Fails to process request
#define    EX_ACKNOWLEDGE         0x05    //Service Need Long Time to Execute
#define    EX_SERVER_BUSY         0x06    //Server Was Unable to Accept MB Request PDU
#define    EX_NEGATIVE_ACK        0x07
#define    EX_MEM_PARITY_PROB     0x08
#define    EX_GATEWAY_PROBLEMP    0x0A    //Gateway Path not Available
#define    EX_GATEWYA_PROBLEMF    0x0B    //Target Device Failed to Response
#define    EX_BAD_DATA            0XFF    //Bad Data lenght or Address

/*定义一个ModbusTcp功能的类*/
class ModbusTcpMaster
{
public:
    bool err{};
    int err_no{};
    std::string error_msg;
    ModbusTcpMaster(std::string host, uint16_t port);
    ~ModbusTcpMaster() = default;
public:
    bool modbus_connect();
    void modbus_close() const;
    void modbus_set_slave_id(int id);
    int modbus_read_coils(uint16_t address, uint16_t amount, bool *buffer);
    int modbus_read_input_bits(uint16_t address, uint16_t amount, bool *buffer);
    int modbus_read_holding_registers(uint16_t address, uint16_t amount, uint16_t *buffer);
    int modbus_read_input_registers(uint16_t address, uint16_t amount, uint16_t *buffer);
    int modbus_write_coil(uint16_t address, const bool &to_write);
    int modbus_write_register(uint16_t address, const uint16_t &value);
    int modbus_write_coils(uint16_t address, uint16_t amount, const bool *value);
    int modbus_write_registers(uint16_t address, uint16_t amount, const uint16_t *value);
private:
    bool _connected{};
    uint16_t PORT{};
    uint32_t _msg_id{};
    int _slaveid{};
    std::string HOST;
    X_SOCKET _socket{};
    SOCKADDR_IN _server{};
#ifdef _WIN32
    WSADATA wsadata;
#endif
    inline void set_bad_con();
    inline void set_bad_input();
    inline ssize_t modbus_receive(uint8_t *buffer) const;
    void modbuserror_handle(const uint8_t *msg, int func);
    inline ssize_t modbus_send(uint8_t *to_send, size_t length);
    int modbus_read(uint16_t address, uint16_t amount, int func);
    int modbus_write(uint16_t address, uint16_t amount, int func, const uint16_t *value);
    inline void modbus_build_request(uint8_t *to_send, uint16_t address, int func) const;
};

#endif

主站:

#include "ModbusTcpSlave.h"
void modbusRunner(ModbusTcpSlave *server)
{
    server->initModbus(502, true);
    server->recieveMessages();
}
ModbusTcpSlave modSer;
int main()
{
    std::thread modSerThread(modbusRunner, &modSer);
    modSerThread.join();
    std::cout << "Running? " << modSer.isRunning() << std::endl;
    return 0;
}

/*g++ -g -Wall -pthread -libmodbus ModbusTcpSlave.cpp main.cpp --std=c++11 -o ModbusTcpSlave
使用小于1024的端口时,需要以管理员权限启动,sudo ./ModbusTcpSlave*/
#include "ModbusTcpSlave.h"
/***************************************************************
 * @file       ModbusTcpSlave.cpp
 * @author     seer-txj
 * @brief      Constructor
 * @version    v1
 * @return     null
 * @date       2021/10/6
 **************************************************************/
ModbusTcpSlave::ModbusTcpSlave()
{
    m_initialized = false;
    mapping = modbus_mapping_new(m_numBits, m_numInputBits, m_numRegisters, m_numInputRegisters);
}
/***************************************************************
 * @file       ModbusTcpSlave.cpp
 * @author     seer-txj
 * @brief      Destructor
 * @version    v1
 * @return     null
 * @date       2021/10/6
 **************************************************************/
ModbusTcpSlave::~ModbusTcpSlave() 
{
    modbus_mapping_free(mapping);
    modbus_close(ctx);
    modbus_free(ctx);
}
/***************************************************************
 * @file       ModbusTcpSlave.cpp
 * @author     seer-txj
 * @brief      modbus initialization flag
 * @version    v1
 * @return     null
 * @date       2021/10/6
 **************************************************************/
bool ModbusTcpSlave::isRunning()
{
    return m_initialized;
}
/***************************************************************
 * @file       ModbusTcpSlave.cpp
 * @author     seer-txj
 * @brief      modbus initialization
 * @param      IP/PORT/debugflag
 * @version    v1
 * @return     null
 * @date       2021/10/6
 **************************************************************/
bool ModbusTcpSlave::initModbus(std::string Host_Ip = "127.0.0.1", int port = 502, bool debugging)
{
    ctx = modbus_new_tcp(Host_Ip.c_str(), port);
    modbus_set_debug(ctx, debugging);
    if(ctx == NULL)
    {
        std::cerr << "There was an error allocating the modbus" << std::endl;
        throw -1;
    }
    /*int rc = modbus_set_slave(ctx, 1);
    if(rc == -1)
    {
        fprintf(stderr,"无效的从站 ID\n");
        modbus_free(ctx);
        return -1;
    }*/
    m_modbusSocket  = modbus_tcp_listen(ctx, 1);
    /*设置线圈, 离散输入, 输入寄存器, 保持寄存器个数*/
    mapping = modbus_mapping_new(500, 500, 500, 500);
    m_initialized = true;
    return true;
}
/***************************************************************
 * @file       ModbusTcpSlave.cpp
 * @author     seer-txj
 * @brief      setRegisterValue
 * @param      registerNumber/Value
 * @version    v1
 * @return     null
 * @date       2021/10/6
 **************************************************************/
bool ModbusTcpSlave::setRegisterValue(int registerNumber, uint16_t Value)
{
    if(registerNumber > m_numRegisters)
    {
        return false;
    }
    mapping->tab_registers[registerNumber] = Value;
    return true;
}
/***************************************************************
 * @file       ModbusTcpSlave.cpp
 * @author     seer-txj
 * @brief      getRegisterValue
 * @param      registerNumber
 * @version    v1
 * @return     null
 * @date       2021/10/6
 **************************************************************/
uint16_t ModbusTcpSlave::getRegisterValue(int registerNumber)
{
    if(!m_initialized)
    {
        return -1;
    }
    std::mutex mappingLock;
    std::lock_guard<std::mutex> lock(mappingLock);
    uint16_t registerVal = mapping->tab_registers[registerNumber];
    return registerVal;
}
/***************************************************************
 * @file       ModbusTcpSlave.cpp
 * @author     seer-txj
 * @brief      getTab_Input_Bits
 * @param      NumBit
 * @version    v1
 * @return     null
 * @date       2021/10/9
 **************************************************************/
uint8_t ModbusTcpSlave::getTab_Input_Bits(int NumBit)
{
    if(!m_initialized)
    {
        return -1;
    }
    std::mutex mappingLock;
    std::lock_guard<std::mutex> lock(mappingLock);
    uint8_t BitValue = mapping->tab_input_bits[NumBit];
    return BitValue;
}
/***************************************************************
 * @file       ModbusTcpSlave.cpp
 * @author     seer-txj
 * @brief      setTab_Input_Bits
 * @param      NumBit/Value
 * @version    v1
 * @return     null
 * @date       2021/10/9
 **************************************************************/
bool ModbusTcpSlave::setTab_Input_Bits(int NumBit, uint8_t Value)
{
    if(NumBit > m_numInputBits)
    {
        return false;
    }
    mapping->tab_input_bits[NumBit] = Value;
    return true;
}
/***************************************************************
 * @file       ModbusTcpSlave.cpp
 * @author     seer-txj
 * @version    v1
 * @return     null
 * @date       2021/10/6
 **************************************************************/
void ModbusTcpSlave::recieveMessages()
{
    int ret;
    uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
    modbus_tcp_accept(ctx, &m_modbusSocket);
    if(m_modbusSocket == -1)
    {
        std::cerr << modbus_strerror(errno) << std::endl;
    }
    for(;;)
    {
        ret = modbus_receive(ctx, query);
        if(ret == 0)
        {
            m_errCount = 0;
            continue;
        } 
        else if(ret > 0)
        {
            m_errCount = 0;
            modbus_reply(ctx, query, sizeof(query), mapping);
        }
        else
        {
            modbus_set_error_recovery(ctx, MODBUS_ERROR_RECOVERY_LINK);
            modbus_close(ctx);
            modbus_tcp_accept(ctx, &m_modbusSocket);
            modbus_set_error_recovery(ctx, MODBUS_ERROR_RECOVERY_NONE);
            m_errCount++;
        }
        if(m_errCount > 5)
        {
            m_initialized = false;
            break;
        }
    }
}
#ifndef MODBUSTCPSLAVE_H
#define MODBUSTCPSLAVE_H
#include <iostream>
#include <thread>
#include <stdlib.h>
#include <iostream>
#include <mutex>
#include <string>
#include <modbus.h>
//#include <modbus/modbus.h>
using namespace std;
/*如果是windows平台则要加载相应的静态库和头文件*/
#ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS
/*https://www.jianshu.com/p/074f93491201*/
#include <winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "modbus.lib")
/*linux平台*/
#else
#include <unistd.h>
#include <error.h>
#endif
class ModbusTcpSlave
{
public:
    ModbusTcpSlave();
    ~ModbusTcpSlave();
public:
    bool        isRunning();
    void        recieveMessages();
    bool        initModbus(std::string Host_Ip = "127.0.0.1", int port = 502, bool debugging);
    uint16_t    getRegisterValue(int registerNumber);
    bool        setRegisterValue(int registerNumber, uint16_t Value);
    uint8_t     getTab_Input_Bits(int NumBit);
    bool        setTab_Input_Bits(int NumBit, uint8_t Value);
private:
    modbus_t            *ctx;
    modbus_mapping_t    *mapping;
    bool        m_initialized;
    int         m_modbusSocket;
    int         m_port;
    int         m_errCount;
    /*Mapping*/
    int         m_numBits;
    int         m_numInputBits;
    int         m_numRegisters;
    int         m_numInputRegisters;
};
/*Annotation:
(1)https://www.jianshu.com/p/0ed380fa39eb
(2)typedef struct _modbus_mapping_t
{
    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;*/
#endif //MODBUSTCPSLAVE_H

CmakeLists.txt:

#cmake_minimum_required(VERSION 3.5.2)
project(ModbusTcpSlave)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

INCLUDE(FindPkgConfig)
pkg_check_modules(MODBUS REQUIRED libmodbus)
include_directories(${MODBUS_INCLUDE_DIRS})
link_directories(${MODBUS_LIBRARY_DIRS})

set(SOURCE_FILES main.cpp)
add_executable(ModbusTcpSlave ${SOURCE_FILES} ModbusTcpSlave.cpp ModbusTcpSlave.h)

target_link_libraries(ModbusTcpSlave ${MODBUS_LIBRARIES} pthread)
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值