Files
blog/usr/uploads/2026/02/3930863264.c
2026-03-21 17:04:18 +08:00

673 lines
18 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
#include "dx_wf_24.h"
#include "cmsis_os.h"
#include "cmsis_os2.h"
#include "elog.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* ================= 配置 ================= */
#define WIFI_TX_BUF_SIZE 512
#define TAG "WIFI"
/* ================= 全局变量 ================= */
static uint8_t wifi_tx_buffer[WIFI_TX_BUF_SIZE];
static volatile uint8_t wifi_tx_busy = 0;
WIFI_HandleTypeDef wifi = {0};
SNTP_Time_t sntp_time = {0};
/* MQTT 状态 */
static volatile uint8_t mqtt_connected = 0;
static MQTT_State_t mqtt_state = MQTT_STATE_IDLE;
static void Generate_Random_ClientID(char *out_id, uint16_t max_len) {
// 获取系统运行时间
uint32_t tick = HAL_GetTick();
// 获取设备唯一标识使用STM32的UID
uint32_t *uid = (uint32_t *)UID_BASE;
uint32_t uid_hash = uid[0] ^ uid[1] ^ uid[2]; // 混合UID各部分
// 组合多个随机源
uint32_t random_seed = tick ^ uid_hash ^ (tick >> 16);
// 计算时间戳各位数字之和
uint32_t temp = tick;
uint32_t sum = 0;
while (temp > 0) {
sum += temp % 10;
temp /= 10;
}
// 生成最终ID包含时间戳、随机种子和校验和
snprintf(out_id, max_len, "SmartPetFeeder_%lu_%lu_%lu", tick, random_seed,
sum);
}
/* ================= DMA RX 初始化 ================= */
HAL_StatusTypeDef WIFI_RECV_DMA_Init(void) {
memset(&wifi, 0, sizeof(wifi));
HAL_UART_DMAStop(&huart1);
HAL_StatusTypeDef status =
HAL_UART_Receive_DMA(&huart1, wifi.rx_buffer, WIFI_RX_BUF_SIZE);
if (status == HAL_OK) {
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
elog_i(TAG, "WiFi串口DMA接收初始化成功");
} else {
elog_e(TAG, "WiFi串口DMA初始化失败");
}
return status;
}
/* ================= UART IDLE 回调 ================= */
void WIFI_UART_IDLE_Callback(UART_HandleTypeDef *huart) {
if (huart->Instance != USART1)
return;
__HAL_UART_CLEAR_IDLEFLAG(huart);
HAL_UART_DMAStop(huart);
uint16_t recv_len = WIFI_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx);
if (recv_len > 0 && recv_len < WIFI_RX_BUF_SIZE) {
wifi.rx_len = recv_len;
wifi.rx_buffer[recv_len] = '\0';
wifi.rx_flag = 1;
}
HAL_UART_Receive_DMA(&huart1, wifi.rx_buffer, WIFI_RX_BUF_SIZE);
}
/* ================= DMA 发送 ================= */
HAL_StatusTypeDef WIFI_SEND_DMA(const char *data) {
if (!data)
return HAL_ERROR;
size_t len = strlen(data);
if (len == 0 || len >= WIFI_TX_BUF_SIZE)
return HAL_ERROR;
if (wifi_tx_busy)
return HAL_BUSY;
memcpy(wifi_tx_buffer, data, len);
wifi_tx_busy = 1;
HAL_StatusTypeDef ret = HAL_UART_Transmit_DMA(&huart1, wifi_tx_buffer, len);
if (ret != HAL_OK)
wifi_tx_busy = 0;
return ret;
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
wifi_tx_busy = 0;
}
}
/* ================= 等待事件 ================= */
uint8_t WIFI_WaitEvent(const char *ok_str, const char *fail_str,
uint32_t timeout_ms) {
uint32_t start = HAL_GetTick();
while (HAL_GetTick() - start < timeout_ms) {
if (wifi.rx_flag) {
wifi.rx_flag = 0;
char *buf = (char *)wifi.rx_buffer;
elog_i(TAG, "接收到WiFi模块数据: %s", buf);
/* ===== 优先处理断线事件 ===== */
if (strstr(buf, "+MQTTDISCONNECTED")) {
mqtt_connected = 0;
elog_e(TAG, "MQTT已断开");
}
/* ===== 处理连接成功 ===== */
if (strstr(buf, "+MQTTCONNECTED")) {
mqtt_connected = 1;
elog_i(TAG, "MQTT服务器连接成功");
}
/* ===== 处理 ERROR=xxx ===== */
if (strstr(buf, "ERROR=")) {
elog_e(TAG, "模块返回错误: %s", buf);
return 0;
}
/* ===== 处理普通ERROR ===== */
if (fail_str && strstr(buf, fail_str)) {
return 0;
}
/* ===== 处理OK ===== */
if (ok_str && strstr(buf, ok_str)) {
return 1;
}
}
osDelay(10);
}
elog_e(TAG, "等待事件超时");
return 0;
}
/* ================= ACK ================= */
uint8_t WIFI_CheckAck(const char *cmd, const char *expect,
uint32_t timeout_ms) {
wifi.rx_flag = 0;
if (cmd && strlen(cmd)) {
if (WIFI_SEND_DMA(cmd) != HAL_OK)
return 0;
}
return WIFI_WaitEvent(expect, "ERROR", timeout_ms);
}
/* ================= WiFi连接 ================= */
uint8_t WIFI_Connect_WiFi(const char *ssid, const char *password,
uint32_t timeout_ms) {
char cmd[128];
snprintf(cmd, sizeof(cmd), "AT+CWJAP=%s,%s\r\n", ssid, password);
elog_i(TAG, "正在连接WiFi: %s", ssid);
elog_i(TAG, "发送命令: %s", cmd);
if (WIFI_SEND_DMA(cmd) != HAL_OK) {
elog_e(TAG, "WiFi命令发送失败");
return 0;
}
elog_i(TAG, "等待WiFi基础连接响应...");
if (!WIFI_WaitEvent("OK", "ERROR", 3000)) {
elog_e(TAG, "WiFi基础连接失败");
return 0;
}
elog_i(TAG, "等待WiFi详细连接结果...");
if (!WIFI_WaitEvent("+CWJAP:1", "+CWJAP:0", timeout_ms)) {
elog_e(TAG, "WiFi详细连接失败");
return 0;
}
elog_i(TAG, "WiFi连接成功");
return 1;
}
/* ================= MQTT连接 ================= */
uint8_t WIFI_Connect_MQTT(const char *wifi_ssid, const char *wifi_pass,
const char *mqtt_host, uint16_t mqtt_port,
const char *client_id, const char *user,
const char *pass) {
char cmd[256];
elog_i(TAG, "开始MQTT连接流程");
elog_i(TAG, "断开 MQTT 连接(如果已连接)");
if (!WIFI_CheckAck("AT+MQTTCLEAN\r\n", "OK", 2000)) {
elog_e(TAG, "MQTTCLEAN 失败");
return 0;
}
/* 等待真正断开事件 */
WIFI_WaitEvent("+MQTTDISCONNECTED", NULL, 2000);
/* 稍微延时,避免状态机还没稳定 */
osDelay(200);
// 设置SNTP服务器设置成功会自动重启模块
elog_i(TAG,"设置SNTP服务器");
if (!WIFI_Enable_SNTP()) {
elog_e(TAG, "SNTP设置失败");
return 0;
}
elog_i(TAG, "1. 检查模块响应...");
if (!WIFI_CheckAck("AT\r\n", "OK", 1000)) {
elog_e(TAG, "模块无响应");
return 0;
}
elog_i(TAG, "2. 设置WiFi模式...");
if (!WIFI_CheckAck("AT+CWMODE=0\r\n", "OK", 2000)) {
elog_e(TAG, "WiFi模式设置失败");
return 0;
}
elog_i(TAG, "3. 连接WiFi网络...");
if (!WIFI_Connect_WiFi(wifi_ssid, wifi_pass, 15000)) {
elog_e(TAG, "WiFi连接失败");
return 0;
}
mqtt_state = MQTT_STATE_WIFI_CONNECTED;
elog_i(TAG, "WiFi连接完成状态: %d", mqtt_state);
elog_i(TAG, "4. 设置MQTT客户端ID: %s", client_id);
snprintf(cmd, sizeof(cmd), "AT+MQTTLONGCLIENTID=%s\r\n", client_id);
if (!WIFI_CheckAck(cmd, "OK", 2000)) {
elog_e(TAG, "MQTT客户端ID设置失败");
return 0;
}
elog_i(TAG, "5. 设置MQTT用户名: %s", user);
snprintf(cmd, sizeof(cmd), "AT+MQTTLONGUSERNAME=%s\r\n", user);
if (!WIFI_CheckAck(cmd, "OK", 2000)) {
elog_e(TAG, "MQTT用户名设置失败");
return 0;
}
elog_i(TAG, "6. 设置MQTT密码");
snprintf(cmd, sizeof(cmd), "AT+MQTTLONGPASSWORD=%s\r\n", pass);
if (!WIFI_CheckAck(cmd, "OK", 2000)) {
elog_e(TAG, "MQTT密码设置失败");
return 0;
}
elog_i(TAG, "7. 连接MQTT服务器并新建会话 %s:%d", mqtt_host, mqtt_port);
snprintf(cmd, sizeof(cmd), "AT+MQTTCONN=%s,%d,0\r\n", mqtt_host, mqtt_port);
if (WIFI_SEND_DMA(cmd) != HAL_OK) {
elog_e(TAG, "MQTT连接命令发送失败");
return 0;
}
elog_i(TAG, "等待MQTT服务器连接响应...");
if (!WIFI_WaitEvent("+MQTTCONNECTED", "FAIL", 10000)) {
elog_e(TAG, "MQTT服务器连接失败");
elog_i(TAG, "接收到的模块响应: %s", wifi.rx_buffer);
return 0;
}
mqtt_connected = 1;
mqtt_state = MQTT_STATE_SERVER_CONNECTED;
elog_i(TAG, "MQTT服务器连接成功状态: %d", mqtt_state);
/* ===== 双订阅:使用自定义等待逻辑以兼容 ALREADY SUBSCRIBE ===== */
// 订阅 control 主题
elog_i(TAG, "8. 订阅control主题");
snprintf(cmd, sizeof(cmd), "AT+MQTTSUB=petfeeder/control,0\r\n");
wifi.rx_flag = 0;
if (WIFI_SEND_DMA(cmd) != HAL_OK) {
elog_e(TAG, "control主题订阅命令发送失败");
return 0;
}
{
uint32_t start = HAL_GetTick();
uint8_t subscribed = 0;
while (HAL_GetTick() - start < 5000) {
if (wifi.rx_flag) {
wifi.rx_flag = 0;
elog_i(TAG, "接收到订阅响应: %s", wifi.rx_buffer);
if (strstr((char *)wifi.rx_buffer, "+MQTTSUB") ||
strstr((char *)wifi.rx_buffer, "ALREADY SUBSCRIBE") ||
strstr((char *)wifi.rx_buffer, "OK")) {
subscribed = 1;
break;
}
if (strstr((char *)wifi.rx_buffer, "ERROR") ||
strstr((char *)wifi.rx_buffer, "FAIL")) {
break;
}
}
osDelay(10);
}
if (!subscribed) {
elog_e(TAG, "control主题订阅失败");
return 0;
}
}
// 订阅 config 主题
elog_i(TAG, "9. 订阅config主题");
snprintf(cmd, sizeof(cmd), "AT+MQTTSUB=petfeeder/config,0\r\n");
wifi.rx_flag = 0;
if (WIFI_SEND_DMA(cmd) != HAL_OK) {
elog_e(TAG, "config主题订阅命令发送失败");
return 0;
}
{
uint32_t start = HAL_GetTick();
uint8_t subscribed = 0;
while (HAL_GetTick() - start < 5000) {
if (wifi.rx_flag) {
wifi.rx_flag = 0;
elog_i(TAG, "接收到订阅响应: %s", wifi.rx_buffer);
if (strstr((char *)wifi.rx_buffer, "+MQTTSUB") ||
strstr((char *)wifi.rx_buffer, "ALREADY SUBSCRIBE") ||
strstr((char *)wifi.rx_buffer, "OK")) {
subscribed = 1;
break;
}
if (strstr((char *)wifi.rx_buffer, "ERROR") ||
strstr((char *)wifi.rx_buffer, "FAIL")) {
break;
}
}
osDelay(10);
}
if (!subscribed) {
elog_e(TAG, "config主题订阅失败");
return 0;
}
}
mqtt_state = MQTT_STATE_SUBSCRIBED;
elog_i(TAG, "MQTT双主题订阅完成最终状态: %d", mqtt_state);
return 1;
}
/* ================= MQTT发布函数 ================= */
uint8_t WIFI_MQTT_Publish_RAW(const char *topic, const uint8_t *payload,
uint16_t length, uint8_t qos, uint8_t retain) {
if (!mqtt_connected) {
elog_e(TAG, "MQTT未连接无法发布消息");
return 0;
}
if (!topic || !payload || length == 0) {
elog_e(TAG, "发布参数无效: topic=%p, payload=%p, length=%d", topic, payload,
length);
return 0;
}
if (length > 1000) {
elog_e(TAG, "发布数据过长: %d bytes", length);
return 0;
}
char cmd[256];
/* 1⃣ 发送发布命令 */
snprintf(cmd, sizeof(cmd), "AT+MQTTPUBRAW=%s,%d,%d,%d\r\n", topic, length,
qos, retain);
elog_i(TAG, "准备发布到主题: %s, 长度: %d, QoS: %d", topic, length, qos);
if (WIFI_SEND_DMA(cmd) != HAL_OK) {
elog_e(TAG, "发布命令发送失败");
return 0;
}
/* 2⃣ 等待 > 提示符 */
elog_i(TAG, "等待发布提示符>");
if (!WIFI_WaitEvent(">", "ERROR", 3000)) {
elog_e(TAG, "未收到发布提示符>");
return 0;
}
/* 3⃣ 发送实际数据(不能带\r\n */
elog_i(TAG, "发送MQTT负载数据 (%d bytes)", length);
if (HAL_UART_Transmit(&huart1, (uint8_t *)payload, length, 3000) != HAL_OK) {
elog_e(TAG, "MQTT负载数据发送失败");
return 0;
}
/* 4⃣ 等待发布成功 */
elog_i(TAG, "等待发布确认");
if (!WIFI_WaitEvent("+MQTTPUB:OK", "FAIL", 5000)) {
elog_e(TAG, "MQTT发布失败");
return 0;
}
elog_i(TAG, "MQTT发布成功到主题: %s", topic);
return 1;
}
/* ================= MQTT接收任务 ================= */
void wifi_task_mqtt(void *argument) {
MQTT_Message_t msg;
elog_i(TAG, "启动WiFi MQTT任务");
WIFI_RECV_DMA_Init();
osDelay(3000);
char client_id[64] = {0};
// 生成唯一ClientID
Generate_Random_ClientID(client_id, sizeof(client_id));
elog_i(TAG, "生成ClientID: %s", client_id);
elog_i(TAG, "开始MQTT连接...");
if (!WIFI_Connect_MQTT("WIFI_TEST", "88888888", "www.beihong.wang", 1883,
client_id, "STM32_MQTT", "123456")) {
elog_e(TAG, "MQTT连接失败任务退出");
vTaskDelete(NULL); // 删除自身任务
}
elog_i(TAG, "发布设备上线状态");
WIFI_MQTT_Publish_Status("{\"status\":\"online\"}");
// 连接MQTT服务器成功后获取SNTP时间
WIFI_Get_SNTP_Time();
// 获取SNTP时间
elog_i(TAG, "获取SNTP时间...");
WIFI_Get_Current_Time();
elog_i(TAG, "当前SNTP时间为: %04d-%02d-%02d %02d:%02d:%02d",
sntp_time.year, sntp_time.month, sntp_time.day,
sntp_time.hour, sntp_time.minute, sntp_time.second);
elog_i(TAG, "进入MQTT消息处理循环");
for (;;) {
if (wifi.rx_flag) {
wifi.rx_flag = 0;
elog_i(TAG, "收到WiFi数据: %s", wifi.rx_buffer);
if (WIFI_Parse_MQTT_Message((char *)wifi.rx_buffer, &msg)) {
elog_i(TAG, "解析MQTT消息成功");
elog_i(TAG, "收到主题: %s", msg.topic);
elog_i(TAG, "内容: %s", msg.payload);
/* ===== control主题 ===== */
if (strcmp(msg.topic, "petfeeder/control") == 0) {
elog_i(TAG, "处理control主题消息");
if (strstr(msg.payload, "feed")) {
elog_i(TAG, "执行喂食动作");
// 执行喂食函数
}
}
/* ===== config主题 ===== */
else if (strcmp(msg.topic, "petfeeder/config") == 0) {
elog_i(TAG, "处理config主题消息");
elog_i(TAG, "更新配置参数");
// 更新参数逻辑
}
} else {
elog_w(TAG, "MQTT消息解析失败");
}
}
osDelay(20);
}
}
uint8_t WIFI_Parse_MQTT_Message(char *input, MQTT_Message_t *msg) {
if (!input || !msg)
return 0;
char *start = strstr(input, "+MQTTSUBRECV:");
if (!start)
return 0;
start += strlen("+MQTTSUBRECV:");
/* ========= 1. 解析 topic ========= */
char *comma1 = strchr(start, ',');
if (!comma1)
return 0;
uint16_t topic_len = comma1 - start;
if (topic_len >= sizeof(msg->topic))
return 0;
memcpy(msg->topic, start, topic_len);
msg->topic[topic_len] = '\0';
/* ========= 2. 解析 length ========= */
char *comma2 = strchr(comma1 + 1, ',');
if (!comma2)
return 0;
char len_str[10] = {0};
uint16_t len_field_len = comma2 - (comma1 + 1);
if (len_field_len >= sizeof(len_str))
return 0;
memcpy(len_str, comma1 + 1, len_field_len);
len_str[len_field_len] = '\0';
uint16_t payload_len = atoi(len_str);
if (payload_len == 0 || payload_len >= sizeof(msg->payload))
return 0;
/* ========= 3. 解析 payload ========= */
char *payload_start = comma2 + 1;
if (strlen(payload_start) < payload_len)
return 0; // 数据还没收完整
memcpy(msg->payload, payload_start, payload_len);
msg->payload[payload_len] = '\0';
msg->payload_len = payload_len;
return 1;
}
uint8_t WIFI_MQTT_Publish_Sensor(const char *json) {
return WIFI_MQTT_Publish_RAW("petfeeder/sensor", (uint8_t *)json,
strlen(json), 0, 0);
}
uint8_t WIFI_MQTT_Publish_Status(const char *json) {
return WIFI_MQTT_Publish_RAW("petfeeder/status", (uint8_t *)json,
strlen(json), 0, 1);
}
/* ================= SNTP 时间相关函数 ================= */
uint8_t WIFI_Enable_SNTP(void) {
elog_i(TAG, "使能SNTP服务器设置中国时区");
// 发送AT+CIPSNTPCFG=1,8,cn.ntp.org.cn命令
if (!WIFI_CheckAck("AT+CIPSNTPCFG=1,8,cn.ntp.org.cn\r\n", "OK", 3000)) {
elog_e(TAG, "SNTP配置失败");
return 0;
}
osDelay(10000); // 等待模块重启并连接到SNTP服务器
elog_i(TAG, "SNTP服务器配置成功模块将自动重启以应用设置");
return 1;
}
uint8_t WIFI_Get_SNTP_Time(void) {
char *time_str;
char time_buf[32];
elog_i(TAG, "查询SNTP时间");
// 发送AT+CIPSNTPTIME命令
if (WIFI_SEND_DMA("AT+CIPSNTPTIME\r\n") != HAL_OK) {
elog_e(TAG, "SNTP时间查询命令发送失败");
return 0;
}
// 等待响应
if (!WIFI_WaitEvent("+CIPSNTPTIME:", "ERROR", 5000)) {
elog_e(TAG, "SNTP时间查询失败");
return 0;
}
// 解析时间字符串 "+CIPSNTPTIME:2024-05-08 21:11:38"
time_str = strstr((char *)wifi.rx_buffer, "+CIPSNTPTIME:");
if (!time_str) {
elog_e(TAG, "未找到时间字符串");
return 0;
}
// 提取时间部分 "2024-05-08 21:11:38"
time_str += strlen("+CIPSNTPTIME:");
// 复制到缓冲区以便处理
strncpy(time_buf, time_str, sizeof(time_buf) - 1);
time_buf[sizeof(time_buf) - 1] = '\0';
// 移除末尾的换行符和回车符
char *newline = strchr(time_buf, '\r');
if (newline) *newline = '\0';
newline = strchr(time_buf, '\n');
if (newline) *newline = '\0';
elog_i(TAG, "解析时间字符串: %s", time_buf);
// 解析时间格式 YYYY-MM-DD HH:MM:SS
int year, month, day, hour, minute, second;
if (sscanf(time_buf, "%d-%d-%d %d:%d:%d",
&year, &month, &day, &hour, &minute, &second) == 6) {
sntp_time.year = (uint16_t)year;
sntp_time.month = (uint8_t)month;
sntp_time.day = (uint8_t)day;
sntp_time.hour = (uint8_t)hour;
sntp_time.minute = (uint8_t)minute;
sntp_time.second = (uint8_t)second;
sntp_time.valid = 1;
elog_i(TAG, "SNTP时间获取成功: %04d-%02d-%02d %02d:%02d:%02d",
sntp_time.year, sntp_time.month, sntp_time.day,
sntp_time.hour, sntp_time.minute, sntp_time.second);
return 1;
} else {
elog_e(TAG, "时间字符串解析失败: %s", time_buf);
sntp_time.valid = 0;
return 0;
}
}
// 获取当前SNTP时间返回结构体副本
SNTP_Time_t WIFI_Get_Current_Time(void) {
return sntp_time;
}
// 检查SNTP时间是否有效
uint8_t WIFI_Is_Time_Valid(void) {
return sntp_time.valid;
}