1. 完成了ota功能的基本测试,测试通过
2. 封装了一个模板线程类,支持创建来自单例类的成员函数线程,普通类的线程,普通函数线程 3. 封装了一个Wifi模块类,支持Wifi的各种基本配置
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/4.
|
||||
//
|
||||
|
||||
#include "app_ota.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_https_ota.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char *TAG = "app_ota";
|
||||
#define DEFAULT_VERSION "0.0.1"
|
||||
|
||||
/* 本地版本号,编译时可由构建系统注入 */
|
||||
#ifndef APP_FW_VERSION
|
||||
#define APP_FW_VERSION DEFAULT_VERSION
|
||||
#endif
|
||||
|
||||
static const char s_version[] = APP_FW_VERSION;
|
||||
|
||||
const char *app_ota_current_version(void)
|
||||
{
|
||||
return s_version;
|
||||
}
|
||||
|
||||
/* 简单版本号比较:a.b.c 字符串比较即可 */
|
||||
static bool need_update(const char *server_ver)
|
||||
{
|
||||
if (!server_ver) return false;
|
||||
return strcmp(server_ver, s_version) > 0;
|
||||
}
|
||||
|
||||
/* HTTP 事件回调,仅打印进度 */
|
||||
static esp_err_t http_evt(esp_http_client_event_t *evt)
|
||||
{
|
||||
switch (evt->event_id) {
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
/* 数据流直接走 OTA,这里不打印 */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void ota_task(void *pv)
|
||||
{
|
||||
char url[256];
|
||||
strncpy(url, (const char *)pv, sizeof(url) - 1);
|
||||
url[sizeof(url) - 1] = '\0';
|
||||
|
||||
ESP_LOGI(TAG, "开始 OTA,URL=%s", url);
|
||||
|
||||
esp_http_client_config_t http_cfg = {
|
||||
.url = url,
|
||||
.event_handler = http_evt,
|
||||
.keep_alive_enable = true,
|
||||
// 如用 https,把 cert_pem 打开即可
|
||||
// .cert_pem = (const char *)server_cert_pem_start,
|
||||
};
|
||||
|
||||
esp_https_ota_config_t ota_cfg = {
|
||||
.http_config = &http_cfg,
|
||||
};
|
||||
|
||||
/* 执行升级 */
|
||||
esp_err_t ret = esp_https_ota(&ota_cfg);
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "OTA 完成,准备重启");
|
||||
esp_restart();
|
||||
} else {
|
||||
ESP_LOGE(TAG, "OTA 失败,err=%s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
esp_err_t app_ota_start(const char *url)
|
||||
{
|
||||
if (!url) return ESP_ERR_INVALID_ARG;
|
||||
/* 内部建 Task,栈 8 KB */
|
||||
BaseType_t ok = xTaskCreate(ota_task, "ota_task", 8192, (void *)url, 5, NULL);
|
||||
return ok == pdPASS ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/4.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
/* 一键启动 OTA(阻塞,内部建 Task) */
|
||||
esp_err_t app_ota_start(const char *url);
|
||||
|
||||
/* 获取当前运行版本号(返回静态指针) */
|
||||
const char *app_ota_current_version(void);
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/4.
|
||||
//
|
||||
|
||||
#include "ota_ws.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_websocket_client.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_https_ota.h"
|
||||
#include "cJSON.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_mac.h"
|
||||
|
||||
static const char *TAG = "OTA_WS";
|
||||
|
||||
#define FW_VERSION "1.0.1" // ←每次发布修改这里
|
||||
#define WS_URL_FMT "ws://%s:%d/ws?mac=%02X%02X%02X%02X%02X%02X"
|
||||
|
||||
static esp_websocket_client_handle_t ws = NULL;
|
||||
|
||||
static void ota_start(const char *url)
|
||||
{
|
||||
esp_http_client_config_t config = {
|
||||
.url = url,
|
||||
.keep_alive_enable = true,
|
||||
};
|
||||
esp_https_ota_config_t ota_cfg = { .http_config = &config };
|
||||
ESP_LOGI(TAG, "开始下载固件...");
|
||||
esp_err_t ret = esp_https_ota(&ota_cfg);
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "下载完成,重启");
|
||||
esp_restart();
|
||||
} else {
|
||||
ESP_LOGE(TAG, "OTA 失败:%s", esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
|
||||
static void ws_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
esp_websocket_event_data_t *d = (esp_websocket_event_data_t *)data;
|
||||
switch (id) {
|
||||
case WEBSOCKET_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "WebSocket 已连接");
|
||||
break;
|
||||
|
||||
case WEBSOCKET_EVENT_DATA:
|
||||
if (d->op_code == WS_TRANSPORT_OPCODES_TEXT) {
|
||||
char *json = malloc(d->data_len + 1);
|
||||
if (!json) break;
|
||||
memcpy(json, d->data_ptr, d->data_len);
|
||||
json[d->data_len] = 0;
|
||||
|
||||
cJSON *root = cJSON_Parse(json);
|
||||
free(json);
|
||||
if (root) {
|
||||
cJSON *cmd = cJSON_GetObjectItem(root, "cmd");
|
||||
if (cmd && strcmp(cmd->valuestring, "upgrade") == 0) {
|
||||
const char *url = cJSON_GetObjectItem(root, "url")->valuestring;
|
||||
ESP_LOGI(TAG, "收到升级指令,URL=%s", url);
|
||||
ota_start(url);
|
||||
}
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WEBSOCKET_EVENT_DISCONNECTED:
|
||||
ESP_LOGW(TAG, "WS 断开,5 秒后重连...");
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
esp_websocket_client_start(ws);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t ota_ws_start(const char *server_ip, uint16_t port)
|
||||
{
|
||||
if (ws) return ESP_OK;
|
||||
|
||||
uint8_t mac[6];
|
||||
esp_efuse_mac_get_default(mac);
|
||||
char uri[128];
|
||||
snprintf(uri, sizeof(uri), WS_URL_FMT,
|
||||
server_ip, port, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
esp_websocket_client_config_t cfg = {
|
||||
.uri = uri,
|
||||
.ping_interval_sec = 30,
|
||||
};
|
||||
ws = esp_websocket_client_init(&cfg);
|
||||
esp_websocket_register_events(ws, WEBSOCKET_EVENT_ANY, ws_event, NULL);
|
||||
esp_websocket_client_start(ws);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/4.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* @brief 启动 WebSocket 长连接,接收服务器推送的升级指令
|
||||
* @param server_ip 服务器 IP 字符串,如 "192.168.1.100"
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t ota_ws_start(const char *server_ip, uint16_t port);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+112
-35
@@ -14,46 +14,41 @@ ble_device_info_t *ble_device_list = NULL;
|
||||
void Wireless_Init(void)
|
||||
{
|
||||
// Initialize NVS.
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
esp_err_t ret = nvs_flash_init(); // 初始化Flash
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { // 如果Flash有错误,则进行修复
|
||||
ESP_ERROR_CHECK(nvs_flash_erase()); // 清空Flash
|
||||
ret = nvs_flash_init(); // 重新初始化Flash
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
// WiFi
|
||||
xTaskCreatePinnedToCore(
|
||||
WIFI_Init,
|
||||
"WIFI task",
|
||||
4096,
|
||||
NULL,
|
||||
1,
|
||||
NULL,
|
||||
0);
|
||||
// BLE
|
||||
xTaskCreatePinnedToCore(
|
||||
BLE_Init,
|
||||
"BLE task",
|
||||
4096,
|
||||
NULL,
|
||||
2,
|
||||
NULL,
|
||||
0);
|
||||
ESP_ERROR_CHECK( ret ); // 检查错误
|
||||
// WiFi 手动调用Wifi初始化
|
||||
// xTaskCreatePinnedToCore(
|
||||
// WIFI_Init,
|
||||
// "WIFI task",
|
||||
// 4096,
|
||||
// NULL,
|
||||
// 1,
|
||||
// NULL,
|
||||
// 0);
|
||||
// BLE 当前工程不启用蓝牙
|
||||
// xTaskCreatePinnedToCore(
|
||||
// BLE_Init,
|
||||
// "BLE task",
|
||||
// 4096,
|
||||
// NULL,
|
||||
// 2,
|
||||
// NULL,
|
||||
// 0);
|
||||
}
|
||||
|
||||
void WIFI_Init(void *arg)
|
||||
{
|
||||
esp_netif_init();
|
||||
esp_event_loop_create_default();
|
||||
esp_netif_create_default_wifi_sta();
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
esp_wifi_init(&cfg);
|
||||
esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
esp_wifi_start();
|
||||
|
||||
WIFI_NUM = WIFI_Scan();
|
||||
printf("WIFI:%d\r\n",WIFI_NUM);
|
||||
|
||||
vTaskDelete(NULL);
|
||||
esp_netif_init(); // 初始化网络接口
|
||||
esp_event_loop_create_default(); // 创建事件循环
|
||||
esp_netif_create_default_wifi_sta(); // 创建默认的WiFi Station
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); // 创建WiFi初始化配置(使用默认配置)
|
||||
esp_wifi_init(&cfg); // 初始化WiFi
|
||||
esp_wifi_set_mode(WIFI_MODE_STA); // 设置WiFi模式为Station
|
||||
esp_wifi_start(); // 启动WiFi
|
||||
}
|
||||
|
||||
|
||||
@@ -269,6 +264,88 @@ uint16_t BLE_Scan(void)
|
||||
return BLE_NUM;
|
||||
}
|
||||
|
||||
|
||||
// 下面是新增的实现
|
||||
static EventGroupHandle_t s_wifi_event_group;
|
||||
#define WIFI_CONNECTED_BIT BIT0
|
||||
#define WIFI_FAIL_BIT BIT1
|
||||
|
||||
static const char *TAG = "WiFi_UTIL";
|
||||
|
||||
/* --------------------------- 事件回调 --------------------------- */
|
||||
static void event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------- 自动连接 --------------------------- */
|
||||
bool WiFi_AutoConnect(const char *ssid, const char *password, uint8_t max_retry)
|
||||
{
|
||||
if (s_wifi_event_group == NULL) {
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
}
|
||||
|
||||
/* 注册一次性事件监听 */
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
WIFI_EVENT_STA_DISCONNECTED,
|
||||
&event_handler,
|
||||
NULL,
|
||||
NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&event_handler,
|
||||
NULL,
|
||||
NULL));
|
||||
|
||||
wifi_config_t wifi_config = { 0 };
|
||||
strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
|
||||
strncpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password));
|
||||
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||
|
||||
uint8_t retry_cnt = 0;
|
||||
while (retry_cnt <= max_retry) {
|
||||
ESP_LOGI(TAG, "WiFi connect attempt %u/%u to \"%s\"", retry_cnt + 1, max_retry + 1, ssid);
|
||||
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT);
|
||||
esp_wifi_connect();
|
||||
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
pdFALSE, pdFALSE,
|
||||
pdMS_TO_TICKS(6000)); /* 6 秒超时 */
|
||||
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "WiFi connected");
|
||||
goto success;
|
||||
}
|
||||
|
||||
/* 失败,准备重试 */
|
||||
retry_cnt++;
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "WiFi connect failed after %u retries", max_retry + 1);
|
||||
return false;
|
||||
|
||||
success:
|
||||
return true;
|
||||
}
|
||||
|
||||
/* --------------------------- 断开连接 --------------------------- */
|
||||
esp_err_t WiFi_Disconnect(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Disconnecting WiFi...");
|
||||
return esp_wifi_disconnect();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 获取WiFi AP列表
|
||||
uint16_t wireless_get_wifi_ap_list(wifi_ap_info_t *ap_list, uint16_t max_aps) {
|
||||
if (!wifi_ap_list || wifi_ap_count == 0) {
|
||||
|
||||
+19
-1
@@ -41,7 +41,25 @@ uint16_t WIFI_Scan(void);
|
||||
void BLE_Init(void *arg);
|
||||
uint16_t BLE_Scan(void);
|
||||
|
||||
// 新增接口函数
|
||||
// 下面的函数为新增的函数
|
||||
// Code By Misaki
|
||||
|
||||
/**
|
||||
* @brief 自动连接指定 Wi-Fi,带重试
|
||||
* @param ssid 目标热点名称
|
||||
* @param password 密码
|
||||
* @param max_retry 最大重连次数(0 表示仅尝试一次)
|
||||
* @return true 成功连接
|
||||
* false 达到重试上限仍未连上
|
||||
*/
|
||||
bool WiFi_AutoConnect(const char *ssid, const char *password, uint8_t max_retry);
|
||||
|
||||
/**
|
||||
* @brief 断开当前 STA 连接
|
||||
*/
|
||||
esp_err_t WiFi_Disconnect(void);
|
||||
|
||||
// 接口函数
|
||||
uint16_t wireless_get_wifi_ap_list(wifi_ap_info_t *ap_list, uint16_t max_aps);
|
||||
uint16_t wireless_get_ble_device_list(ble_device_info_t *device_list, uint16_t max_devices);
|
||||
void wireless_print_wifi_aps(void);
|
||||
|
||||
Reference in New Issue
Block a user