WOKWI ESP-IDF MQTT 作業一
WOKWI ESP-32 ESP-IDF程式
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // for strcasecmp
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/timers.h" // 新增: FreeRTOS Timer 庫
#include "esp_system.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "lwip/sockets.h"
#include "lwip/pbuf.h"
#include "lwip/netif.h"
#include "driver/gpio.h"
#include "mqtt_client.h"
// **********************************************
// 1. 配置參數 (修正了所有宏定義中的隱藏空白字元)
// **********************************************
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
#define MQTT_BROKER_URI "mqtt://broker.hivemq.com:1883"
#define MQTT_LED_TOPIC "esp32/wokwi/led/control"
#define LED_GPIO_PIN GPIO_NUM_2
// 定時器秒數
#define TIMER_DURATION_SEC 20
static const char *TAG = "MQTT_LED_CTRL";
// **********************************************
// 2. LED 狀態及模式管理
// **********************************************
// LED 控制模式
typedef enum {
MODE_OFF = 0,
MODE_ON,
MODE_TIMER,
MODE_FLASH
} led_mode_t;
// 全局變數
static led_mode_t g_led_mode = MODE_OFF;
static TimerHandle_t s_timer_handle = NULL; // FreeRTOS Timer 句柄
static TaskHandle_t s_flash_task_handle = NULL; // Flash 任務句柄
// **********************************************
// 3. 處理函數: LED 操作
// **********************************************
/**
* @brief 設置 LED 的 GPIO level
* @param level 1 開啟, 0 關閉
*/
static void led_set_level(int level) {
gpio_set_level(LED_GPIO_PIN, level);
}
/**
* @brief 啟動/停止 Flash 任務 (閃爍)
* @param start_flash true: 啟動, false: 停止
*/
static void start_stop_flash_task(bool start_flash);
/**
* @brief FreeRTOS 軟體定時器的回調函數 (20 秒後執行)
* @param xTimer 觸發的定時器句柄
*/
static void timer_callback(TimerHandle_t xTimer) {
ESP_LOGI(TAG, "定時器 (20 秒) 到期,自動關閉 LED。");
// 將模式設定為 OFF
g_led_mode = MODE_OFF;
led_set_level(0); // 關閉 LED
}
// **********************************************
// 4. FreeRTOS 任務: 閃爍 (FLASH)
// **********************************************
static void flash_task(void *pvParameter) {
ESP_LOGI(TAG, "FLASH 任務已啟動。");
while (g_led_mode == MODE_FLASH) {
led_set_level(1); // 開啟
vTaskDelay(pdMS_TO_TICKS(500));
led_set_level(0); // 關閉
vTaskDelay(pdMS_TO_TICKS(500));
}
ESP_LOGI(TAG, "FLASH 任務已停止。");
s_flash_task_handle = NULL; // 任務結束,清除句柄
vTaskDelete(NULL); // 刪除自身任務
}
/**
* @brief 啟動或停止閃爍任務
*/
static void start_stop_flash_task(bool start_flash) {
if (start_flash) {
// 如果任務還沒啟動,則創建任務
if (s_flash_task_handle == NULL) {
xTaskCreate(flash_task, "flash_task", 2048, NULL, 5, &s_flash_task_handle);
}
} else {
// 如果任務正在運行,則停止它 (通過改變 g_led_mode,任務會自行結束)
// 由於 g_led_mode 已經在外部被設置為非 MODE_FLASH,任務會在下一個循環週期退出
if (s_flash_task_handle != NULL) {
// 任務將自行退出,無需立即刪除
}
}
}
/**
* @brief 核心控制邏輯:根據命令字串切換 LED 模式
* @param cmd_str 接收到的命令字串 (如 "ON", "OFF", "TIMER", "FLASH")
*/
static void led_control_logic(const char *cmd_str) {
// 停止所有當前活動 (定時器或閃爍任務)
xTimerStop(s_timer_handle, 0);
if (s_flash_task_handle != NULL) {
// 讓 Flash 任務自行停止 (通過改變 g_led_mode)
}
if (strcasecmp(cmd_str, "ON") == 0 || strncmp(cmd_str, "1", 1) == 0) {
g_led_mode = MODE_ON;
led_set_level(1);
ESP_LOGI(TAG, "LED 狀態: ON (模式: ON)");
} else if (strcasecmp(cmd_str, "OFF") == 0 || strncmp(cmd_str, "0", 1) == 0) {
g_led_mode = MODE_OFF;
led_set_level(0);
ESP_LOGI(TAG, "LED 狀態: OFF (模式: OFF)");
} else if (strcasecmp(cmd_str, "TIMER") == 0) {
g_led_mode = MODE_TIMER;
led_set_level(1); // 啟動定時器時先開啟 LED
// 重設並啟動定時器 (20 秒)
xTimerChangePeriod(s_timer_handle, pdMS_TO_TICKS(TIMER_DURATION_SEC * 1000), 0);
xTimerStart(s_timer_handle, 0);
ESP_LOGI(TAG, "LED 狀態: ON (模式: TIMER , 將在 %d 秒後關閉)", TIMER_DURATION_SEC);
} else if (strcasecmp(cmd_str, "FLASH") == 0) {
g_led_mode = MODE_FLASH;
// 啟動閃爍任務
start_stop_flash_task(true);
ESP_LOGI(TAG, "LED 狀態: 閃爍中 (模式: FLASH)");
} else {
ESP_LOGW(TAG, "無法識別的控制命令: %s", cmd_str);
// 如果收到無法識別的命令,保持當前狀態
return;
}
// 處理完模式切換後,檢查是否需要停止 FLASH 任務
if (g_led_mode != MODE_FLASH) {
start_stop_flash_task(false);
}
}
// **********************************************
// 5. MQTT 事件處理
// **********************************************
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32, base, event_id);
esp_mqtt_event_handle_t event = event_data;
esp_mqtt_client_handle_t client = event->client;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT 連線成功!");
esp_mqtt_client_subscribe(client, MQTT_LED_TOPIC, 0);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT 斷線。");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "訂閱主題成功: topic=%s", MQTT_LED_TOPIC);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "收到 MQTT 訊息:");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
if (event->topic_len == strlen(MQTT_LED_TOPIC) && strncmp(event->topic, MQTT_LED_TOPIC, event->topic_len) == 0) {
if (event->data_len > 0) {
// 將收到的數據轉換為 C 字符串
char data_str[event->data_len + 1];
strncpy(data_str, event->data, event->data_len);
data_str[event->data_len] = '\0';
// ** 核心控制邏輯調用 **
led_control_logic(data_str);
}
}
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT 錯誤");
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
int transport_errno = event->error_handle->esp_transport_sock_errno;
ESP_LOGI(TAG, "Last transport errno: %d (%s)",
transport_errno,
strerror(transport_errno));
}
break;
default:
break;
}
}
// **********************************************
// 6. Wi-Fi 連接事件處理
// **********************************************
static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
if (event_base == WIFI_EVENT) {
if (event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "Wi-Fi 連接失敗,嘗試重新連線...");
esp_wifi_connect();
}
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "已獲得 IP: " IPSTR, IP2STR(&event->ip_info.ip));
}
}
static void wifi_init_sta(void) {
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASSWORD,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
},
};
if (strlen(WIFI_PASSWORD) == 0) {
wifi_config.sta.threshold.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "Wi-Fi 初始化完成,正在等待連接...");
}
// **********************************************
// 7. LED 和 GPIO 初始化
// **********************************************
static void led_init(void) {
gpio_reset_pin(LED_GPIO_PIN);
gpio_set_direction(LED_GPIO_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(LED_GPIO_PIN, 0);
ESP_LOGI(TAG, "LED (GPIO %d) 初始化完成。", LED_GPIO_PIN);
}
// **********************************************
// 8. MQTT 客戶端初始化
// **********************************************
static void mqtt_app_start(void) {
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = MQTT_BROKER_URI,
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
if (client == NULL) {
ESP_LOGE(TAG, "無法初始化 MQTT 客戶端!");
return;
}
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(client);
ESP_LOGI(TAG, "MQTT 客戶端啟動。");
}
// **********************************************
// 9. 主應用程式入口點 (Wokwi setup/loop)
// **********************************************
// 解決 LwIP 鉤子缺失錯誤
int __attribute__((weak)) lwip_hook_ip6_input(struct pbuf *p, struct netif *inp) { return 0; }
// C++ 混淆名稱修正
void _Z5setupv(void) __attribute__((weak, alias("setup")));
void setup(void) {
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_ERROR_CHECK(ret);
// 初始化 LED
led_init();
// 創建 FreeRTOS 軟體定時器 (不啟動)
s_timer_handle = xTimerCreate(
"LED_Timer",
pdMS_TO_TICKS(1000), // 初始週期設為 1000ms (將在 led_control_logic 中更新)
pdFALSE, // 自動重載: false (只運行一次)
(void *)0,
timer_callback);
if (s_timer_handle == NULL) {
ESP_LOGE(TAG, "無法創建 FreeRTOS 定時器!");
}
// 初始化 Wi-Fi 連線
wifi_init_sta();
// 啟動 MQTT 客戶端 (等待 Wi-Fi 連線)
vTaskDelay(pdMS_TO_TICKS(5000));
mqtt_app_start();
}
// C++ 混淆名稱修正
void _Z4loopv(void) __attribute__((weak, alias("loop")));
void loop(void) {
// 保持 loop 簡潔,讓 FreeRTOS 處理所有任務
vTaskDelay(pdMS_TO_TICKS(10));
}
PYthon + Tkinter 程式
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // for strcasecmp
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/timers.h" // 新增: FreeRTOS Timer 庫
#include "esp_system.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "lwip/sockets.h"
#include "lwip/pbuf.h"
#include "lwip/netif.h"
#include "driver/gpio.h"
#include "mqtt_client.h"
// **********************************************
// 1. 配置參數 (修正了所有宏定義中的隱藏空白字元)
// **********************************************
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
#define MQTT_BROKER_URI "mqtt://broker.hivemq.com:1883"
#define MQTT_LED_TOPIC "esp32/wokwi/led/control"
#define LED_GPIO_PIN GPIO_NUM_2
// 定時器秒數
#define TIMER_DURATION_SEC 20
static const char *TAG = "MQTT_LED_CTRL";
// **********************************************
// 2. LED 狀態及模式管理
// **********************************************
// LED 控制模式
typedef enum {
MODE_OFF = 0,
MODE_ON,
MODE_TIMER,
MODE_FLASH
} led_mode_t;
// 全局變數
static led_mode_t g_led_mode = MODE_OFF;
static TimerHandle_t s_timer_handle = NULL; // FreeRTOS Timer 句柄
static TaskHandle_t s_flash_task_handle = NULL; // Flash 任務句柄
// **********************************************
// 3. 處理函數: LED 操作
// **********************************************
/**
* @brief 設置 LED 的 GPIO level
* @param level 1 開啟, 0 關閉
*/
static void led_set_level(int level) {
gpio_set_level(LED_GPIO_PIN, level);
}
/**
* @brief 啟動/停止 Flash 任務 (閃爍)
* @param start_flash true: 啟動, false: 停止
*/
static void start_stop_flash_task(bool start_flash);
/**
* @brief FreeRTOS 軟體定時器的回調函數 (20 秒後執行)
* @param xTimer 觸發的定時器句柄
*/
static void timer_callback(TimerHandle_t xTimer) {
ESP_LOGI(TAG, "定時器 (20 秒) 到期,自動關閉 LED。");
// 將模式設定為 OFF
g_led_mode = MODE_OFF;
led_set_level(0); // 關閉 LED
}
// **********************************************
// 4. FreeRTOS 任務: 閃爍 (FLASH)
// **********************************************
static void flash_task(void *pvParameter) {
ESP_LOGI(TAG, "FLASH 任務已啟動。");
while (g_led_mode == MODE_FLASH) {
led_set_level(1); // 開啟
vTaskDelay(pdMS_TO_TICKS(500));
led_set_level(0); // 關閉
vTaskDelay(pdMS_TO_TICKS(500));
}
ESP_LOGI(TAG, "FLASH 任務已停止。");
s_flash_task_handle = NULL; // 任務結束,清除句柄
vTaskDelete(NULL); // 刪除自身任務
}
/**
* @brief 啟動或停止閃爍任務
*/
static void start_stop_flash_task(bool start_flash) {
if (start_flash) {
// 如果任務還沒啟動,則創建任務
if (s_flash_task_handle == NULL) {
xTaskCreate(flash_task, "flash_task", 2048, NULL, 5, &s_flash_task_handle);
}
} else {
// 如果任務正在運行,則停止它 (通過改變 g_led_mode,任務會自行結束)
// 由於 g_led_mode 已經在外部被設置為非 MODE_FLASH,任務會在下一個循環週期退出
if (s_flash_task_handle != NULL) {
// 任務將自行退出,無需立即刪除
}
}
}
/**
* @brief 核心控制邏輯:根據命令字串切換 LED 模式
* @param cmd_str 接收到的命令字串 (如 "ON", "OFF", "TIMER", "FLASH")
*/
static void led_control_logic(const char *cmd_str) {
// 停止所有當前活動 (定時器或閃爍任務)
xTimerStop(s_timer_handle, 0);
if (s_flash_task_handle != NULL) {
// 讓 Flash 任務自行停止 (通過改變 g_led_mode)
}
if (strcasecmp(cmd_str, "ON") == 0 || strncmp(cmd_str, "1", 1) == 0) {
g_led_mode = MODE_ON;
led_set_level(1);
ESP_LOGI(TAG, "LED 狀態: ON (模式: ON)");
} else if (strcasecmp(cmd_str, "OFF") == 0 || strncmp(cmd_str, "0", 1) == 0) {
g_led_mode = MODE_OFF;
led_set_level(0);
ESP_LOGI(TAG, "LED 狀態: OFF (模式: OFF)");
} else if (strcasecmp(cmd_str, "TIMER") == 0) {
g_led_mode = MODE_TIMER;
led_set_level(1); // 啟動定時器時先開啟 LED
// 重設並啟動定時器 (20 秒)
xTimerChangePeriod(s_timer_handle, pdMS_TO_TICKS(TIMER_DURATION_SEC * 1000), 0);
xTimerStart(s_timer_handle, 0);
ESP_LOGI(TAG, "LED 狀態: ON (模式: TIMER , 將在 %d 秒後關閉)", TIMER_DURATION_SEC);
} else if (strcasecmp(cmd_str, "FLASH") == 0) {
g_led_mode = MODE_FLASH;
// 啟動閃爍任務
start_stop_flash_task(true);
ESP_LOGI(TAG, "LED 狀態: 閃爍中 (模式: FLASH)");
} else {
ESP_LOGW(TAG, "無法識別的控制命令: %s", cmd_str);
// 如果收到無法識別的命令,保持當前狀態
return;
}
// 處理完模式切換後,檢查是否需要停止 FLASH 任務
if (g_led_mode != MODE_FLASH) {
start_stop_flash_task(false);
}
}
// **********************************************
// 5. MQTT 事件處理
// **********************************************
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32, base, event_id);
esp_mqtt_event_handle_t event = event_data;
esp_mqtt_client_handle_t client = event->client;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT 連線成功!");
esp_mqtt_client_subscribe(client, MQTT_LED_TOPIC, 0);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT 斷線。");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "訂閱主題成功: topic=%s", MQTT_LED_TOPIC);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "收到 MQTT 訊息:");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
if (event->topic_len == strlen(MQTT_LED_TOPIC) && strncmp(event->topic, MQTT_LED_TOPIC, event->topic_len) == 0) {
if (event->data_len > 0) {
// 將收到的數據轉換為 C 字符串
char data_str[event->data_len + 1];
strncpy(data_str, event->data, event->data_len);
data_str[event->data_len] = '\0';
// ** 核心控制邏輯調用 **
led_control_logic(data_str);
}
}
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT 錯誤");
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
int transport_errno = event->error_handle->esp_transport_sock_errno;
ESP_LOGI(TAG, "Last transport errno: %d (%s)",
transport_errno,
strerror(transport_errno));
}
break;
default:
break;
}
}
// **********************************************
// 6. Wi-Fi 連接事件處理
// **********************************************
static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
if (event_base == WIFI_EVENT) {
if (event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "Wi-Fi 連接失敗,嘗試重新連線...");
esp_wifi_connect();
}
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "已獲得 IP: " IPSTR, IP2STR(&event->ip_info.ip));
}
}
static void wifi_init_sta(void) {
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASSWORD,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
},
};
if (strlen(WIFI_PASSWORD) == 0) {
wifi_config.sta.threshold.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "Wi-Fi 初始化完成,正在等待連接...");
}
// **********************************************
// 7. LED 和 GPIO 初始化
// **********************************************
static void led_init(void) {
gpio_reset_pin(LED_GPIO_PIN);
gpio_set_direction(LED_GPIO_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(LED_GPIO_PIN, 0);
ESP_LOGI(TAG, "LED (GPIO %d) 初始化完成。", LED_GPIO_PIN);
}
// **********************************************
// 8. MQTT 客戶端初始化
// **********************************************
static void mqtt_app_start(void) {
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = MQTT_BROKER_URI,
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
if (client == NULL) {
ESP_LOGE(TAG, "無法初始化 MQTT 客戶端!");
return;
}
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(client);
ESP_LOGI(TAG, "MQTT 客戶端啟動。");
}
// **********************************************
// 9. 主應用程式入口點 (Wokwi setup/loop)
// **********************************************
// 解決 LwIP 鉤子缺失錯誤
int __attribute__((weak)) lwip_hook_ip6_input(struct pbuf *p, struct netif *inp) { return 0; }
// C++ 混淆名稱修正
void _Z5setupv(void) __attribute__((weak, alias("setup")));
void setup(void) {
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_ERROR_CHECK(ret);
// 初始化 LED
led_init();
// 創建 FreeRTOS 軟體定時器 (不啟動)
s_timer_handle = xTimerCreate(
"LED_Timer",
pdMS_TO_TICKS(1000), // 初始週期設為 1000ms (將在 led_control_logic 中更新)
pdFALSE, // 自動重載: false (只運行一次)
(void *)0,
timer_callback);
if (s_timer_handle == NULL) {
ESP_LOGE(TAG, "無法創建 FreeRTOS 定時器!");
}
// 初始化 Wi-Fi 連線
wifi_init_sta();
// 啟動 MQTT 客戶端 (等待 Wi-Fi 連線)
vTaskDelay(pdMS_TO_TICKS(5000));
mqtt_app_start();
}
// C++ 混淆名稱修正
void _Z4loopv(void) __attribute__((weak, alias("loop")));
void loop(void) {
// 保持 loop 簡潔,讓 FreeRTOS 處理所有任務
vTaskDelay(pdMS_TO_TICKS(10));
}





沒有留言:
張貼留言