基于c++ modbus标准协议实现串口通讯,实现其读写数据,单个数据和多个数据读写的完整代码
时间: 2024-03-14 18:48:10 浏览: 15
以下是基于C++ Modbus标准协议实现串口通讯,实现其读写数据,单个数据和多个数据读写的完整代码示例:
```c++
#include <iostream>
#include <modbus/modbus.h>
#include <cstring>
#include <chrono>
#include <thread>
#include <vector>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
// 定义串口号和波特率
#define SERIAL_PORT "/dev/ttyUSB0"
#define BAUDRATE B9600
// 定义Modbus协议参数
#define SLAVE_ID 1
#define START_ADDR 0
#define NUM_REGS 1
// 定义读写操作码
#define READ_COILS 1
#define READ_INPUT_BITS 2
#define READ_HOLDING_REGS 3
#define READ_INPUT_REGS 4
#define WRITE_COIL 5
#define WRITE_HOLDING_REG 6
#define WRITE_COILS 15
#define WRITE_HOLDING_REGS 16
// 定义读写数据类型
enum DataType
{
COIL,
DISCRETE_INPUT,
HOLDING_REG,
INPUT_REG
};
// 打开串口
int open_serial_port(const char *port)
{
int fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
std::cerr << "Error: Unable to open serial port." << std::endl;
return -1;
}
fcntl(fd, F_SETFL, 0);
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, BAUDRATE);
cfsetospeed(&options, BAUDRATE);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag &= ~CRTSCTS;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_oflag &= ~OPOST;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
tcsetattr(fd, TCSANOW, &options);
return fd;
}
// 关闭串口
void close_serial_port(int fd)
{
close(fd);
}
// 读取单个数据
int read_data(modbus_t *ctx, DataType type, uint16_t addr)
{
int rc;
uint8_t coils_status[1];
uint8_t discrete_input_status[1];
uint16_t holding_regs[1];
uint16_t input_regs[1];
switch (type)
{
case COIL:
rc = modbus_read_bits(ctx, addr, 1, coils_status);
if (rc == -1)
{
std::cerr << "Error: Unable to read coils status." << std::endl;
return -1;
}
return coils_status[0];
case DISCRETE_INPUT:
rc = modbus_read_input_bits(ctx, addr, 1, discrete_input_status);
if (rc == -1)
{
std::cerr << "Error: Unable to read discrete input status." << std::endl;
return -1;
}
return discrete_input_status[0];
case HOLDING_REG:
rc = modbus_read_registers(ctx, addr, 1, holding_regs);
if (rc == -1)
{
std::cerr << "Error: Unable to read holding registers." << std::endl;
return -1;
}
return holding_regs[0];
case INPUT_REG:
rc = modbus_read_input_registers(ctx, addr, 1, input_regs);
if (rc == -1)
{
std::cerr << "Error: Unable to read input registers." << std::endl;
return -1;
}
return input_regs[0];
default:
std::cerr << "Error: Invalid data type." << std::endl;
return -1;
}
}
// 写入单个数据
int write_data(modbus_t *ctx, DataType type, uint16_t addr, uint16_t value)
{
int rc;
switch (type)
{
case COIL:
rc = modbus_write_bit(ctx, addr, value);
if (rc == -1)
{
std::cerr << "Error: Unable to write coil." << std::endl;
return -1;
}
return 0;
case HOLDING_REG:
rc = modbus_write_register(ctx, addr, value);
if (rc == -1)
{
std::cerr << "Error: Unable to write holding register." << std::endl;
return -1;
}
return 0;
default:
std::cerr << "Error: Invalid data type." << std::endl;
return -1;
}
}
// 读取多个数据
std::vector<int> read_data(modbus_t *ctx, DataType type, uint16_t start_addr, uint16_t num_regs)
{
int rc;
uint8_t coils_status[1];
uint8_t *discrete_input_status = new uint8_t[num_regs];
uint16_t *holding_regs = new uint16_t[num_regs];
uint16_t *input_regs = new uint16_t[num_regs];
std::vector<int> values;
switch (type)
{
case COIL:
rc = modbus_read_bits(ctx, start_addr, num_regs, coils_status);
if (rc == -1)
{
std::cerr << "Error: Unable to read coils status." << std::endl;
return values;
}
for (int i = 0; i < num_regs; i++)
{
values.push_back(coils_status[i]);
}
break;
case DISCRETE_INPUT:
rc = modbus_read_input_bits(ctx, start_addr, num_regs, discrete_input_status);
if (rc == -1)
{
std::cerr << "Error: Unable to read discrete input status." << std::endl;
return values;
}
for (int i = 0; i < num_regs; i++)
{
values.push_back(discrete_input_status[i]);
}
break;
case HOLDING_REG:
rc = modbus_read_registers(ctx, start_addr, num_regs, holding_regs);
if (rc == -1)
{
std::cerr << "Error: Unable to read holding registers." << std::endl;
return values;
}
for (int i = 0; i < num_regs; i++)
{
values.push_back(holding_regs[i]);
}
break;
case INPUT_REG:
rc = modbus_read_input_registers(ctx, start_addr, num_regs, input_regs);
if (rc == -1)
{
std::cerr << "Error: Unable to read input registers." << std::endl;
return values;
}
for (int i = 0; i < num_regs; i++)
{
values.push_back(input_regs[i]);
}
break;
}
delete[] discrete_input_status;
delete[] holding_regs;
delete[] input_regs;
return values;
}
// 写入多个数据
int write_data(modbus_t *ctx, DataType type, uint16_t start_addr, const std::vector<int> &values)
{
int rc;
uint8_t *coils_status = new uint8_t[values.size()];
uint16_t *holding_regs = new uint16_t[values.size()];
switch (type)
{
case COIL:
for (int i = 0; i < values.size(); i++)
{
coils_status[i] = values[i];
}
rc = modbus_write_bits(ctx, start_addr, values.size(), coils_status);
if (rc == -1)
{
std::cerr << "Error: Unable to write coils." << std::endl;
delete[] coils_status;
return -1;
}
delete[] coils_status;
return 0;
case HOLDING_REG:
for (int i = 0; i < values.size(); i++)
{
holding_regs[i] = values[i];
}
rc = modbus_write_registers(ctx, start_addr, values.size(), holding_regs);
if (rc == -1)
{
std::cerr << "Error: Unable to write holding registers." << std::endl;
delete[] holding_regs;
return -1;
}
delete[] holding_regs;
return 0;
default:
std::cerr << "Error: Invalid data type." << std::endl;
delete[] coils_status;
delete[] holding_regs;
return -1;
}
}
int main()
{
// 打开串口
int fd = open_serial_port(SERIAL_PORT);
if (fd == -1)
{
return -1;
}
// 创建Modbus context
modbus_t *ctx = modbus_new_rtu(SERIAL_PORT, BAUDRATE, 'N', 8, 1);
if (ctx == NULL)
{
std::cerr << "Error: Unable to create Modbus context." << std::endl;
close_serial_port(fd);
return -1;
}
// 设置从站ID
modbus_set_slave(ctx, SLAVE_ID);
// 打开Modbus连接
modbus_connect(ctx);
// 读取单个数据
int data = read_data(ctx, HOLDING_REG, START_ADDR);
if (data == -1)
{
modbus_close(ctx);
modbus_free(ctx);
close_serial_port(fd);
return -1;
}
std::cout << "Read single data: " << data << std::endl;
// 写入单个数据
uint16_t value = 1;
int ret = write_data(ctx, HOLDING_REG, START_ADDR, value);
if (ret == -1)
{
modbus_close(ctx);
modbus_free(ctx);
close_serial_port(fd);
return -1;
}
std::cout << "Write single data: " << value << std::endl;
// 读取多个数据
std::vector<int> values = read_data(ctx, HOLDING_REG, START_ADDR, NUM_REGS);
if (values.empty())
{
modbus_close(ctx);
modbus_free(ctx);
close_serial_port(fd);
return -1;
}
std::cout << "Read multiple data: ";
for (auto value : values)
{
std::cout << value << " ";
}
std::cout << std::endl;
// 写入多个数据
std::vector<int> new_values = {1, 2, 3};
ret = write_data(ctx, HOLDING_REG, START_ADDR, new_values);
if (ret == -1)
{
modbus_close(ctx);
modbus_free(ctx);
close_serial_port(fd);
return -1;
}
std::cout << "Write multiple data: ";
for (auto value : new_values)
{
std::cout << value << " ";
}
std::cout << std::endl;
// 关闭Modbus连接
modbus_close(ctx);
modbus_free(ctx);
// 关闭串口
close_serial_port(fd);
return 0;
}
```
这个示例代码通过打开串口和创建Modbus context连接到Modbus从站。然后,它执行了单个数据的读写和多个数据的读写操作。最后,它关闭了Modbus连接和串口。请注意,这个示例代码假设Modbus从站的ID是1,起始地址是0,要读写的寄存器数目是1。如果这些参数不同,需要相应地更改。