2026-06-14 17:10:24 +08:00
|
|
|
|
/*
|
2026-04-27 00:42:26 +08:00
|
|
|
|
* wifi_ota.h - WiFi OTA 服务器
|
|
|
|
|
|
* T4.1 - T4.5
|
2026-05-31 03:20:26 +08:00
|
|
|
|
*
|
|
|
|
|
|
* WifiOta 类封装 WiFi 初始化、HTTP OTA 服务器和网络状态查询。
|
2026-06-14 23:03:28 +08:00
|
|
|
|
* ESP32 本机 OTA 的写入/收尾逻辑已抽离到 Esp32OtaCore(WiFi/BLE 共用)。
|
2026-04-27 00:42:26 +08:00
|
|
|
|
*/
|
|
|
|
|
|
#ifndef WIFI_OTA_H
|
|
|
|
|
|
#define WIFI_OTA_H
|
|
|
|
|
|
|
2026-05-31 03:20:26 +08:00
|
|
|
|
#include "esp_ota_ops.h"
|
|
|
|
|
|
#include "esp_http_server.h"
|
|
|
|
|
|
#include "esp_event.h"
|
2026-06-20 00:46:04 +08:00
|
|
|
|
#include "freertos/task.h"
|
2026-06-14 17:10:24 +08:00
|
|
|
|
#include <atomic>
|
2026-04-27 00:42:26 +08:00
|
|
|
|
|
2026-06-22 01:53:16 +08:00
|
|
|
|
#include "ota/ota_coordinator.h" // IBleCoexListener 接口定义
|
|
|
|
|
|
|
2026-06-21 23:48:31 +08:00
|
|
|
|
/** @brief WiFi 工作模式枚举,区分 AP(设备热点)和 STA(连接路由器)模式。 */
|
2026-04-27 00:42:26 +08:00
|
|
|
|
typedef enum {
|
2026-06-21 23:48:31 +08:00
|
|
|
|
OTA_WIFI_MODE_AP, ///< AP 模式:设备作为热点供局域网设备连接
|
|
|
|
|
|
OTA_WIFI_MODE_STA ///< STA 模式:设备连接上级路由器
|
2026-04-27 00:42:26 +08:00
|
|
|
|
} ota_wifi_mode_t;
|
|
|
|
|
|
|
2026-06-22 01:53:16 +08:00
|
|
|
|
class WifiOta : public IBleCoexListener {
|
2026-05-31 03:20:26 +08:00
|
|
|
|
public:
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 获取 WifiOta 单例实例。
|
|
|
|
|
|
* @return WifiOta 单例引用。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
static WifiOta& instance();
|
2026-04-27 00:42:26 +08:00
|
|
|
|
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 在调度器启动前初始化 WiFi 并尝试 STA 连接。
|
|
|
|
|
|
* @return true 表示 STA 初始化中,false 需回退至 AP 模式。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
bool earlyInit();
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief WiFi OTA FreeRTOS 任务入口,提供 HTTP 上传和中继触发。
|
|
|
|
|
|
* @param pvParameters FreeRTOS 任务参数。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
static void taskEntry(void *pvParameters);
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 获取当前 WiFi 工作模式。
|
|
|
|
|
|
* @return 当前 WiFi 模式(AP 或 STA)。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
static ota_wifi_mode_t getMode();
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 获取当前 WiFi 信号强度。
|
|
|
|
|
|
* @return 信号强度值(dBm)。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
static int8_t getRssi();
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 判断 STA 是否已连接到上级 WiFi。
|
|
|
|
|
|
* @return true 已连接,false 未连接。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
static bool isConnected();
|
2026-04-27 00:42:26 +08:00
|
|
|
|
|
2026-06-14 23:03:28 +08:00
|
|
|
|
// ── WiFi 连接状态(WiFi 事件回调 / HTTP / reconnect task 共享,需 mutex 保护)──
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/** @brief 获取 STA 连接状态。 */
|
2026-06-14 17:10:24 +08:00
|
|
|
|
bool getStaConnected() const { return m_staConnected.load(); }
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/** @brief 设置 STA 连接状态。 */
|
2026-06-14 17:10:24 +08:00
|
|
|
|
void setStaConnected(bool v) { m_staConnected.store(v); }
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/** @brief 获取 STA 重试次数。 */
|
2026-06-14 17:10:24 +08:00
|
|
|
|
int getStaRetryCount() const { return m_staRetryCount.load(); }
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/** @brief 设置 STA 重试次数。 */
|
2026-06-14 17:10:24 +08:00
|
|
|
|
void setStaRetryCount(int v) { m_staRetryCount.store(v); }
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/** @brief 获取 STA 重试是否已耗尽。 */
|
2026-06-14 17:10:24 +08:00
|
|
|
|
bool getStaRetryExhausted() const { return m_staRetryExhausted.load(); }
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/** @brief 设置 STA 重试耗尽标志。 */
|
2026-06-14 17:10:24 +08:00
|
|
|
|
void setStaRetryExhausted(bool v) { m_staRetryExhausted.store(v); }
|
|
|
|
|
|
// ── 状态互斥锁(保护复合读-改-写操作)──
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/** @brief 获取状态互斥锁句柄。 */
|
2026-06-14 17:10:24 +08:00
|
|
|
|
SemaphoreHandle_t getStateMutex() const { return m_stateMutex; }
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/** @brief 锁定状态互斥锁。 */
|
2026-06-14 17:10:24 +08:00
|
|
|
|
void stateLock() { if (m_stateMutex) xSemaphoreTake(m_stateMutex, portMAX_DELAY); }
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/** @brief 解锁状态互斥锁。 */
|
2026-06-14 17:10:24 +08:00
|
|
|
|
void stateUnlock() { if (m_stateMutex) xSemaphoreGive(m_stateMutex); }
|
|
|
|
|
|
|
|
|
|
|
|
// ── 初始化/运行时状态(仅 OTA 任务访问,无需额外锁)──
|
2026-06-21 23:48:31 +08:00
|
|
|
|
ota_wifi_mode_t m_wifiMode = OTA_WIFI_MODE_AP; ///< 当前 WiFi 工作模式
|
|
|
|
|
|
bool m_coldBoot = false; ///< 是否为冷启动(首次上电)
|
|
|
|
|
|
SemaphoreHandle_t m_staGotIpSem = NULL; ///< STA 获取 IP 信号量
|
|
|
|
|
|
TaskHandle_t m_reconnectTask = nullptr; ///< STA 后台重连任务句柄
|
2026-04-27 00:42:26 +08:00
|
|
|
|
|
2026-06-21 23:48:31 +08:00
|
|
|
|
httpd_handle_t m_httpServer = NULL; ///< HTTP OTA 服务器句柄
|
2026-04-27 00:42:26 +08:00
|
|
|
|
|
2026-06-16 23:17:06 +08:00
|
|
|
|
// ★ BLE OTA 传输期间暂停 WiFi 射频活动(减少共存干扰)
|
|
|
|
|
|
std::atomic<bool> m_bleOtaActive{false};
|
|
|
|
|
|
/** @brief BLE OTA 开始传输时调用,暂停 WiFi RSSI 轮询等射频活动。 */
|
|
|
|
|
|
void setBleOtaActive(bool v) { m_bleOtaActive.store(v); }
|
|
|
|
|
|
|
2026-07-16 01:24:12 +08:00
|
|
|
|
// ── IBleCoexListener 接口实现 ──
|
2026-06-22 01:53:16 +08:00
|
|
|
|
/** @brief BLE OTA 开始,暂停 WiFi RSSI 轮询。 */
|
|
|
|
|
|
void onBleOtaStart() override;
|
|
|
|
|
|
/** @brief BLE OTA 结束,恢复 WiFi 射频活动。 */
|
|
|
|
|
|
void onBleOtaEnd() override;
|
|
|
|
|
|
|
2026-06-20 00:46:04 +08:00
|
|
|
|
/** @brief 获取 WiFi OTA 任务句柄。 */
|
|
|
|
|
|
TaskHandle_t getTaskHandle() const { return m_taskHandle; }
|
|
|
|
|
|
|
2026-06-14 17:10:24 +08:00
|
|
|
|
private:
|
|
|
|
|
|
// ── 跨任务共享状态(atomic + m_stateMutex 保护)──
|
|
|
|
|
|
std::atomic<bool> m_staConnected{false};
|
|
|
|
|
|
std::atomic<int> m_staRetryCount{0};
|
|
|
|
|
|
std::atomic<bool> m_staRetryExhausted{false};
|
|
|
|
|
|
SemaphoreHandle_t m_stateMutex = NULL;
|
2026-06-20 00:46:04 +08:00
|
|
|
|
TaskHandle_t m_taskHandle = nullptr;
|
2026-06-14 17:10:24 +08:00
|
|
|
|
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 初始化 SoftAP 模式,供局域网 OTA 使用。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
void initAp();
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 初始化 STA 模式并连接预配置路由器。
|
|
|
|
|
|
* @return ESP_OK 成功,其他失败。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
esp_err_t initSta();
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief WiFi/IP 事件回调,处理连接、断开和 IP 获取。
|
|
|
|
|
|
* @param arg 回调用户参数。
|
|
|
|
|
|
* @param event_base 事件基类。
|
|
|
|
|
|
* @param event_id 具体事件 ID。
|
|
|
|
|
|
* @param event_data 事件附带数据。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
static void wifiEventHandler(void *arg, esp_event_base_t event_base,
|
|
|
|
|
|
int32_t event_id, void *event_data);
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief STA 后台重连任务,处理退避延迟和重连。
|
|
|
|
|
|
* @param pvParameters FreeRTOS 任务参数。
|
|
|
|
|
|
*/
|
2026-06-14 17:10:24 +08:00
|
|
|
|
static void reconnectTask(void *pvParameters);
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 启动 HTTP OTA 服务器,注册各接口路由。
|
|
|
|
|
|
* @return ESP_OK 成功,其他失败。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
esp_err_t httpServerStart();
|
2026-06-15 01:14:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 等待 STA 连接成功并获取 IP 地址。
|
|
|
|
|
|
* @param ip_str 输出缓冲区,存放 IP 字符串。
|
|
|
|
|
|
* @param ip_len 缓冲区长度。
|
|
|
|
|
|
* @return ESP_OK 连接成功,ESP_ERR_TIMEOUT 超时。
|
|
|
|
|
|
*/
|
2026-05-31 03:20:26 +08:00
|
|
|
|
esp_err_t waitStaConnected(char *ip_str, size_t ip_len);
|
|
|
|
|
|
};
|
2026-04-27 00:42:26 +08:00
|
|
|
|
|
2026-06-14 23:03:28 +08:00
|
|
|
|
#endif // WIFI_OTA_H
|