1. 完成了ota功能的基本测试,测试通过

2. 封装了一个模板线程类,支持创建来自单例类的成员函数线程,普通类的线程,普通函数线程
3. 封装了一个Wifi模块类,支持Wifi的各种基本配置
This commit is contained in:
Misaki
2025-09-05 01:09:12 +08:00
parent 5d79f88918
commit 28ceb0caf5
24 changed files with 824 additions and 91 deletions
-12
View File
@@ -3,15 +3,3 @@
//
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
+3 -2
View File
@@ -6,5 +6,6 @@
#include "OTAClass.h"
void Cpp_Hand() {
OTAClass::Init();
}
OTAClass oc;
oc.Init();
}
+135
View File
@@ -4,6 +4,141 @@
#include "OTAClass.h"
#include "esp_log.h"
#include <iostream>
#include <thread>
#include <chrono>
#include <memory>
#include <sstream>
#include <pthread.h>
#include <esp_pthread.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
using namespace std::chrono;
const auto sleep_time = seconds{
5
};
// 下面的这个函数可以放在任何线程中,自动打印出对应线程的信息
void print_thread_info(const char *extra = nullptr)
{
std::stringstream ss;
if (extra) {
ss << extra;
}
ss << "Core id: " << xPortGetCoreID()
<< ", prio: " << uxTaskPriorityGet(nullptr)
<< ", minimum free stack: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes.";
ESP_LOGI(pcTaskGetName(nullptr), "%s", ss.str().c_str());
}
void thread_func_inherited()
{
while (true) {
print_thread_info("我是 普普通通的inherited 线程");
std::this_thread::sleep_for(sleep_time);
}
}
void thread_func_any_core()
{
while (true) {
print_thread_info("我是一个会跑在任意一个核的任务~");
std::this_thread::sleep_for(sleep_time);
}
}
esp_pthread_cfg_t create_config(const char *name, int core_id, int stack, int prio)
{
auto cfg = esp_pthread_get_default_config();
cfg.thread_name = name;
cfg.pin_to_core = core_id;
cfg.stack_size = stack;
cfg.prio = prio;
return cfg;
}
#include "ThreadManager.h"
#include "WifiConnectors.h"
#include <string>
void OTAClass::Init() {
ESP_LOGI("OTA", "Init");
ESP_LOGI("OTAClass::Init", "当前固件版本 1.0.1");
// 测试Wifi
// WifiConnectors::getInstance()->log();
//
// WifiConnectors::getInstance()->connectWifi("Misaki-2.4G", "88888888");
// 1. 创建普通函数一个可以运行在任意核上的线程
ThreadConfig config1;
config1.name = "NormalThread";
// config1.core_id = 0; // 不指定运行在哪个核,使其自动选择
config1.stack_size = 3072;
config1.priority = 5; // 优先级
std::thread normal_thread = ThreadManager::createThread(config1, thread_func_any_core);
ThreadConfig config2;
config2.name = "Thread2";
config2.core_id = 1; // 指定运行在核1
config2.stack_size = 3072;
config2.priority = 5;
std::thread thread2 = ThreadManager::createThread(config2, thread_func_inherited);
// 配置Wifi连接线程参数
ThreadConfig wifi_config;
wifi_config.name = "WifiConnector"; // 线程名称
wifi_config.core_id = 1; // 绑定到核心1(避免与主线程冲突)
wifi_config.stack_size = 4096; // 设置稍大的栈空间(Wifi连接可能需要)
wifi_config.priority = 6; // 设置较高优先级(确保连接及时)
// 使用单例方式创建线程,调用connectWifi成员函数
std::thread wifi_thread = ThreadManager::createSingletonThread<WifiConnectors>(
wifi_config,
&WifiConnectors::connectWifi,
"Misaki-2.4G", // SSID
"88888888", // 密码
5 // 最大重试次数
);
ThreadConfig ota_config;
ota_config.name = "OTA";
ota_config.stack_size = 4096;
ota_config.priority = 6;
ota_config.core_id = 0;
std::thread ota_thread = ThreadManager::createMemberThread<OTAClass>(
ota_config,
this,
&OTAClass::Update
);
while (true) {
std::stringstream ss;
ss << "core id: " << xPortGetCoreID()
<< ", prio: " << uxTaskPriorityGet(nullptr)
<< ", minimum free stack: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes.";
ESP_LOGI(pcTaskGetName(nullptr), "%s", ss.str().c_str());
std::this_thread::sleep_for(sleep_time);
}
}
#include "ota_ws.h"
// 启动OTA更新线程,前提是已经连接WiFi
void OTAClass::Update() {
// 测试OTA服务器192.168.1.11
while (true) {
if (WifiConnectors::getInstance()->isWifiConnect()) { // 如果Wifi已连接
ota_ws_start("192.168.1.11", 8080);
// 启动完就退出,删除自身
break;
}else {
std::this_thread::sleep_for(sleep_time);
}
}
vTaskDelete(nullptr);
}
+1 -9
View File
@@ -4,18 +4,10 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
class OTAClass {
public:
static void Init(void);
void Init(void);
void Update(void);
};
#ifdef __cplusplus
}
#endif
-12
View File
@@ -2,15 +2,3 @@
// Created by misaki on 2025/9/2.
//
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,4 @@
//
// Created by misaki on 2025/9/4.
//
#include "ThreadManager.h"
@@ -0,0 +1,149 @@
//
// Created by misaki on 2025/9/4.
//
#pragma once
#include <thread>
#include <functional>
#include <memory>
#include <string>
#include <esp_pthread.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>
#include <sstream>
/**
* @brief 线程配置结构体
*
* 用于配置线程的各种属性,如名称、核心绑定、栈大小和优先级
*/
struct ThreadConfig {
std::string name = "thread"; // 线程名称
int core_id = -1; // 绑定核心ID (-1表示不绑定)
int stack_size = 3072; // 栈大小 (字节)
int priority = 5; // 优先级
bool inherit_cfg = false; // 是否允许子线程继承此配置
};
/**
* @brief 线程管理类
*
* 封装了ESP32上的线程创建和管理功能,支持普通函数、类成员函数和单例类成员函数
*/
class ThreadManager {
public:
/**
* @brief 创建并启动一个线程
*
* @tparam Function 函数类型
* @tparam Args 参数类型
* @param config 线程配置
* @param func 要执行的函数
* @param args 函数参数
* @return std::thread 创建的线程对象
*/
template<typename Function, typename... Args>
static std::thread createThread(const ThreadConfig& config, Function&& func, Args&&... args) {
// 创建ESP32线程配置
auto esp_cfg = create_esp_config(config);
// 设置线程配置
esp_pthread_set_cfg(&esp_cfg);
// 创建并启动线程
return std::thread(std::forward<Function>(func), std::forward<Args>(args)...);
}
/**
* @brief 创建并启动一个执行类成员函数的线程
*
* @tparam T 类类型
* @tparam Method 成员函数类型
* @tparam Args 参数类型
* @param config 线程配置
* @param obj 类对象指针
* @param method 成员函数指针
* @param args 函数参数
* @return std::thread 创建的线程对象
*/
template<typename T, typename Method, typename... Args>
static std::thread createMemberThread(const ThreadConfig& config, T* obj, Method&& method, Args&&... args) {
// 使用lambda表达式包装成员函数调用
auto task = [obj, method, args...]() {
(obj->*method)(args...);
};
// 创建ESP32线程配置
auto esp_cfg = create_esp_config(config);
// 设置线程配置
esp_pthread_set_cfg(&esp_cfg);
// 创建并启动线程
return std::thread(task);
}
/**
* @brief 创建并启动一个执行单例类成员函数的线程
*
* @tparam T 单例类类型
* @tparam Method 成员函数类型
* @tparam Args 参数类型
* @param config 线程配置
* @param method 成员函数指针
* @param args 函数参数
* @return std::thread 创建的线程对象
*/
template<typename T, typename Method, typename... Args>
static std::thread createSingletonThread(const ThreadConfig& config, Method&& method, Args&&... args) {
// 获取单例实例
T* instance = T::getInstance(); // 获取单例实例,注意这里就要求每个单例都要实现这个同名的静态函数
// 使用lambda表达式包装成员函数调用
auto task = [instance, method, args...]() {
(instance->*method)(args...);
};
// 创建ESP32线程配置
auto esp_cfg = create_esp_config(config);
// 设置线程配置
esp_pthread_set_cfg(&esp_cfg);
// 创建并启动线程
return std::thread(task);
}
/**
* @brief 打印当前线程信息
*
* @param extra 额外信息
*/
static void printThreadInfo(const char *extra = nullptr) {
std::stringstream ss;
if (extra) {
ss << extra;
}
ss << "Core id: " << xPortGetCoreID()
<< ", prio: " << uxTaskPriorityGet(nullptr)
<< ", minimum free stack: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes.";
ESP_LOGI(pcTaskGetName(nullptr), "%s", ss.str().c_str());
}
private:
/**
* @brief 创建ESP32线程配置
*
* @param config 线程配置
* @return esp_pthread_cfg_t ESP32线程配置
*/
static esp_pthread_cfg_t create_esp_config(const ThreadConfig& config) {
auto cfg = esp_pthread_get_default_config();
cfg.thread_name = config.name.c_str();
cfg.pin_to_core = config.core_id;
cfg.stack_size = config.stack_size;
cfg.prio = config.priority;
cfg.inherit_cfg = config.inherit_cfg;
return cfg;
}
};
-1
View File
@@ -2,4 +2,3 @@
// Created by misaki on 2025/9/2.
//
#include "ToolsClass.h"
+5 -9
View File
@@ -3,14 +3,10 @@
//
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* 本模块提供各种杂项工具类,基本都来源于对底层驱动的封装
*
*
*/
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,74 @@
//
// Created by misaki on 2025/9/4.
//
#include "WifiConnectors.h"
#include "Wireless.h"
WifiConnectors* WifiConnectors::WifiConnectorsInstance = nullptr; /// 单例实例
std::mutex WifiConnectors::m_mutex;
WifiConnectors *WifiConnectors::getInstance() {
// 双检锁(DCLP),C++11 起 atomic+mutex 组合保证线程安全
WifiConnectors* tmp = WifiConnectorsInstance;
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(m_mutex);
tmp = WifiConnectorsInstance;
if (tmp == nullptr) {
tmp = new WifiConnectors();
WifiConnectorsInstance = tmp;
}
}
return tmp;
}
WifiConnectors::WifiConnectors() {
// 在此处调用底层的Wifi初始化驱动,这样Wifi就只会被初始化一次
Wireless_Init();
WIFI_Init(nullptr);
ESP_LOGI("WifiConnectors", "WifiConnectors getInstance");// 在此处调用底层的Wifi初始化驱动,这样Wifi就只会被初始化一次
}
// 析构函数
WifiConnectors::~WifiConnectors() {
}
// 连接Wifi
bool WifiConnectors::connectWifi(const std::string &ssid, const std::string &password, uint8_t max_retry) {
ESP_LOGI("WifiConnectors", "WifiConnectors connectWifi");
ESP_LOGI("WifiConnectors", "Now Try to connect %s", ssid.c_str());
if (WiFi_AutoConnect(ssid.c_str(), password.c_str(), max_retry)) {
ESP_LOGI("WifiConnectors", "WifiConnectors 连接WIFI %s 成功!!!", ssid.c_str());
this->isConnected = true; // 设置已连接
return true;
}
ESP_LOGI("WifiConnectors", "WifiConnectors 连接Wifi失败!!!");
return false;
}
// 断开Wifi
bool WifiConnectors::disconnectWifi() {
if (WiFi_Disconnect() == ESP_OK) {
ESP_LOGI("WifiConnectors", "WifiConnectors disconnectWifi");
this->isConnected = false; // 设置未连接
return true;
}
return false;
}
bool WifiConnectors::isWifiConnect() {
return this->isConnected;
}
void WifiConnectors::log() {
ESP_LOGI("WifiConnectors", "WifiConnectors log");
WIFI_Scan();
}
@@ -0,0 +1,48 @@
//
// Created by misaki on 2025/9/4.
//
#pragma once
#include <string>
#include <mutex>
class WifiConnectors{
// 显然,Wifi连接必须是单例的,否则必然出现冲突
public:
// 获取单例的静态方法
static WifiConnectors* getInstance();
public:
void log();
/**
* 连接Wifi
* 注意:此处只是对连接wifi接口做了C++封装,实际调用的时候需要为连接Wifi创建一个线程,不要让其阻塞主线程
* @param ssid Wifi名称
* @param password Wifi密码
* @param max_retry 最大重连次数
* @return 最终是否连接成功
*/
bool connectWifi(const std::string& ssid, const std::string& password, uint8_t max_retry = 3);
/**
*
* @return 是否成功断开连接
*/
bool disconnectWifi();
bool isWifiConnect();
private:
explicit WifiConnectors(); // 构造函数私有化
~WifiConnectors(); // 析构函数私有化
WifiConnectors(const WifiConnectors&) = delete; // 禁止拷贝
WifiConnectors& operator=(const WifiConnectors&) = delete; // 禁止赋值
private:
static WifiConnectors *WifiConnectorsInstance; /// 单例实例指针
static std::mutex m_mutex; /// 互斥锁以确保线程安全
bool isConnected = false; /// 当前Wifi是否连接成功
};