Files
433_STM32/Core/Src/modbus_tcp_client.c
edisondeng ee67076ec7 暂存
2026-05-09 19:53:47 +08:00

176 lines
7.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
******************************************************************************
* @file modbus_tcp_client.c
* @brief Modbus TCP 客户端模块实现 (针对寄存器 40031 轮询)
******************************************************************************
*/
#include "modbus_tcp_client.h"
#include "socket.h"
#include "wizchip_conf.h"
#include "main.h"
#include "debug_log.h"
#include <string.h>
/* 私有变量 */
static uint8_t g_sn = 0;
static modbus_client_state_t g_state = MODBUS_STATE_IDLE;
static uint32_t g_last_tick = 0;
static uint16_t g_transaction_id = 0;
/* 服务器信息 */
static uint8_t dest_ip[4] = MODBUS_SERVER_IP;
static uint16_t dest_port = MODBUS_SERVER_PORT;
/**
* @brief 构造 Modbus TCP 请求包 (FC 03)
*/
static uint16_t build_read_request(uint8_t *buf, uint16_t addr, uint16_t qty)
{
g_transaction_id++;
/* MBAP Header */
buf[0] = (uint8_t)(g_transaction_id >> 8);
buf[1] = (uint8_t)(g_transaction_id & 0xFF);
buf[2] = 0x00; /* Protocol ID (0 for Modbus) */
buf[3] = 0x00;
buf[4] = 0x00; /* Length (6 bytes follow UnitID) */
buf[5] = 0x06;
buf[6] = MODBUS_UNIT_ID;
/* PDU */
buf[7] = 0x03; /* Function Code: Read Holding Registers */
buf[8] = (uint8_t)(addr >> 8);
buf[9] = (uint8_t)(addr & 0xFF);
buf[10] = (uint8_t)(qty >> 8);
buf[11] = (uint8_t)(qty & 0xFF);
return 12;
}
void ModbusTCP_Client_Init(uint8_t sn)
{
g_sn = sn;
g_state = MODBUS_STATE_IDLE;
g_last_tick = HAL_GetTick();
/* 动态重新加载配置,防止静态初始化带来的宏定义未生效问题 */
uint8_t ip[4] = MODBUS_SERVER_IP;
dest_ip[0] = ip[0]; dest_ip[1] = ip[1]; dest_ip[2] = ip[2]; dest_ip[3] = ip[3];
dest_port = MODBUS_SERVER_PORT;
LOG_INFO("MODBUS", "Client initialized on Socket %d, Target: %d.%d.%d.%d:%d",
sn, dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3], dest_port);
}
void ModbusTCP_Client_Task(void)
{
uint8_t tmp_buf[128]; // 临时接收缓存
uint16_t len; // 接收长度
int32_t ret; // 返回值
switch (g_state) // 客户端状态机切换
{
case MODBUS_STATE_IDLE: // 空闲状态
if (HAL_GetTick() - g_last_tick >= MODBUS_POLL_INTERVAL) { // 检查轮询间隔 (2秒)
g_state = MODBUS_STATE_CONNECTING; // 切换到连接状态
}
break;
case MODBUS_STATE_CONNECTING: // 连接中状态
{
uint8_t sr = getSn_SR(g_sn);
switch(sr) // 获取当前 Socket 状态
{
case SOCK_CLOSED: // 如果 Socket 已关闭
// 打开 TCP 模式的 Socket分配随机本地端口以避免冲突
if (socket(g_sn, Sn_MR_TCP, 50000 + (HAL_GetTick() % 10000), 0x00) == g_sn) {
LOG_DEBUG("MODBUS", "Socket opened");
}
break;
case SOCK_INIT: // Socket 初始化成功,开始连接
LOG_INFO("MODBUS", "Connecting to %d.%d.%d.%d:%d",
dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3], dest_port);
ret = connect(g_sn, dest_ip, dest_port); // 发起连接请求
if (ret != SOCK_OK) {
LOG_ERROR("MODBUS", "Connect failed: %d", ret);
close(g_sn);
g_state = MODBUS_STATE_IDLE;
g_last_tick = HAL_GetTick();
}
break;
case SOCK_ESTABLISHED: // 连接已建立
LOG_INFO("MODBUS", "TCP Connected!");
g_state = MODBUS_STATE_SEND_QUERY; // 切换到发送请求状态
break;
case SOCK_CLOSE_WAIT: // 对方已关闭连接
disconnect(g_sn); // 本地断开连接
break;
default:
if (HAL_GetTick() % 5000 == 0) { // 每 5 秒打印一次未处理的状态
LOG_WARN("MODBUS", "Unhandled socket state: 0x%02X", sr);
}
break;
}
break;
}
case MODBUS_STATE_SEND_QUERY: // 发送查询请求状态
len = build_read_request(tmp_buf, TARGET_REG_ADDR, 1); // 构造 Modbus TCP FC03 报文
ret = send(g_sn, tmp_buf, len); // 发送 TCP 数据
if (ret > 0) { // 发送成功
LOG_DEBUG("MODBUS", "Request sent (TID: %d)", g_transaction_id);
g_state = MODBUS_STATE_WAIT_RESPONSE; // 切换到等待响应状态
g_last_tick = HAL_GetTick(); // 更新计时器
} else { // 发送失败
LOG_ERROR("MODBUS", "Send failed, reconnecting...");
close(g_sn); // 关闭 Socket
g_state = MODBUS_STATE_IDLE; // 返回空闲重试
}
break;
case MODBUS_STATE_WAIT_RESPONSE: // 等待响应状态
if ((len = getSn_RX_RSR(g_sn)) > 0) { // 检查是否有数据到达
if (len > sizeof(tmp_buf)) len = sizeof(tmp_buf); // 防止缓存溢出
ret = recv(g_sn, tmp_buf, len); // 接收数据
if (ret > 0) { // 接收成功
g_state = MODBUS_STATE_PROCESS_DATA; // 切换到处理数据状态
}
} else if (HAL_GetTick() - g_last_tick > 3000) { // 等待超时 (3秒)
LOG_WARN("MODBUS", "Response timeout");
close(g_sn); // 关闭连接
g_state = MODBUS_STATE_IDLE; // 返回空闲重试
}
break;
case MODBUS_STATE_PROCESS_DATA: // 处理数据状态
/* 校验响应 (检查 MBAP 长度、功能码等) */
if (len >= 9 && tmp_buf[7] == 0x03) { // 基本校验
uint16_t reg_val = (tmp_buf[9] << 8) | tmp_buf[10]; // 提取寄存器值
LOG_INFO("MODBUS", "Read Register %d OK: 0x%04X", TARGET_REG_ADDR + 40001, reg_val);
/* 🚀 关键步骤:通过 433 无线转发结果 */
uint8_t payload[2]; // 构造 433 负载
payload[0] = tmp_buf[9]; // 寄存器高位
payload[1] = tmp_buf[10]; // 寄存器低位
RF433_SendPacket(PROTO_TYPE_NET, payload, 2); // 打包并通过无线发出
g_state = MODBUS_STATE_IDLE; // 处理完成,返回空闲
g_last_tick = HAL_GetTick(); // 记录本次完成时间
} else { // 响应格式非法
LOG_ERROR("MODBUS", "Invalid response format");
g_state = MODBUS_STATE_IDLE; // 返回空闲
g_last_tick = HAL_GetTick(); // 更新计时
}
break;
default: // 异常保护
g_state = MODBUS_STATE_IDLE;
break;
}
}