前言
之前经常使用mbpoll在Linux命令行下调试modbus,用久了发现其实并不方便,因为每次携带的参数太多,使用的时候老是忘记怎么使用,经常需要去翻笔记,于是决定自己开发一款Linux命令行版本的Modbus调试工具,方便调试。
项目开源地址:https://gitee.com/chen-dongyu123/modbus_tools
Linux终端命令行图形化Modbus调试助手
-
连接ModbusTcp服务器
-
连接Modbus串口
-
查询和写入寄存器,先在菜单栏上选择寄存器类型,然后可以根据从机地址,起始寄存器地址,结束寄存器地址读取范围类的寄存器数据,支持单个数据写入寄存器
-
查询结果
该工具完全支持无鼠标键盘操作,Esc可以关闭页面,Tab键切换输入框,上下左右键可用来上下滚动表格。
Modbus客户端重点代码实现
-
创建客户端基类,主要有以下几个方法
#pragma once #include <vector> #include <string> #include "libmodbus/src/modbus.h" class ModbusBaseClient { public: ModbusBaseClient() = default; ~ModbusBaseClient(); bool isConnected(); // 线圈寄存器(功能码0x01,可读可写) std::vector<int> readCoilValues(int slaveId, int addr, int cnt); bool writeCoilValue(int slaveId, int addr, int value); // 离散输入寄存器(功能码0x02,只读) std::vector<int> readDiscreteInputValues(int slaveId, int addr, int cnt); // 保持寄存器(功能码0x03,可读可写) std::vector<int> readHoldRegisterValues(int slaveId, int addr, int cnt); bool writeHoldRegisterValue(int slaveId, int addr, int value); // 输入寄存器(功能码0x04,只读) std::vector<int> readInputRegisterValues(int slaveId, int addr, int cnt); // 根据地址范围读取寄存器,起始地址-结束地址 std::vector<int> readRangeCoilValues(int slaveId, int startAddr, int endAddr); std::vector<int> readRangeDiscreteInputValues(int slaveId, int startAddr, int endAddr); std::vector<int> readRangeHoldRegisterValues(int slaveId, int startAddr, int endAddr); std::vector<int> readRangeInputRegisterValues(int slaveId, int startAddr, int endAddr); protected: modbus_t *_ctx{nullptr}; bool _isConnected{false}; };
-
客户端基类实现
#include "modbus_base_client.h" #include "log.h" bool ModbusBaseClient::isConnected() { return _isConnected; } ModbusBaseClient::~ModbusBaseClient() { if (_ctx != nullptr) { modbus_close(_ctx); modbus_free(_ctx); } } std::vector<int> ModbusBaseClient::readCoilValues(int slaveId, int addr, int cnt) { modbus_set_slave(_ctx, slaveId); uint8_t dest[cnt]; memset(dest, 0, cnt * sizeof(uint8_t)); int rc = modbus_read_bits(_ctx, addr, cnt, dest); if (rc == -1) { LOG_ERROR(fmt::format("readCoilValues failed, {}", modbus_strerror(errno))); return {}; } std::vector<int> result; for (const auto &val : dest) { result.push_back(static_cast<int>(val)); } return result; } bool ModbusBaseClient::writeCoilValue(int slaveId, int addr, int value) { modbus_set_slave(_ctx, slaveId); int rc = modbus_write_bit(_ctx, addr, value); if (rc == -1) { LOG_ERROR(fmt::format("writeCoilValue failed, {}", modbus_strerror(errno))); return false; } else { return true; } } std::vector<int> ModbusBaseClient::readDiscreteInputValues(int slaveId, int addr, int cnt) { modbus_set_slave(_ctx, slaveId); uint8_t dest[cnt]; memset(dest, 0, cnt * sizeof(uint8_t)); int rc = modbus_read_input_bits(_ctx, addr, cnt, dest); if (rc == -1) { LOG_ERROR(fmt::format("readDiscreteInputValues failed, {}", modbus_strerror(errno))); return {}; } std::vector<int> result; for (const auto &val : dest) { result.push_back(static_cast<int>(val)); } return result; } std::vector<int> ModbusBaseClient::readHoldRegisterValues(int slaveId, int addr, int cnt) { modbus_set_slave(_ctx, slaveId); uint16_t dest[cnt]; memset(dest, 0, cnt * sizeof(uint16_t)); int rc = modbus_read_registers(_ctx, addr, cnt, dest); if (rc == -1) { LOG_ERROR(fmt::format("readHoldRegisterValues failed, {}", modbus_strerror(errno))); return {}; } std::vector<int> result; for (const auto &val : dest) { result.push_back(static_cast<int>(val)); } return result; } std::vector<int> ModbusBaseClient::readInputRegisterValues(int slaveId, int addr, int cnt) { modbus_set_slave(_ctx, slaveId); uint16_t dest[cnt]; memset(dest, 0, cnt * sizeof(uint16_t)); int rc = modbus_read_input_registers(_ctx, addr, cnt, dest); if (rc == -1) { LOG_ERROR(fmt::format("readInputRegisterValues failed, {}", modbus_strerror(errno))); return {}; } std::vector<int> result; for (const auto &val : dest) { result.push_back(static_cast<int>(val)); } return result; } bool ModbusBaseClient::writeHoldRegisterValue(int slaveId, int addr, int value) { modbus_set_slave(_ctx, slaveId); int rc = modbus_write_register(_ctx, addr, value); if (rc == -1) { LOG_ERROR(fmt::format("writeHoldRegisterValue failed, {}", modbus_strerror(errno))); return false; } else { return true; } } std::vector<int> ModbusBaseClient::readRangeCoilValues(int slaveId, int startAddr, int endAddr) { modbus_set_slave(_ctx, slaveId); int cnt = endAddr - startAddr + 1; int num = cnt % 100 == 0 ? cnt / 100 : cnt / 100 + 1; std::vector<int> result; for (int i = 0; i < num; i++) { int addr = startAddr + i * 100; int cnt = 100; if (i == num - 1) { cnt = endAddr - addr + 1; } uint8_t dest[cnt]; memset(dest, 0, cnt * sizeof(uint8_t)); int rc = modbus_read_bits(_ctx, addr, cnt, dest); if (rc == -1) { LOG_ERROR(fmt::format("readCoilValues failed, {}", modbus_strerror(errno))); return {}; } for (const auto &val : dest) { result.push_back(static_cast<int>(val)); } } return result; } std::vector<int> ModbusBaseClient::readRangeDiscreteInputValues(int slaveId, int startAddr, int endAddr) { modbus_set_slave(_ctx, slaveId); int cnt = endAddr - startAddr + 1; int num = cnt % 100 == 0 ? cnt / 100 : cnt / 100 + 1; std::vector<int> result; for (int i = 0; i < num; i++) { int addr = startAddr + i * 100; int cnt = 100; if (i == num - 1) { cnt = endAddr - addr + 1; } uint8_t dest[cnt]; memset(dest, 0, cnt * sizeof(uint8_t)); int rc = modbus_read_input_bits(_ctx, addr, cnt, dest); if (rc == -1) { LOG_ERROR(fmt::format("readDiscreteInputValues failed, {}", modbus_strerror(errno))); return {}; } for (const auto &val : dest) { result.push_back(static_cast<int>(val)); } } return result; } std::vector<int> ModbusBaseClient::readRangeHoldRegisterValues(int slaveId, int startAddr, int endAddr) { modbus_set_slave(_ctx, slaveId); int cnt = endAddr - startAddr + 1; int num = cnt % 100 == 0 ? cnt / 100 : cnt / 100 + 1; std::vector<int> result; for (int i = 0; i < num; i++) { int addr = startAddr + i * 100; int cnt = 100; if (i == num - 1) { cnt = endAddr - addr + 1; } uint16_t dest[cnt]; memset(dest, 0, cnt * sizeof(uint16_t)); int rc = modbus_read_registers(_ctx, addr, cnt, dest); if (rc == -1) { LOG_ERROR(fmt::format("readDiscreteInputValues failed, {}", modbus_strerror(errno))); return {}; } for (const auto &val : dest) { result.push_back(static_cast<int>(val)); } } return result; } std::vector<int> ModbusBaseClient::readRangeInputRegisterValues(int slaveId, int startAddr, int endAddr) { modbus_set_slave(_ctx, slaveId); int cnt = endAddr - startAddr + 1; int num = cnt % 100 == 0 ? cnt / 100 : cnt / 100 + 1; std::vector<int> result; for (int i = 0; i < num; i++) { int addr = startAddr + i * 100; int cnt = 100; if (i == num - 1) { cnt = endAddr - addr + 1; } uint16_t dest[cnt]; memset(dest, 0, cnt * sizeof(uint16_t)); int rc = modbus_read_input_registers(_ctx, addr, cnt, dest); if (rc == -1) { LOG_ERROR(fmt::format("readDiscreteInputValues failed, {}", modbus_strerror(errno))); return {}; } for (const auto &val : dest) { result.push_back(static_cast<int>(val)); } } return result; }
-
ModbusTcp客户端
#pragma once #include <iostream> #include <vector> #include "modbus_base_client.h" #include "libmodbus/src/modbus-tcp.h" class ModbusTcpClient : public ModbusBaseClient { public: ModbusTcpClient(const std::string ip, int port); ~ModbusTcpClient() = default; public: std::string ip; int port; };
实现
#include "modbus_tcp_client.h" #include "log.h" ModbusTcpClient::ModbusTcpClient(const std::string ip, int port) : ip(ip), port(port) { _ctx = modbus_new_tcp(ip.c_str(), port); if (_ctx == nullptr) { _isConnected = false; LOG_ERROR("Unable to create the libmodbus context"); return; } if (modbus_connect(_ctx) == -1) { LOG_ERROR(fmt::format("Connection failed, {}", modbus_strerror(errno))); modbus_free(_ctx); _ctx = nullptr; _isConnected = false; return; } _isConnected = true; LOG_INFO("Connected to Modbus Tcp server"); }
-
Modbus串口
#pragma once #include <iostream> #include <vector> #include "modbus_base_client.h" #include "libmodbus/src/modbus-rtu.h" class ModbusRtuClient : public ModbusBaseClient { public: ModbusRtuClient(const std::string &port, int baudRate, int byteSize, char parity, int stopBits); ModbusRtuClient() : ModbusRtuClient("/ttyUSB0", baudRate, byteSize, parity, stopBits){}; // 委托构造 ~ModbusRtuClient() = default; public: std::string port{"/ttyUSB0"}; // 端口 int baudRate{9600}; // 波特率 int byteSize{8}; // 数据位 char parity{'N'}; // 奇偶校验 int stopBits{1}; // 停止位 };
实现
#include "modbus_rtu_client.h" #include "log.h" ModbusRtuClient::ModbusRtuClient(const std::string &port, int baudRate, int byteSize, char parity, int stopBits) : port(port), baudRate(baudRate), byteSize(byteSize), parity(parity), stopBits(stopBits) { _ctx = modbus_new_rtu(port.c_str(), baudRate, parity, byteSize, stopBits); // 使用9600-N-8,1个停止位 if (_ctx == nullptr) { _isConnected = false; LOG_ERROR("Unable to create the libmodbus context"); return; } if (modbus_connect(_ctx) == -1) { _isConnected = false; LOG_ERROR(fmt::format("Connection failed: {0}", modbus_strerror(errno))); modbus_free(_ctx); _ctx = nullptr; return; } _isConnected = true; LOG_INFO("Connected to Modbus Serial server"); }