使用 C++ Inheritance 和 ESP32 | 掌握強大 Task 技術
簡介
繼承 (Inheritance) 是C++的一個強大特性,它顯著增強了面向對象編程(OOP)的能力。當它與ESP32結合使用時,這是一款擁有強大處理能力和對 FreeRTOS 支持的微控制器,使其成為開發多任務 (Task) 應用程序的理想選擇。通過利用 ESP-IDF 框架,開發者可以通過繼承來充分發揮 C++ 的潛力。在這篇博客中,我們將探索如何在 ESP32 上建立一個靈活且可重用的 C++ 任務 (Task) 管理系統,利用繼承來簡化嵌入式應用程序中多任務的管理。
內容
為什麼在 ESP32 上使用 C++ 繼承 (Inheritance)?
繼承(Inheritance)是物件導向程式設計(OOP)中的一個重要特性,允許類別從其他類別中繼承屬性和方法。透過實現一個封裝了通用功能的基類,可以減少代碼重複,並簡化複雜的多任務系統。在 ESP32 上應用 FreeRTOS 時,繼承讓我們能夠創建多功能的任務類別,從而簡化建立、管理和重複使用多個任務的過程。
開發環境設置
1. 安裝 Visual Studio Code,可從官方網站下載並且安裝。 2. 安裝 ESP-IDF 插件。
創建一個新項目
1. 在 VS Code 中,按下 Ctrl (Cmd) + Shift + P,然後輸入 ESP-IDF: New Project。
2. 選擇一個樣板專案(如 blink example),設定專案名稱(例如 my_project),並選擇存儲路徑。
3. 此時,系統會自動為您生成一個新的 ESP-IDF 項目,其中包含基本的 CMake 文件和一個範例程式碼。
檔案結構
my_project/
├── CMakeLists.txt # 構建配置
├── main/
│ ├── CMakeLists.txt # 主組件的CMake配置
│ └── main.cpp # UART類和應用邏輯
└── sdkconfig # 項目配置
使用繼承的 Task 的好處
1. 代碼重用:通用功能在基類中定義,並由所有任務類繼承(Inheritance),減少代碼重複。
2. 模塊化:每個任務封裝在自己的類中,使得單獨管理、擴展和修改任務變得簡單。
3. 可擴展性:可以通過簡單地繼承(Inheritance)基任務類並定義特定功能,輕鬆添加新任務。
4. 簡單性:任務管理被抽象化,使開發者能夠專注於任務邏輯,而不是冗長的FreeRTOS設置。
設置基礎 Task 類
我們的基礎任務類將封裝必要的 FreeRTOS 任務設置。這個類將提供:
一個構造函數來初始化任務屬性。
一個啟動方法來創建和啟動任務。
一個純虛擬函數 run(),派生類將重寫以定義特定的任務行為。
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
class Task {
public:
Task(const char* name, uint32_t stackSize, UBaseType_t priority)
: taskName(name), taskStackSize(stackSize), taskPriority(priority), taskHandle(nullptr) {}
void start() {
xTaskCreate(taskFunctionWrapper, taskName, taskStackSize, this, taskPriority, &taskHandle);
}
protected:
virtual void run() = 0;
private:
static void taskFunctionWrapper(void* parameter) {
Task* task = static_cast<Task*>(parameter);
task->run();
}
const char* taskName;
uint32_t taskStackSize;
UBaseType_t taskPriority;
TaskHandle_t taskHandle;
};
使用繼承創建專用 Task
讓我們使用這個基類為 ESP32 構建一個可重用的任務系統。我們將創建三個特定的任務類:
CounterTask:這個任務每秒遞增並打印一個計數器。
TemperatureTask:這個任務每5秒模擬一次溫度讀取。
HumidityTask:這個任務每3秒模擬一次濕度讀取。
每個任務都將從 Task 繼承,並在 run() 中實現其唯一功能。
在 app_main 中整合所有內容
要啟動所有任務,我們將創建每個派生任務類的實例,並調用它們的 start() 方法,如下所示。基類管理所有任務管理細節,而派生類通過繼承實現它們的特定邏輯。
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "string.h"
// Base class Task, encapsulating FreeRTOS task functionality
class Task {
public:
Task(const char* name, uint32_t stackSize, UBaseType_t priority)
: taskName(name), taskStackSize(stackSize), taskPriority(priority), taskHandle(nullptr) {}
void start() {
xTaskCreate(taskFunctionWrapper, taskName, taskStackSize, this, taskPriority, &taskHandle);
}
protected:
virtual void run() = 0;
private:
static void taskFunctionWrapper(void* parameter) {
Task* task = static_cast<Task*>(parameter);
task->run();
}
const char* taskName;
uint32_t taskStackSize;
UBaseType_t taskPriority;
TaskHandle_t taskHandle;
};
// Counter Task
class CounterTask : public Task {
public:
CounterTask() : Task("Counter Task", 2048, 3), counter(0) {}
protected:
void run() override {
while (true) {
counter++;
ESP_LOGI("COUNTER_TASK", "Counter value: %d", counter);
vTaskDelay(1000 / portTICK_PERIOD_MS); // Delay for 1 second
}
}
private:
int counter;
};
// Temperature Measurement Task
class TemperatureTask : public Task {
public:
TemperatureTask() : Task("Temperature Task", 2048, 3) {}
protected:
void run() override {
while (true) {
float temperature = getTemperature();
ESP_LOGI("TEMP_TASK", "Temperature: %.2f °C", temperature);
vTaskDelay(5000 / portTICK_PERIOD_MS); // Delay for 5 seconds
}
}
private:
float getTemperature() {
return 25.0 + (rand() % 100) / 10.0; // Simulate temperature
}
};
// Humidity Measurement Task
class HumidityTask : public Task {
public:
HumidityTask() : Task("Humidity Task", 2048, 3) {}
protected:
void run() override {
while (true) {
float humidity = getHumidity();
ESP_LOGI("HUMIDITY_TASK", "Humidity: %.2f %%", humidity);
vTaskDelay(3000 / portTICK_PERIOD_MS); // Delay for 3 seconds
}
}
private:
float getHumidity() {
return 50.0 + (rand() % 100) / 10.0; // Simulate humidity
}
};
extern "C" void app_main() {
// Create task instances and start them
CounterTask counterTask;
TemperatureTask temperatureTask;
HumidityTask humidityTask;
counterTask.start();
temperatureTask.start();
humidityTask.start();
}
說明
基類 Task:封裝了 FreeRTOS 任務創建和管理邏輯。每個特定的任務類必須實現run() 方法。
計數任務 CounterTask:每秒遞增計數器並打印當前值。
溫度任務 TemperatureTask:每5秒模擬一次溫度讀取並打印。
濕度任務 HumidityTask:每3秒模擬一次濕度讀取並打印。
編譯與燒錄
在 VSCode 視窗最下方中找到 "ESP32-IDF : Build, Flash and Monitor" ICON 執行和燒錄。
程式運行效果
運行程式後,您將在終端機中看到以下輸出:
I (324) COUNTER_TASK: Counter value: 1
I (1324) COUNTER_TASK: Counter value: 2
I (2324) COUNTER_TASK: Counter value: 3
I (3324) COUNTER_TASK: Counter value: 4
I (4324) COUNTER_TASK: Counter value: 5
I (5324) TEMP_TASK: Temperature: 25.60 °C
I (5324) HUMIDITY_TASK: Humidity: 50.30 %
I (6324) COUNTER_TASK: Counter value: 6
I (7324) TEMP_TASK: Temperature: 26.10 °C
I (7324) HUMIDITY_TASK: Humidity: 52.10 %
I (8324) COUNTER_TASK: Counter value: 7
I (9324) TEMP_TASK: Temperature: 25.80 °C
I (9324) HUMIDITY_TASK: Humidity: 53.90 %
I (10324) COUNTER_TASK: Counter value: 8
I (11324) TEMP_TASK: Temperature: 27.30 °C
I (11324) HUMIDITY_TASK: Humidity: 51.50 %
結論
使用繼承 (Inheritance) 在 ESP32 上構建任務系統,結合 FreeRTOS 和C++,提供了一種乾淨、有組織且可擴展的任務管理方式。通過定義可重用的基類,我們簡化了任務的創建和管理,讓代碼庫更加可讀、可維護和可擴展。無論你是在開發簡單的應用程序還是複雜的多任務嵌入式系統,這種方法都能讓你高效地處理 ESP32上的多個任務。
在建立了良好結構的任務系統後,你可以自信地探索更高級的功能,擴展你的 ESP32項目。祝編程愉快!