深入浅出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 0x02uint8_t d1 = 0x01;uint8_t d2 = 0x02;uint16_t wd = ((uint16_t)d2 << 8) | d1;d1 d2 d3 ... dn-1 ... dnuint16_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 _WIN32if (WSAStartup(0x0202, &wsadata)){return false;}
#endif_socket = socket(AF_INET, SOCK_STREAM, 0);if (!X_ISVALIDSOCKET(_socket)){LOG("Error Opening Socket\n");
#ifdef _WIN32WSACleanup();
#endifreturn false;}else{LOG("Socket Opened Successfully\n");}#ifdef WIN32const DWORD timeout = 20;
#elsestruct 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 _WIN32WSACleanup();
#endifreturn 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 _WIN32WSACleanup();
#endifLOG("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 _WIN32WSADATA wsadata;
#endifinline 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)
深入浅出ModbusTcp相关推荐
- Python --深入浅出Apriori关联分析算法(二) Apriori关联规则实战
上一篇我们讲了关联分析的几个概念,支持度,置信度,提升度.以及如何利用Apriori算法高效地根据物品的支持度找出所有物品的频繁项集. Python --深入浅出Apriori关联分析算法(一) 这次 ...
- MSDN Webcast“深入浅出ASP.NET AJAX系列”
课程: ASP.NET AJAX深入浅出系列课程(1):ASP.NET AJAX 概述(3月13日):对于ASP.NET AJAX的大致功能进行概述和演示,通过简单的演示让听众了解到ASP.NET A ...
- 5.3Role和Claims授权「深入浅出ASP.NET Core系列」
5.3Role和Claims授权「深入浅出ASP.NET Core系列」 原文:5.3Role和Claims授权「深入浅出ASP.NET Core系列」 希望给你3-5分钟的碎片化学习,可能是坐地铁. ...
- 深入浅出开源性能测试工具 Locust (使用篇 1)
在<[LocustPlus序]漫谈服务端性能测试>中,我对服务端性能测试的基础概念和性能测试工具的基本原理进行了介绍,并且重点推荐了Locust这一款开源性能测试工具.然而,当前在网络上针 ...
- 《深入浅出iPhone/iPad开发(第2版)》——在Xcode中建立你的界面
本节书摘来自异步社区<深入浅出iPhone/iPad开发(第2版)>一书中的在Xcode中建立你的界面,作者 [美]Dan Pilone , Tracey Pilone,更多章节内容可以访 ...
- 【组队学习】【35期】深入浅出Pytorch
深入浅出Pytorch 航路开辟者:李嘉骐.牛志康.刘洋.陈安东 领航员:朱松青 航海士:管柯琴.宋泽山.林旭升 基本信息 开源内容:https://github.com/datawhalechina ...
- 深入浅出Pytorch:02 PyTorch基础知识
深入浅出Pytorch 02 PyTorch基础知识 内容属性:深度学习(实践)专题 航路开辟者:李嘉骐.牛志康.刘洋.陈安东 领航员:叶志雄 航海士:李嘉骐.牛志康.刘洋.陈安东 开源内容:http ...
- 深入浅出Pytorch:01 课程大纲与PyTorch简介
深入浅出Pytorch 01 课程大纲与PyTorch简介 内容属性:深度学习(实践)专题 航路开辟者:李嘉骐.牛志康.刘洋.陈安东 领航员:叶志雄 航海士:李嘉骐.牛志康.刘洋.陈安东 开源内容:h ...
- 今晚8点直播 | 深入浅出理解A3C强化学习
强化学习是一种比较传统的人工智能手段,在近年来随着深度学习的发展,强化学习和深度学习逐渐结合在了一起.这种结合使得很多原来无法想象的工作有了可能,最令我们瞩目的莫过于AlphaGo战胜李世石,以及Op ...
最新文章
- usb-key登录windows+远程桌面
- Eclipse中使用SVN
- 测试两个主机之间的连通性_UCloud 全链路大规模网络连通性检测系统详解
- 项目经理升职了是啥_什么是升职率?
- pandas绘图_Pandas内置绘图方法(线型图、柱状图、密度图)
- 《可用性测试手册(第2版)》一第1章 什么造就了可用性1.1 “可用”究竟是什么...
- emacs VS vim 替换为回车符
- python的sorted函数和operator.itemgetter函数
- matlab离散信号z变换,离散信号与系统的Z变换分析
- 新买的华为Matebook,Office没激活,激活方法在这里!!!
- 百度网盘,到底限了谁的速?
- 若干物联网无线技术 - NB-IOT、LoRa、433、GPRS、2.4G、PKE近场通信,基础理论与开发点滴总结
- Go 限流器 limter
- 微信开发 ━━ 微信商户v3微信支付回调之php篇
- 龙腾世纪:起源–最后的古代墓碑和剑圣盔甲
- GraphX 在图数据库 Nebula Graph 的图计算实践
- Air724UG 4G LTE 模块AT指令连接服务器
- linux shell遍历多个数组
- Flutter 加载WebView(加载网页)
- 【CCF会议期刊推荐】CCF推荐国际学术期刊/会议(网络与信息安全)
热门文章
- All Attention You Need
- 【自然语言处理】【向量检索】面向开放域稠密检索的多视角文档表示学习
- 舔狗日记 API数据接口
- Qbao Network携手FinCredit Protocol启动大规模空投
- PB 饼状图制作过程
- 一览Polkadot平行链Moonbeam生态的应用
- Python相对引用报错ImportError: attempted relative import with no known parent package的处理方法
- 计算机音乐安顺学院教务网络管理系统,安顺学院教务网络管理系统jwxt.asu.edu.cn/jwweb/...
- DVDRW光驱无法读DVD刻录盘
- 长方形纸做容积最大的长方体_探究无盖长方体的最大容积