first commit
BIN
usr/uploads/2026/01/119898774.pdf
Normal file
BIN
usr/uploads/2026/01/1224982803.png
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
usr/uploads/2026/01/1324290890.png
Normal file
|
After Width: | Height: | Size: 338 KiB |
BIN
usr/uploads/2026/01/1328242656.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
usr/uploads/2026/01/1347812368.png
Normal file
|
After Width: | Height: | Size: 215 KiB |
BIN
usr/uploads/2026/01/138299279.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
usr/uploads/2026/01/1460346294.png
Normal file
|
After Width: | Height: | Size: 333 KiB |
BIN
usr/uploads/2026/01/1607820026.png
Normal file
|
After Width: | Height: | Size: 211 KiB |
BIN
usr/uploads/2026/01/164089559.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
usr/uploads/2026/01/1955380027.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
usr/uploads/2026/01/1999357887.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
usr/uploads/2026/01/2028403772.png
Normal file
|
After Width: | Height: | Size: 344 KiB |
BIN
usr/uploads/2026/01/2038690442.png
Normal file
|
After Width: | Height: | Size: 213 KiB |
BIN
usr/uploads/2026/01/2130124341.png
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
usr/uploads/2026/01/2175544241.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
usr/uploads/2026/01/2203224117.png
Normal file
|
After Width: | Height: | Size: 304 KiB |
BIN
usr/uploads/2026/01/2263720425.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
usr/uploads/2026/01/2342412742.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
usr/uploads/2026/01/2387308251.png
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
usr/uploads/2026/01/2485833343.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
usr/uploads/2026/01/2512136969.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
usr/uploads/2026/01/2532989208.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
usr/uploads/2026/01/2556804585.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
usr/uploads/2026/01/2851965842.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
usr/uploads/2026/01/2896637320.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
usr/uploads/2026/01/3003260076.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
usr/uploads/2026/01/3048304592.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
usr/uploads/2026/01/3115066687.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
usr/uploads/2026/01/3127675096.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
usr/uploads/2026/01/3465595596.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
usr/uploads/2026/01/3552607588.png
Normal file
|
After Width: | Height: | Size: 308 KiB |
BIN
usr/uploads/2026/01/3620182230.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
usr/uploads/2026/01/3776725632.png
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
usr/uploads/2026/01/3793017173.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
usr/uploads/2026/01/3794216455.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
usr/uploads/2026/01/3820866135.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
usr/uploads/2026/01/3836954535.png
Normal file
|
After Width: | Height: | Size: 279 KiB |
BIN
usr/uploads/2026/01/3922588579.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
usr/uploads/2026/01/3962679033.png
Normal file
|
After Width: | Height: | Size: 196 KiB |
BIN
usr/uploads/2026/01/3986479521.png
Normal file
|
After Width: | Height: | Size: 226 KiB |
BIN
usr/uploads/2026/01/410512821.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
usr/uploads/2026/01/4237002661.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
usr/uploads/2026/01/4270541227.png
Normal file
|
After Width: | Height: | Size: 251 KiB |
BIN
usr/uploads/2026/01/451152301.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
usr/uploads/2026/01/629740757.png
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
usr/uploads/2026/01/728288246.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
usr/uploads/2026/01/783825228.zip
Normal file
BIN
usr/uploads/2026/01/865164219.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
usr/uploads/2026/01/944042128.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
84
usr/uploads/2026/02/1640333784.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#ifndef __DX_WF_24_H__
|
||||
#define __DX_WF_24_H__
|
||||
|
||||
#include "usart.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/* 默认超时时间(毫秒) */
|
||||
#define WIFI_DEFAULT_TIMEOUT 1000
|
||||
|
||||
/* WiFi 接收缓冲区大小 */
|
||||
#define WIFI_RX_BUF_SIZE 1024
|
||||
|
||||
/* WiFi 数据结构体 */
|
||||
typedef struct {
|
||||
uint8_t rx_buffer[WIFI_RX_BUF_SIZE]; // 接收缓冲区
|
||||
uint16_t rx_len; // 本次接收长度
|
||||
uint8_t rx_flag; // 接收完成标志
|
||||
} WIFI_HandleTypeDef;
|
||||
|
||||
extern WIFI_HandleTypeDef wifi;
|
||||
|
||||
/* MQTT消息结构体 */
|
||||
typedef struct {
|
||||
char topic[128];
|
||||
char payload[256];
|
||||
uint16_t payload_len;
|
||||
} MQTT_Message_t;
|
||||
|
||||
|
||||
// SNTP 时间结构体
|
||||
typedef struct {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint8_t valid; // 1表示有效,0表示无效
|
||||
} SNTP_Time_t;
|
||||
|
||||
extern SNTP_Time_t sntp_time;
|
||||
|
||||
|
||||
/* 解析函数 */
|
||||
uint8_t WIFI_Parse_MQTT_Message(char *input, MQTT_Message_t *msg);
|
||||
|
||||
|
||||
/* MQTT运行状态 */
|
||||
typedef enum {
|
||||
MQTT_STATE_IDLE = 0,
|
||||
MQTT_STATE_WIFI_CONNECTED,
|
||||
MQTT_STATE_SERVER_CONNECTED,
|
||||
MQTT_STATE_SUBSCRIBED
|
||||
} MQTT_State_t;
|
||||
|
||||
/* 函数声明 */
|
||||
HAL_StatusTypeDef WIFI_SEND_DMA(const char *data);
|
||||
HAL_StatusTypeDef WIFI_RECV_DMA_Init(void);
|
||||
void WIFI_UART_IDLE_Callback(UART_HandleTypeDef *huart);
|
||||
// int WIFI_Get_Received_Data(uint8_t *buffer, uint16_t len);
|
||||
|
||||
/* 同步应答检测函数 */
|
||||
uint8_t WIFI_CheckAck(const char *cmd, const char *expect, uint32_t timeout_ms);
|
||||
|
||||
/* 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);
|
||||
|
||||
uint8_t WIFI_MQTT_Publish_RAW(const char *topic, const uint8_t *payload,
|
||||
uint16_t length, uint8_t qos, uint8_t retain);
|
||||
|
||||
void wifi_task_mqtt(void *argument);
|
||||
uint8_t WIFI_MQTT_Publish_Sensor(const char *json);
|
||||
uint8_t WIFI_MQTT_Publish_Status(const char *json);
|
||||
|
||||
/* ================= SNTP函数声明 ================= */
|
||||
uint8_t WIFI_Enable_SNTP(void);
|
||||
uint8_t WIFI_Get_SNTP_Time(void);
|
||||
SNTP_Time_t WIFI_Get_Current_Time(void);
|
||||
uint8_t WIFI_Is_Time_Valid(void);
|
||||
|
||||
#endif /* __DX_WF_24_H__ */
|
||||
672
usr/uploads/2026/02/3930863264.c
Normal file
@@ -0,0 +1,672 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
BIN
usr/uploads/2026/03/1522479772.png
Normal file
|
After Width: | Height: | Size: 338 KiB |
BIN
usr/uploads/2026/03/2144686927.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
usr/uploads/2026/03/2319058680.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
usr/uploads/2026/03/2484166875.png
Normal file
|
After Width: | Height: | Size: 315 KiB |
BIN
usr/uploads/2026/03/2556797285.png
Normal file
|
After Width: | Height: | Size: 262 KiB |
BIN
usr/uploads/2026/03/27012603.png
Normal file
|
After Width: | Height: | Size: 489 KiB |
BIN
usr/uploads/2026/03/383181920.png
Normal file
|
After Width: | Height: | Size: 296 KiB |