c++与modbus tcp通信的代码
时间: 2024-01-23 18:02:43 浏览: 905
以下是一个简单的C++与Modbus TCP通信的示例代码:
```cpp
#include <iostream>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#define SERVER_IP "192.168.1.100"
#define SERVER_PORT 502
// Modbus TCP帧结构
typedef struct
{
uint16_t trans_id; // 事务ID
uint16_t proto_id; // 协议ID,默认为0
uint16_t len; // 数据长度
uint8_t unit_id; // 单元ID
uint8_t func_code; // 功能码
uint16_t reg_addr; // 寄存器地址
uint16_t reg_count; // 寄存器数量
uint16_t data[256]; // 数据
} modbus_tcp_frame_t;
// Modbus TCP功能码
enum
{
MODBUS_FUNC_READ_HOLDING_REGISTERS = 0x03,
MODBUS_FUNC_WRITE_SINGLE_REGISTER = 0x06,
};
// 计算CRC16校验码
uint16_t ModbusCRC16(const uint8_t *buf, int len)
{
uint16_t crc = 0xFFFF;
for (int i = 0; i < len; ++i)
{
crc ^= buf[i];
for (int j = 0; j < 8; ++j)
{
if (crc & 0x0001)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
return crc;
}
// 发送Modbus TCP帧
bool SendModbusTCPFrame(int sockfd, modbus_tcp_frame_t *frame)
{
uint8_t buf[512] = {0};
int len = 0;
// 将Modbus TCP帧转换为字节流
memcpy(buf + len, &frame->trans_id, sizeof(frame->trans_id));
len += sizeof(frame->trans_id);
memcpy(buf + len, &frame->proto_id, sizeof(frame->proto_id));
len += sizeof(frame->proto_id);
memcpy(buf + len, &frame->len, sizeof(frame->len));
len += sizeof(frame->len);
memcpy(buf + len, &frame->unit_id, sizeof(frame->unit_id));
len += sizeof(frame->unit_id);
memcpy(buf + len, &frame->func_code, sizeof(frame->func_code));
len += sizeof(frame->func_code);
memcpy(buf + len, &frame->reg_addr, sizeof(frame->reg_addr));
len += sizeof(frame->reg_addr);
memcpy(buf + len, &frame->reg_count, sizeof(frame->reg_count));
len += sizeof(frame->reg_count);
if (frame->func_code == MODBUS_FUNC_WRITE_SINGLE_REGISTER)
{
memcpy(buf + len, &frame->data[0], sizeof(frame->data[0]));
len += sizeof(frame->data[0]);
}
// 计算CRC校验码
uint16_t crc = ModbusCRC16(buf, len);
memcpy(buf + len, &crc, sizeof(crc));
len += sizeof(crc);
// 发送Modbus TCP帧
int ret = send(sockfd, buf, len, 0);
if (ret < 0)
{
std::cerr << "send error: " << strerror(errno) << std::endl;
return false;
}
else if (ret != len)
{
std::cerr << "send error: incomplete data" << std::endl;
return false;
}
return true;
}
// 接收Modbus TCP帧
bool RecvModbusTCPFrame(int sockfd, modbus_tcp_frame_t *frame)
{
uint8_t buf[512] = {0};
int len = 0;
// 接收Modbus TCP帧
int ret = recv(sockfd, buf, sizeof(buf), 0);
if (ret < 0)
{
std::cerr << "recv error: " << strerror(errno) << std::endl;
return false;
}
else if (ret == 0)
{
std::cerr << "recv error: connection closed by peer" << std::endl;
return false;
}
// 将字节流转换为Modbus TCP帧
memcpy(&frame->trans_id, buf + len, sizeof(frame->trans_id));
len += sizeof(frame->trans_id);
memcpy(&frame->proto_id, buf + len, sizeof(frame->proto_id));
len += sizeof(frame->proto_id);
memcpy(&frame->len, buf + len, sizeof(frame->len));
len += sizeof(frame->len);
memcpy(&frame->unit_id, buf + len, sizeof(frame->unit_id));
len += sizeof(frame->unit_id);
memcpy(&frame->func_code, buf + len, sizeof(frame->func_code));
len += sizeof(frame->func_code);
memcpy(&frame->reg_count, buf + len, sizeof(frame->reg_count));
len += sizeof(frame->reg_count);
if (frame->func_code == MODBUS_FUNC_READ_HOLDING_REGISTERS)
{
for (int i = 0; i < frame->reg_count; ++i)
{
memcpy(&frame->data[i], buf + len, sizeof(frame->data[i]));
len += sizeof(frame->data[i]);
}
}
else if (frame->func_code == MODBUS_FUNC_WRITE_SINGLE_REGISTER)
{
memcpy(&frame->data[0], buf + len, sizeof(frame->data[0]));
len += sizeof(frame->data[0]);
}
// 验证CRC校验码
uint16_t crc = ModbusCRC16(buf, len - sizeof(crc));
if (crc != frame->data[frame->reg_count - 1])
{
std::cerr << "recv error: invalid CRC" << std::endl;
return false;
}
return true;
}
// 读取Modbus寄存器值
bool ReadModbusRegisters(int sockfd, uint16_t addr, uint16_t count, uint16_t *data)
{
modbus_tcp_frame_t frame = {0};
frame.trans_id = rand() & 0xFFFF; // 生成随机事务ID
frame.proto_id = 0;
frame.len = 6;
frame.unit_id = 1;
frame.func_code = MODBUS_FUNC_READ_HOLDING_REGISTERS;
frame.reg_addr = htons(addr);
frame.reg_count = htons(count);
if (!SendModbusTCPFrame(sockfd, &frame))
{
return false;
}
if (!RecvModbusTCPFrame(sockfd, &frame))
{
return false;
}
for (int i = 0; i < count; ++i)
{
data[i] = ntohs(frame.data[i]);
}
return true;
}
// 写入Modbus寄存器值
bool WriteModbusRegister(int sockfd, uint16_t addr, uint16_t value)
{
modbus_tcp_frame_t frame = {0};
frame.trans_id = rand() & 0xFFFF; // 生成随机事务ID
frame.proto_id = 0;
frame.len = 6;
frame.unit_id = 1;
frame.func_code = MODBUS_FUNC_WRITE_SINGLE_REGISTER;
frame.reg_addr = htons(addr);
frame.reg_count = htons(1);
frame.data[0] = htons(value);
if (!SendModbusTCPFrame(sockfd, &frame))
{
return false;
}
if (!RecvModbusTCPFrame(sockfd, &frame))
{
return false;
}
return true;
}
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
std::cerr << "socket error: " << strerror(errno) << std::endl;
return -1;
}
struct sockaddr_in server_addr = {0};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
int ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret < 0)
{
std::cerr << "connect error: " << strerror(errno) << std::endl;
close(sockfd);
return -1;
}
uint16_t data[256] = {0};
if (ReadModbusRegisters(sockfd, 0x0000, 0x0001, data))
{
std::cout << "Register 0x0000 value: " << data[0] << std::endl;
}
if (WriteModbusRegister(sockfd, 0x0001, 0x1234))
{
std::cout << "Write register 0x0001 success" << std::endl;
}
close(sockfd);
return 0;
}
```
该示例代码实现了Modbus TCP的读写功能,包括:
- 计算CRC16校验码;
- 发送Modbus TCP帧;
- 接收Modbus TCP帧;
- 读取Modbus寄存器值;
- 写入Modbus寄存器值。
其中,`ReadModbusRegisters`函数用于读取Modbus寄存器值,`WriteModbusRegister`函数用于写入Modbus寄存器值。在主函数中,首先创建了一个TCP套接字,并连接到Modbus TCP服务器。然后,通过`ReadModbusRegisters`函数读取了寄存器0x0000的值,并通过`WriteModbusRegister`函数向寄存器0x0001写入了一个值。最后,关闭TCP套接字。需要注意的是,在实际应用中,需要根据Modbus设备的寄存器映射表来确定要读取或写入的寄存器地址和数量。
阅读全文