From 8c33fe7411428219dcf683f75b464134e310ab8e Mon Sep 17 00:00:00 2001 From: Wang Beihong Date: Sat, 28 Mar 2026 16:12:37 +0800 Subject: [PATCH] Add Zone.Identifier files for wifi-connect and partitions - Created new Zone.Identifier file in components/wifi-connect with ZoneId=3. - Created new Zone.Identifier file in partitions.csv with ZoneId=3. --- .../lvgl_st7735s_use/include/ui_display.h | 4 +- components/lvgl_st7735s_use/ui_display.c | 94 +- .../protocol_examples_common/CMakeLists.txt | 52 - .../Kconfig.projbuild | 504 ----- components/protocol_examples_common/README.md | 66 - .../addr_from_stdin.c | 69 - components/protocol_examples_common/connect.c | 148 -- .../protocol_examples_common/console_cmd.c | 87 - .../protocol_examples_common/eth_connect.c | 245 --- .../include/addr_from_stdin.h | 44 - .../include/addr_from_stdin.h:Zone.Identifier | Bin 25 -> 0 bytes .../include/example_common_private.h | 58 - .../example_common_private.h:Zone.Identifier | Bin 25 -> 0 bytes .../include/protocol_examples_common.h | 154 -- ...protocol_examples_common.h:Zone.Identifier | Bin 25 -> 0 bytes .../include/protocol_examples_thread_config.h | 115 -- ...l_examples_thread_config.h:Zone.Identifier | Bin 25 -> 0 bytes .../include/protocol_examples_utils.h | 49 - .../protocol_examples_utils.h:Zone.Identifier | Bin 25 -> 0 bytes .../protocol_examples_common/ppp_connect.c | 260 --- .../ppp_connect.c:Zone.Identifier | Bin 25 -> 0 bytes .../protocol_examples_utils.c | 388 ---- .../protocol_examples_utils.c:Zone.Identifier | Bin 25 -> 0 bytes .../protocol_examples_common/stdin_out.c | 32 - .../stdin_out.c:Zone.Identifier | Bin 25 -> 0 bytes .../protocol_examples_common/thread_connect.c | 130 -- .../thread_connect.c:Zone.Identifier | Bin 25 -> 0 bytes .../protocol_examples_common/wifi_connect.c | 247 --- .../wifi_connect.c:Zone.Identifier | Bin 25 -> 0 bytes components/wifi-connect/CMakeLists.txt | 5 + .../CMakeLists.txt:Zone.Identifier | Bin components/wifi-connect/Kconfig.projbuild | 88 + .../Kconfig.projbuild:Zone.Identifier | Bin components/wifi-connect/README.md | 165 ++ .../README.md:Zone.Identifier | Bin components/wifi-connect/USER_GUIDE.md | 82 + .../USER_GUIDE.md:Zone.Identifier} | Bin .../wifi-connect/include/wifi-connect.h | 34 + .../include/wifi-connect.h:Zone.Identifier} | Bin components/wifi-connect/wifi-connect.c | 1618 +++++++++++++++++ .../wifi-connect.c:Zone.Identifier} | Bin dependencies.lock | 24 +- main/CMakeLists.txt | 2 +- main/idf_component.yml | 6 +- main/main.c | 442 +---- partitions.csv | 10 +- ...entifier => partitions.csv:Zone.Identifier | Bin 47 files changed, 2090 insertions(+), 3132 deletions(-) delete mode 100644 components/protocol_examples_common/CMakeLists.txt delete mode 100644 components/protocol_examples_common/Kconfig.projbuild delete mode 100644 components/protocol_examples_common/README.md delete mode 100644 components/protocol_examples_common/addr_from_stdin.c delete mode 100644 components/protocol_examples_common/connect.c delete mode 100644 components/protocol_examples_common/console_cmd.c delete mode 100644 components/protocol_examples_common/eth_connect.c delete mode 100644 components/protocol_examples_common/include/addr_from_stdin.h delete mode 100644 components/protocol_examples_common/include/addr_from_stdin.h:Zone.Identifier delete mode 100644 components/protocol_examples_common/include/example_common_private.h delete mode 100644 components/protocol_examples_common/include/example_common_private.h:Zone.Identifier delete mode 100644 components/protocol_examples_common/include/protocol_examples_common.h delete mode 100644 components/protocol_examples_common/include/protocol_examples_common.h:Zone.Identifier delete mode 100644 components/protocol_examples_common/include/protocol_examples_thread_config.h delete mode 100644 components/protocol_examples_common/include/protocol_examples_thread_config.h:Zone.Identifier delete mode 100644 components/protocol_examples_common/include/protocol_examples_utils.h delete mode 100644 components/protocol_examples_common/include/protocol_examples_utils.h:Zone.Identifier delete mode 100644 components/protocol_examples_common/ppp_connect.c delete mode 100644 components/protocol_examples_common/ppp_connect.c:Zone.Identifier delete mode 100644 components/protocol_examples_common/protocol_examples_utils.c delete mode 100644 components/protocol_examples_common/protocol_examples_utils.c:Zone.Identifier delete mode 100644 components/protocol_examples_common/stdin_out.c delete mode 100644 components/protocol_examples_common/stdin_out.c:Zone.Identifier delete mode 100644 components/protocol_examples_common/thread_connect.c delete mode 100644 components/protocol_examples_common/thread_connect.c:Zone.Identifier delete mode 100644 components/protocol_examples_common/wifi_connect.c delete mode 100644 components/protocol_examples_common/wifi_connect.c:Zone.Identifier create mode 100644 components/wifi-connect/CMakeLists.txt rename components/{protocol_examples_common => wifi-connect}/CMakeLists.txt:Zone.Identifier (100%) create mode 100644 components/wifi-connect/Kconfig.projbuild rename components/{protocol_examples_common => wifi-connect}/Kconfig.projbuild:Zone.Identifier (100%) create mode 100644 components/wifi-connect/README.md rename components/{protocol_examples_common => wifi-connect}/README.md:Zone.Identifier (100%) create mode 100644 components/wifi-connect/USER_GUIDE.md rename components/{protocol_examples_common/addr_from_stdin.c:Zone.Identifier => wifi-connect/USER_GUIDE.md:Zone.Identifier} (100%) create mode 100644 components/wifi-connect/include/wifi-connect.h rename components/{protocol_examples_common/connect.c:Zone.Identifier => wifi-connect/include/wifi-connect.h:Zone.Identifier} (100%) create mode 100644 components/wifi-connect/wifi-connect.c rename components/{protocol_examples_common/console_cmd.c:Zone.Identifier => wifi-connect/wifi-connect.c:Zone.Identifier} (100%) rename components/protocol_examples_common/eth_connect.c:Zone.Identifier => partitions.csv:Zone.Identifier (100%) diff --git a/components/lvgl_st7735s_use/include/ui_display.h b/components/lvgl_st7735s_use/include/ui_display.h index 422b6b3..e12f7e5 100644 --- a/components/lvgl_st7735s_use/include/ui_display.h +++ b/components/lvgl_st7735s_use/include/ui_display.h @@ -20,10 +20,8 @@ void ui_display_init(void); * @param temperature 温度值(°C),-1.0表示无效 * @param humidity 湿度值(%),-1.0表示无效 * @param lux 光照强度(lx),-1.0表示无效 - * @param ppm 空气中有害气体浓度(ppm) - * @param quality_level 空气质量等级描述 */ -void ui_update_sensor_data(float temperature, float humidity, float lux, float ppm, const char* quality_level); +void ui_update_sensor_data(float temperature, float humidity, float lux); /* Time page APIs */ void ui_show_time_page(void); diff --git a/components/lvgl_st7735s_use/ui_display.c b/components/lvgl_st7735s_use/ui_display.c index 8c794f4..918a19f 100644 --- a/components/lvgl_st7735s_use/ui_display.c +++ b/components/lvgl_st7735s_use/ui_display.c @@ -10,7 +10,6 @@ static const char *TAG = "ui_display"; static lv_obj_t *temp_label = NULL; static lv_obj_t *humid_label = NULL; static lv_obj_t *lux_label = NULL; -static lv_obj_t *air_quality_label = NULL; // 新增空气质量标签 /* Time page objects */ static lv_obj_t *time_container = NULL; @@ -28,54 +27,42 @@ void ui_display_init(void) { // 获取当前活动屏幕对象 lv_obj_t *scr = lv_scr_act(); - + /* 任务锁定 */ lvgl_port_lock(0); - + /* 设置屏幕背景为白色 */ lv_obj_set_style_bg_color(scr, lv_color_white(), 0); lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0); - + /* 创建标题标签 */ lv_obj_t *title_label = lv_label_create(scr); - lv_label_set_text(title_label, "IoT Home Monitor"); - lv_obj_set_style_text_color(title_label, lv_palette_main(LV_PALETTE_BLUE), 0); + lv_label_set_text(title_label, "IOT Home"); + lv_obj_set_style_text_color(title_label, lv_color_black(), 0); lv_obj_set_style_text_font(title_label, &lv_font_unscii_8, 0); - lv_obj_align(title_label, LV_ALIGN_TOP_MID, 0, 2); // 调整标题位置 - - /* 创建温度标签 */ + lv_obj_align(title_label, LV_ALIGN_TOP_MID, 0, 6); + + /* 创建传感器数据显示标签 */ temp_label = lv_label_create(scr); lv_label_set_text(temp_label, "Temp: --.- C"); lv_obj_set_style_text_color(temp_label, lv_color_black(), 0); lv_obj_set_style_text_font(temp_label, &lv_font_unscii_8, 0); - lv_obj_align(temp_label, LV_ALIGN_TOP_LEFT, 3, 20); // 调整位置 - - /* 创建湿度标签 */ + lv_obj_align(temp_label, LV_ALIGN_TOP_LEFT, 3, 30); + humid_label = lv_label_create(scr); lv_label_set_text(humid_label, "Humidity: --.- %"); lv_obj_set_style_text_color(humid_label, lv_color_black(), 0); lv_obj_set_style_text_font(humid_label, &lv_font_unscii_8, 0); - lv_obj_align(humid_label, LV_ALIGN_TOP_LEFT, 3, 35); // 调整位置 - + lv_obj_align(humid_label, LV_ALIGN_TOP_LEFT, 3, 45); + /* 创建光照标签 */ lux_label = lv_label_create(scr); lv_label_set_text(lux_label, "Light: --.- lux"); lv_obj_set_style_text_color(lux_label, lv_color_black(), 0); lv_obj_set_style_text_font(lux_label, &lv_font_unscii_8, 0); - lv_obj_align(lux_label, LV_ALIGN_TOP_LEFT, 3, 50); // 调整位置 - - /* 创建空气质量标签 */ - air_quality_label = lv_label_create(scr); - lv_label_set_text(air_quality_label, "IAQ : --.- Index"); - lv_obj_set_style_text_color(air_quality_label, lv_color_black(), 0); - lv_obj_set_style_text_font(air_quality_label, &lv_font_unscii_8, 0); - lv_obj_align(air_quality_label, LV_ALIGN_TOP_LEFT, 3, 65); // 调整位置 - - /* 任务解锁 */ - lvgl_port_unlock(); + lv_obj_align(lux_label, LV_ALIGN_TOP_LEFT, 3, 60); - // 创建时间页面(初始隐藏) - lvgl_port_lock(0); + /* 创建时间页面(初始隐藏) */ time_container = lv_obj_create(lv_scr_act()); lv_obj_set_size(time_container, lv_pct(100), lv_pct(100)); lv_obj_set_style_bg_color(time_container, lv_color_white(), 0); @@ -94,12 +81,12 @@ void ui_display_init(void) lv_obj_align(time_label, LV_ALIGN_CENTER, 0, 12); // 默认显示时间页面,隐藏传感器页面 - lv_obj_clear_flag(time_container, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(time_container, LV_OBJ_FLAG_HIDDEN); if (temp_label) lv_obj_add_flag(temp_label, LV_OBJ_FLAG_HIDDEN); if (humid_label) lv_obj_add_flag(humid_label, LV_OBJ_FLAG_HIDDEN); if (lux_label) lv_obj_add_flag(lux_label, LV_OBJ_FLAG_HIDDEN); - if (air_quality_label) lv_obj_add_flag(air_quality_label, LV_OBJ_FLAG_HIDDEN); time_page_visible = true; + lvgl_port_unlock(); } @@ -111,17 +98,15 @@ void ui_display_init(void) * @param temperature 温度值(°C),-1.0表示无效 * @param humidity 湿度值(%),-1.0表示无效 * @param lux 光照强度(lx),-1.0表示无效 - * @param ppm 空气中有害气体浓度(ppm) - * @param quality_level 空气质量等级描述 */ -void ui_update_sensor_data(float temperature, float humidity, float lux, float ppm, const char* quality_level) +void ui_update_sensor_data(float temperature, float humidity, float lux) { - if (temp_label != NULL && humid_label != NULL && lux_label != NULL && air_quality_label != NULL) + if (temp_label != NULL && humid_label != NULL && lux_label != NULL) { /* 任务锁定 */ lvgl_port_lock(0); - - // 更新温度标签 - 缩短文本以节省空间 + + // 更新温度标签 if (temperature >= -0.5) // -1.0表示无效 { char temp_str[32]; @@ -132,8 +117,8 @@ void ui_update_sensor_data(float temperature, float humidity, float lux, float p { lv_label_set_text(temp_label, "Temp: Invalid"); } - - // 更新湿度标签 - 缩短文本以节省空间 + + // 更新湿度标签 if (humidity >= -0.5) // -1.0表示无效 { char humid_str[32]; @@ -145,8 +130,8 @@ void ui_update_sensor_data(float temperature, float humidity, float lux, float p lv_label_set_text(humid_label, "Humidity: Invalid"); } - // 更新光照标签 - 缩短文本以节省空间 - if (lux >= -0.5) // -1.0表示无效 + // 更新光照标签 + if (lux >= -0.5) { char lux_str[32]; snprintf(lux_str, sizeof(lux_str), "Light: %.2f lx", lux); @@ -156,34 +141,7 @@ void ui_update_sensor_data(float temperature, float humidity, float lux, float p { lv_label_set_text(lux_label, "Light: Invalid"); } - - // 更新空气质量标签 - 缩短文本以节省空间 - if (ppm >= 0) // 空气质量值有效 - { - char ppm_str[32]; - snprintf(ppm_str, sizeof(ppm_str), "IAQ : %.2f Index", ppm); - - // 根据空气质量等级更改颜色 - lv_color_t color = lv_color_black(); // 默认黑色 - if (ppm <= 20.0f) { - color = lv_color_make(0, 128, 0); // 绿色 - 空气质量优秀 - } else if (ppm <= 100.0f) { - color = lv_color_make(0, 0, 0); // 黑色 - 空气质量良好 - } else if (ppm <= 300.0f) { - color = lv_color_make(255, 165, 0); // 橙色 - 轻度污染 - } else { - color = lv_color_make(255, 0, 0); // 红色 - 中重度污染 - } - - lv_label_set_text(air_quality_label, ppm_str); - lv_obj_set_style_text_color(air_quality_label, color, 0); - } - else - { - lv_label_set_text(air_quality_label, "IAQ : Invalid"); - lv_obj_set_style_text_color(air_quality_label, lv_color_black(), 0); - } - + /* 任务解锁 */ lvgl_port_unlock(); } @@ -200,7 +158,6 @@ void ui_show_time_page(void) if (temp_label) lv_obj_add_flag(temp_label, LV_OBJ_FLAG_HIDDEN); if (humid_label) lv_obj_add_flag(humid_label, LV_OBJ_FLAG_HIDDEN); if (lux_label) lv_obj_add_flag(lux_label, LV_OBJ_FLAG_HIDDEN); - if (air_quality_label) lv_obj_add_flag(air_quality_label, LV_OBJ_FLAG_HIDDEN); time_page_visible = true; lvgl_port_unlock(); } @@ -216,7 +173,6 @@ void ui_show_sensor_page(void) if (temp_label) lv_obj_clear_flag(temp_label, LV_OBJ_FLAG_HIDDEN); if (humid_label) lv_obj_clear_flag(humid_label, LV_OBJ_FLAG_HIDDEN); if (lux_label) lv_obj_clear_flag(lux_label, LV_OBJ_FLAG_HIDDEN); - if (air_quality_label) lv_obj_clear_flag(air_quality_label, LV_OBJ_FLAG_HIDDEN); time_page_visible = false; lvgl_port_unlock(); } diff --git a/components/protocol_examples_common/CMakeLists.txt b/components/protocol_examples_common/CMakeLists.txt deleted file mode 100644 index 8d0501a..0000000 --- a/components/protocol_examples_common/CMakeLists.txt +++ /dev/null @@ -1,52 +0,0 @@ -idf_build_get_property(target IDF_TARGET) - -if(${target} STREQUAL "linux") - # Header only library for linux - - idf_component_register(INCLUDE_DIRS include - SRCS protocol_examples_utils.c) - return() -endif() - -set(srcs "stdin_out.c" - "addr_from_stdin.c" - "connect.c" - "wifi_connect.c" - "protocol_examples_utils.c") - -if(CONFIG_EXAMPLE_PROVIDE_WIFI_CONSOLE_CMD) - list(APPEND srcs "console_cmd.c") -endif() - -if(CONFIG_EXAMPLE_CONNECT_ETHERNET) - list(APPEND srcs "eth_connect.c") -endif() - -if(CONFIG_EXAMPLE_CONNECT_THREAD) - list(APPEND srcs "thread_connect.c") -endif() - -if(CONFIG_EXAMPLE_CONNECT_PPP) - list(APPEND srcs "ppp_connect.c") -endif() - - -idf_component_register(SRCS "${srcs}" - INCLUDE_DIRS "include" - PRIV_REQUIRES esp_netif esp_driver_gpio esp_driver_uart esp_wifi vfs console esp_eth openthread) - -if(CONFIG_EXAMPLE_PROVIDE_WIFI_CONSOLE_CMD) - idf_component_optional_requires(PRIVATE console) -endif() - -if(CONFIG_EXAMPLE_CONNECT_ETHERNET) - idf_component_optional_requires(PUBLIC esp_eth) -endif() - -if(CONFIG_EXAMPLE_CONNECT_THREAD) - idf_component_optional_requires(PRIVATE openthread) -endif() - -if(CONFIG_EXAMPLE_CONNECT_PPP) - idf_component_optional_requires(PRIVATE esp_tinyusb espressif__esp_tinyusb) -endif() diff --git a/components/protocol_examples_common/Kconfig.projbuild b/components/protocol_examples_common/Kconfig.projbuild deleted file mode 100644 index f9ccbfb..0000000 --- a/components/protocol_examples_common/Kconfig.projbuild +++ /dev/null @@ -1,504 +0,0 @@ -menu "Example Connection Configuration" - - orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" - - config EXAMPLE_CONNECT_WIFI - bool "connect using WiFi interface" - depends on !IDF_TARGET_LINUX && (SOC_WIFI_SUPPORTED || ESP_WIFI_REMOTE_ENABLED || ESP_HOST_WIFI_ENABLED) - default y if SOC_WIFI_SUPPORTED - help - Protocol examples can use Wi-Fi, Ethernet and/or Thread to connect to the network. - Choose this option to connect with WiFi - - if EXAMPLE_CONNECT_WIFI - config EXAMPLE_WIFI_SSID_PWD_FROM_STDIN - bool "Get ssid and password from stdin" - default n - help - Give the WiFi SSID and password from stdin. - - config EXAMPLE_PROVIDE_WIFI_CONSOLE_CMD - depends on !EXAMPLE_WIFI_SSID_PWD_FROM_STDIN - bool "Provide wifi connect commands" - default y - help - Provide wifi connect commands for esp_console. - Please use `example_register_wifi_connect_commands` to register them. - - config EXAMPLE_WIFI_SSID - depends on !EXAMPLE_WIFI_SSID_PWD_FROM_STDIN - string "WiFi SSID" - default "myssid" - help - SSID (network name) for the example to connect to. - - config EXAMPLE_WIFI_PASSWORD - depends on !EXAMPLE_WIFI_SSID_PWD_FROM_STDIN - string "WiFi Password" - default "mypassword" - help - WiFi password (WPA or WPA2) for the example to use. - Can be left blank if the network has no security set. - - config EXAMPLE_WIFI_CONN_MAX_RETRY - int "Maximum retry" - default 6 - help - Set the Maximum retry to avoid station reconnecting to the AP unlimited, - in case the AP is really inexistent. - - choice EXAMPLE_WIFI_SCAN_METHOD - prompt "WiFi Scan Method" - default EXAMPLE_WIFI_SCAN_METHOD_ALL_CHANNEL - help - WiFi scan method: - - If "Fast" is selected, scan will end after find SSID match AP. - - If "All Channel" is selected, scan will end after scan all the channel. - - config EXAMPLE_WIFI_SCAN_METHOD_FAST - bool "Fast" - config EXAMPLE_WIFI_SCAN_METHOD_ALL_CHANNEL - bool "All Channel" - endchoice - - menu "WiFi Scan threshold" - config EXAMPLE_WIFI_SCAN_RSSI_THRESHOLD - int "WiFi minimum rssi" - range -127 0 - - default -127 - help - The minimum rssi to accept in the scan mode. - - choice EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD - prompt "WiFi Scan auth mode threshold" - default EXAMPLE_WIFI_AUTH_OPEN - help - The weakest authmode to accept in the scan mode. - - config EXAMPLE_WIFI_AUTH_OPEN - bool "OPEN" - config EXAMPLE_WIFI_AUTH_WEP - bool "WEP" - config EXAMPLE_WIFI_AUTH_WPA_PSK - bool "WPA PSK" - config EXAMPLE_WIFI_AUTH_WPA2_PSK - bool "WPA2 PSK" - config EXAMPLE_WIFI_AUTH_WPA_WPA2_PSK - bool "WPA WPA2 PSK" - config EXAMPLE_WIFI_AUTH_WPA2_ENTERPRISE - bool "WPA2 ENTERPRISE" - config EXAMPLE_WIFI_AUTH_WPA3_PSK - bool "WPA3 PSK" - config EXAMPLE_WIFI_AUTH_WPA2_WPA3_PSK - bool "WPA2 WPA3 PSK" - config EXAMPLE_WIFI_AUTH_WAPI_PSK - bool "WAPI PSK" - endchoice - endmenu - - choice EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD - prompt "WiFi Connect AP Sort Method" - default EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL - help - WiFi connect AP sort method: - - If "Signal" is selected, Sort matched APs in scan list by RSSI. - - If "Security" is selected, Sort matched APs in scan list by security mode. - - config EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL - bool "Signal" - config EXAMPLE_WIFI_CONNECT_AP_BY_SECURITY - bool "Security" - endchoice - endif - - config EXAMPLE_CONNECT_ETHERNET - bool "connect using Ethernet interface" - depends on !IDF_TARGET_LINUX - default y if !EXAMPLE_CONNECT_WIFI && !EXAMPLE_CONNECT_THREAD - help - Protocol examples can use Wi-Fi, Ethernet and/or Thread to connect to the network. - Choose this option to connect with Ethernet - - if EXAMPLE_CONNECT_ETHERNET - config EXAMPLE_ETHERNET_EMAC_TASK_STACK_SIZE - int "emac_rx task stack size" - default 2048 - help - This set stack size for emac_rx task - - config EXAMPLE_USE_SPI_ETHERNET - bool - - choice EXAMPLE_ETHERNET_TYPE - prompt "Ethernet Type" - default EXAMPLE_USE_INTERNAL_ETHERNET if SOC_EMAC_SUPPORTED - default EXAMPLE_USE_W5500 - help - Select which kind of Ethernet will be used in the example. - - config EXAMPLE_USE_INTERNAL_ETHERNET - depends on SOC_EMAC_SUPPORTED - select ETH_USE_ESP32_EMAC - bool "Internal EMAC" - help - Select internal Ethernet MAC controller. - - config EXAMPLE_USE_DM9051 - bool "DM9051 Module" - select EXAMPLE_USE_SPI_ETHERNET - select ETH_USE_SPI_ETHERNET - select ETH_SPI_ETHERNET_DM9051 - help - Select external SPI-Ethernet module. - - config EXAMPLE_USE_W5500 - bool "W5500 Module" - select EXAMPLE_USE_SPI_ETHERNET - select ETH_USE_SPI_ETHERNET - select ETH_SPI_ETHERNET_W5500 - help - Select external SPI-Ethernet module (W5500). - - config EXAMPLE_USE_OPENETH - bool "OpenCores Ethernet MAC (EXPERIMENTAL)" - select ETH_USE_OPENETH - help - When this option is enabled, the example is built with support for - OpenCores Ethernet MAC, which allows testing the example in QEMU. - Note that this option is used for internal testing purposes, and - not officially supported. Examples built with this option enabled - will not run on a real ESP32 chip. - - endchoice # EXAMPLE_ETHERNET_TYPE - - if EXAMPLE_USE_INTERNAL_ETHERNET - choice EXAMPLE_ETH_PHY_MODEL - prompt "Ethernet PHY Device" - default EXAMPLE_ETH_PHY_IP101 - help - Select the Ethernet PHY device to use in the example. - - config EXAMPLE_ETH_PHY_GENERIC - bool "Generic 802.3 PHY" - help - Any Ethernet PHY chip compliant with IEEE 802.3 can be used. However, while - basic functionality should always work, some specific features might be limited, - even if the PHY meets IEEE 802.3 standard. A typical example is loopback - functionality, where certain PHYs may require setting a specific speed mode to - operate correctly. - - config EXAMPLE_ETH_PHY_IP101 - bool "IP101" - help - IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. - Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. - - config EXAMPLE_ETH_PHY_RTL8201 - bool "RTL8201/SR8201" - help - RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX. - Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it. - - config EXAMPLE_ETH_PHY_LAN87XX - bool "LAN87xx" - help - Below chips are supported: - LAN8710A is a small footprint MII/RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and - flexPWR® Technology. - LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support. - LAN8740A/LAN8741A is a small footprint MII/RMII 10/100 Energy Efficient Ethernet Transceiver - with HP Auto-MDIX and flexPWR® Technology. - LAN8742A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and - flexPWR® Technology. - Goto https://www.microchip.com for more information about them. - - config EXAMPLE_ETH_PHY_DP83848 - bool "DP83848" - help - DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver. - Goto http://www.ti.com/product/DP83848J for more information about it. - - config EXAMPLE_ETH_PHY_KSZ80XX - bool "KSZ80xx" - help - With the KSZ80xx series, Microchip offers single-chip 10BASE-T/100BASE-TX - Ethernet Physical Layer Transceivers (PHY). - The following chips are supported: KSZ8001, KSZ8021, KSZ8031, KSZ8041, - KSZ8051, KSZ8061, KSZ8081, KSZ8091 - Goto https://www.microchip.com for more information about them. - endchoice - - config EXAMPLE_ETH_MDC_GPIO - int "SMI MDC GPIO number" - range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX - default 23 if IDF_TARGET_ESP32 - default 31 if IDF_TARGET_ESP32P4 - help - Set the GPIO number used by SMI MDC. - - config EXAMPLE_ETH_MDIO_GPIO - int "SMI MDIO GPIO number" - range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX - default 18 if IDF_TARGET_ESP32 - default 52 if IDF_TARGET_ESP32P4 - help - Set the GPIO number used by SMI MDIO. - endif - - if EXAMPLE_USE_SPI_ETHERNET - config EXAMPLE_ETH_SPI_HOST - int "SPI Host Number" - range 0 2 - default 1 - help - Set the SPI host used to communicate with the SPI Ethernet Controller. - - config EXAMPLE_ETH_SPI_SCLK_GPIO - int "SPI SCLK GPIO number" - range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX - default 14 - help - Set the GPIO number used by SPI SCLK. - - config EXAMPLE_ETH_SPI_MOSI_GPIO - int "SPI MOSI GPIO number" - range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX - default 13 - help - Set the GPIO number used by SPI MOSI. - - config EXAMPLE_ETH_SPI_MISO_GPIO - int "SPI MISO GPIO number" - range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX - default 12 - help - Set the GPIO number used by SPI MISO. - - config EXAMPLE_ETH_SPI_CS_GPIO - int "SPI CS GPIO number" - range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX - default 15 - help - Set the GPIO number used by SPI CS. - - config EXAMPLE_ETH_SPI_CLOCK_MHZ - int "SPI clock speed (MHz)" - range 5 80 - default 36 - help - Set the clock speed (MHz) of SPI interface. - - config EXAMPLE_ETH_SPI_INT_GPIO - int "Interrupt GPIO number" - range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX - default 4 - help - Set the GPIO number used by the SPI Ethernet module interrupt line. - endif # EXAMPLE_USE_SPI_ETHERNET - - config EXAMPLE_ETH_PHY_RST_GPIO - int "PHY Reset GPIO number" - range -1 ENV_GPIO_OUT_RANGE_MAX - default 51 if IDF_TARGET_ESP32P4 - default 5 - help - Set the GPIO number used to reset PHY chip. - Set to -1 to disable PHY chip hardware reset. - - config EXAMPLE_ETH_PHY_ADDR - int "PHY Address" - range 0 31 if EXAMPLE_USE_INTERNAL_ETHERNET - default 1 - help - Set PHY address according your board schematic. - endif # EXAMPLE_CONNECT_ETHERNET - - config EXAMPLE_CONNECT_PPP - bool "connect using Point to Point interface" - select LWIP_PPP_SUPPORT - help - Protocol examples can use PPP connection over serial line. - Choose this option to connect to the ppp server running - on your laptop over a serial line (either UART or USB ACM) - - if EXAMPLE_CONNECT_PPP - choice EXAMPLE_CONNECT_PPP_DEVICE - prompt "Choose PPP device" - default EXAMPLE_CONNECT_PPP_DEVICE_USB - help - Select which peripheral to use to connect to the PPP server. - - config EXAMPLE_CONNECT_PPP_DEVICE_USB - bool "USB" - depends on SOC_USB_OTG_SUPPORTED - select TINYUSB_CDC_ENABLED - help - Use USB ACM device. - - config EXAMPLE_CONNECT_PPP_DEVICE_UART - bool "UART" - help - Use UART. - - endchoice - - menu "UART Configuration" - depends on EXAMPLE_CONNECT_PPP_DEVICE_UART - config EXAMPLE_CONNECT_UART_TX_PIN - int "TXD Pin Number" - default 4 - range 0 31 - help - Pin number of UART TX. - - config EXAMPLE_CONNECT_UART_RX_PIN - int "RXD Pin Number" - default 5 - range 0 31 - help - Pin number of UART RX. - - config EXAMPLE_CONNECT_UART_BAUDRATE - int "UART Baudrate" - default 115200 - range 9600 3000000 - help - Baudrate of the UART device - - endmenu - - config EXAMPLE_PPP_CONN_MAX_RETRY - int "Maximum retry" - default 6 - help - Set the Maximum retry to avoid station reconnecting if the pppd - is not available - - endif # EXAMPLE_CONNECT_PPP - - config EXAMPLE_CONNECT_THREAD - bool "Connect using Thread interface" - depends on !IDF_TARGET_LINUX && OPENTHREAD_ENABLED - default y if SOC_IEEE802154_SUPPORTED - select EXAMPLE_CONNECT_IPV6 - help - Protocol examples can use Wi-Fi, Ethernet and/or Thread to connect to the network. - Choose this option to connect with Thread. - The operational active dataset of the Thread network can be configured in openthread - component at '->Components->OpenThread->Thread Core Features->Thread Operational Dataset' - - if EXAMPLE_CONNECT_THREAD - config EXAMPLE_THREAD_TASK_STACK_SIZE - int "Example Thread task stack size" - default 8192 - help - Thread task stack size - - menu "Radio Spinel Options" - depends on OPENTHREAD_RADIO_SPINEL_UART || OPENTHREAD_RADIO_SPINEL_SPI - - config EXAMPLE_THREAD_UART_RX_PIN - depends on OPENTHREAD_RADIO_SPINEL_UART - int "Uart Rx Pin" - default 17 - - config EXAMPLE_THREAD_UART_TX_PIN - depends on OPENTHREAD_RADIO_SPINEL_UART - int "Uart Tx pin" - default 18 - - config EXAMPLE_THREAD_UART_BAUD - depends on OPENTHREAD_RADIO_SPINEL_UART - int "Uart baud rate" - default 460800 - - config EXAMPLE_THREAD_UART_PORT - depends on OPENTHREAD_RADIO_SPINEL_UART - int "Uart port" - default 1 - - config EXAMPLE_THREAD_SPI_CS_PIN - depends on OPENTHREAD_RADIO_SPINEL_SPI - int "SPI CS Pin" - default 10 - - config EXAMPLE_THREAD_SPI_SCLK_PIN - depends on OPENTHREAD_RADIO_SPINEL_SPI - int "SPI SCLK Pin" - default 12 - - config EXAMPLE_THREAD_SPI_MISO_PIN - depends on OPENTHREAD_RADIO_SPINEL_SPI - int "SPI MISO Pin" - default 13 - - config EXAMPLE_THREAD_SPI_MOSI_PIN - depends on OPENTHREAD_RADIO_SPINEL_SPI - int "SPI MOSI Pin" - default 11 - - config EXAMPLE_THREAD_SPI_INTR_PIN - depends on OPENTHREAD_RADIO_SPINEL_SPI - int "SPI Interrupt Pin" - default 8 - endmenu - - endif - - config EXAMPLE_CONNECT_IPV4 - bool - depends on LWIP_IPV4 - default n if EXAMPLE_CONNECT_THREAD - default y - - config EXAMPLE_CONNECT_IPV6 - depends on EXAMPLE_CONNECT_WIFI || EXAMPLE_CONNECT_ETHERNET || EXAMPLE_CONNECT_PPP || EXAMPLE_CONNECT_THREAD - bool "Obtain IPv6 address" - default y - select LWIP_IPV6 - select LWIP_PPP_ENABLE_IPV6 if EXAMPLE_CONNECT_PPP - help - By default, examples will wait until IPv4 and IPv6 local link addresses are obtained. - Disable this option if the network does not support IPv6. - Choose the preferred IPv6 address type if the connection code should wait until other than - the local link address gets assigned. - Consider enabling IPv6 stateless address autoconfiguration (SLAAC) in the LWIP component. - - if EXAMPLE_CONNECT_IPV6 - choice EXAMPLE_CONNECT_PREFERRED_IPV6 - prompt "Preferred IPv6 Type" - default EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK - help - Select which kind of IPv6 address the connect logic waits for. - - config EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK - bool "Local Link Address" - help - Blocks until Local link address assigned. - - config EXAMPLE_CONNECT_IPV6_PREF_GLOBAL - bool "Global Address" - help - Blocks until Global address assigned. - - config EXAMPLE_CONNECT_IPV6_PREF_SITE_LOCAL - bool "Site Local Address" - help - Blocks until Site link address assigned. - - config EXAMPLE_CONNECT_IPV6_PREF_UNIQUE_LOCAL - bool "Unique Local Link Address" - help - Blocks until Unique local address assigned. - - endchoice - - endif - - -endmenu diff --git a/components/protocol_examples_common/README.md b/components/protocol_examples_common/README.md deleted file mode 100644 index 00111db..0000000 --- a/components/protocol_examples_common/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# protocol_example_connect - -This component implements the most common connection methods for ESP32 boards. It should be used mainly in examples of ESP-IDF to demonstrate functionality of network protocols and other libraries, that need the connection step as a prerequisite. - -## How to use this component - -Choose the preferred interface (WiFi, Ethernet, Thread, PPPoS) to connect to the network and configure the interface. - -It is possible to enable multiple interfaces simultaneously making the connection phase to block until all the chosen interfaces acquire IP addresses. -It is also possible to disable all interfaces, skipping the connection phase altogether. - -### WiFi - -Choose WiFi connection method (for chipsets that support it) and configure basic WiFi connection properties: -* WiFi SSID -* WiFI password -* Maximum connection retry (connection would be aborted if it doesn't succeed after specified number of retries) -* WiFi scan method (including RSSI and authorization mode threshold) - - - -### Ethernet - -Choose Ethernet connection if your board supports it. The most common settings is using Espressif Ethernet Kit, which is also the recommended HW for this selection. You can also select an SPI ethernet device (if your chipset doesn't support internal EMAC or if you prefer). It is also possible to use OpenCores Ethernet MAC if you're running the example under QEMU. - -### Thread - -Choose Thread connection if your board supports IEEE802.15.4 native radio or works with [OpenThread RCP](../../openthread/ot_rcp/README.md). You can configure the Thread network at menuconfig '->Components->OpenThread->Thread Core Features->Thread Operational Dataset'. - -If the Thread end-device joins a Thread network with a Thread Border Router that has the NAT64 feature enabled, the end-device can access the Internet with the standard DNS APIs after configuring the following properties: -* Enable DNS64 client ('->Components->OpenThread->Thread Core Features->Enable DNS64 client') -* Enable custom DNS external resolve Hook ('->Components->LWIP->Hooks->DNS external resolve Hook->Custom implementation') - -### PPP - -Point to point connection method creates a simple IP tunnel to the counterpart device (running PPP server), typically a Linux machine with pppd service. We currently support only PPP over Serial (using UART or USB CDC). This is useful for simple testing of networking layers, but with some additional configuration on the server side, we could simulate standard model of internet connectivity. The PPP server could be also represented by a cellular modem device with pre-configured connectivity and already switched to PPP mode (this setup is not very flexible though, so we suggest using a standard modem library implementing commands and modes, e.g. [esp_modem](https://components.espressif.com/component/espressif/esp_modem) ). - -> [!Note] -> Note that if you choose USB device, you have to manually add a dependency on `esp_tinyusb` component. This step is necessary to keep the `protocol_example_connect` component simple and dependency free. Please run this command from your project location to add the dependency: -> ```bash -> idf.py add-dependency espressif/esp_tinyusb^1 -> ``` - -#### Setup a PPP server - -Connect the board using UART or USB and note the device name, which would be typically: -* `/dev/ttyACMx` for USB devices -* `/dev/ttyUSBx` for UART devices - -Run the pppd server: - -```bash -sudo pppd /dev/ttyACM0 115200 192.168.11.1:192.168.11.2 ms-dns 8.8.8.8 modem local noauth debug nocrtscts nodetach +ipv6 -``` - -Please update the parameters with the correct serial device, baud rate, IP addresses, DNS server, use `+ipv6` if `EXAMPLE_CONNECT_IPV6=y`. - -#### Connection to outside - -In order to access other network endpoints, we have to configure some IP/translation rules. The easiest method is to setup a masquerade of the PPPD created interface (`ppp0`) to your default networking interface (`${ETH0}`). Here is an example of such rule: - -```bash -sudo iptables -t nat -A POSTROUTING -o ${ETH0} -j MASQUERADE -sudo iptables -A FORWARD -i ${ETH0} -o ppp0 -m state --state RELATED,ESTABLISHED -j ACCEPT -sudo iptables -A FORWARD -i ppp0 -o ${ETH0} -j ACCEPT -``` diff --git a/components/protocol_examples_common/addr_from_stdin.c b/components/protocol_examples_common/addr_from_stdin.c deleted file mode 100644 index e4c94b1..0000000 --- a/components/protocol_examples_common/addr_from_stdin.c +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include "esp_system.h" -#include "esp_log.h" -#include "esp_netif.h" -#include "protocol_examples_common.h" - -#include "lwip/sockets.h" -#include -#include - -#define HOST_IP_SIZE 128 - -esp_err_t get_addr_from_stdin(int port, int sock_type, int *ip_protocol, int *addr_family, struct sockaddr_storage *dest_addr) -{ - char host_ip[HOST_IP_SIZE]; - int len; - static bool already_init = false; - - // this function could be called multiple times -> make sure UART init runs only once - if (!already_init) { - example_configure_stdin_stdout(); - already_init = true; - } - - // ignore empty or LF only string (could receive from DUT class) - do { - fgets(host_ip, HOST_IP_SIZE, stdin); - len = strlen(host_ip); - } while (len<=1 && host_ip[0] == '\n'); - host_ip[len - 1] = '\0'; - - struct addrinfo hints, *addr_list, *cur; - memset( &hints, 0, sizeof( hints ) ); - - // run getaddrinfo() to decide on the IP protocol - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = sock_type; - hints.ai_protocol = IPPROTO_TCP; - if( getaddrinfo( host_ip, NULL, &hints, &addr_list ) != 0 ) { - return ESP_FAIL; - } - for( cur = addr_list; cur != NULL; cur = cur->ai_next ) { - memcpy(dest_addr, cur->ai_addr, sizeof(*dest_addr)); -#if CONFIG_EXAMPLE_CONNECT_IPV4 - if (cur->ai_family == AF_INET) { - *ip_protocol = IPPROTO_IP; - *addr_family = AF_INET; - // add port number and return on first IPv4 match - ((struct sockaddr_in*)dest_addr)->sin_port = htons(port); - freeaddrinfo( addr_list ); - return ESP_OK; - } -#endif // IPV4 -#if CONFIG_EXAMPLE_CONNECT_IPV6 - if (cur->ai_family == AF_INET6) { - *ip_protocol = IPPROTO_IPV6; - *addr_family = AF_INET6; - // add port and interface number and return on first IPv6 match - ((struct sockaddr_in6*)dest_addr)->sin6_port = htons(port); - ((struct sockaddr_in6*)dest_addr)->sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE); - freeaddrinfo( addr_list ); - return ESP_OK; - } -#endif // IPV6 - } - // no match found - freeaddrinfo( addr_list ); - return ESP_FAIL; -} diff --git a/components/protocol_examples_common/connect.c b/components/protocol_examples_common/connect.c deleted file mode 100644 index 65fa2b8..0000000 --- a/components/protocol_examples_common/connect.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ - -#include -#include "protocol_examples_common.h" -#include "example_common_private.h" -#include "sdkconfig.h" -#include "esp_event.h" -#include "esp_wifi.h" -#include "esp_wifi_default.h" -#include "esp_log.h" -#include "esp_netif.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "lwip/err.h" -#include "lwip/sys.h" - -static const char *TAG = "example_common"; - -#if CONFIG_EXAMPLE_CONNECT_IPV6 -/* types of ipv6 addresses to be displayed on ipv6 events */ -const char *example_ipv6_addr_types_to_str[6] = { - "ESP_IP6_ADDR_IS_UNKNOWN", - "ESP_IP6_ADDR_IS_GLOBAL", - "ESP_IP6_ADDR_IS_LINK_LOCAL", - "ESP_IP6_ADDR_IS_SITE_LOCAL", - "ESP_IP6_ADDR_IS_UNIQUE_LOCAL", - "ESP_IP6_ADDR_IS_IPV4_MAPPED_IPV6" -}; -#endif - -/** - * @brief Checks the netif description if it contains specified prefix. - * All netifs created within common connect component are prefixed with the module TAG, - * so it returns true if the specified netif is owned by this module - */ -bool example_is_our_netif(const char *prefix, esp_netif_t *netif) -{ - return strncmp(prefix, esp_netif_get_desc(netif), strlen(prefix) - 1) == 0; -} - -static bool netif_desc_matches_with(esp_netif_t *netif, void *ctx) -{ - return strcmp(ctx, esp_netif_get_desc(netif)) == 0; -} - -esp_netif_t *get_example_netif_from_desc(const char *desc) -{ - return esp_netif_find_if(netif_desc_matches_with, (void*)desc); -} - -static esp_err_t print_all_ips_tcpip(void* ctx) -{ - const char *prefix = ctx; - // iterate over active interfaces, and print out IPs of "our" netifs - esp_netif_t *netif = NULL; - while ((netif = esp_netif_next_unsafe(netif)) != NULL) { - if (example_is_our_netif(prefix, netif)) { - ESP_LOGI(TAG, "Connected to %s", esp_netif_get_desc(netif)); -#if CONFIG_EXAMPLE_CONNECT_IPV4 - esp_netif_ip_info_t ip; - ESP_ERROR_CHECK(esp_netif_get_ip_info(netif, &ip)); - - ESP_LOGI(TAG, "- IPv4 address: " IPSTR ",", IP2STR(&ip.ip)); -#endif -#if CONFIG_EXAMPLE_CONNECT_IPV6 - esp_ip6_addr_t ip6[MAX_IP6_ADDRS_PER_NETIF]; - int ip6_addrs = esp_netif_get_all_ip6(netif, ip6); - for (int j = 0; j < ip6_addrs; ++j) { - esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&(ip6[j])); - ESP_LOGI(TAG, "- IPv6 address: " IPV6STR ", type: %s", IPV62STR(ip6[j]), example_ipv6_addr_types_to_str[ipv6_type]); - } -#endif - } - } - return ESP_OK; -} - -void example_print_all_netif_ips(const char *prefix) -{ - // Print all IPs in TCPIP context to avoid potential races of removing/adding netifs when iterating over the list - esp_netif_tcpip_exec(print_all_ips_tcpip, (void*) prefix); -} - - -esp_err_t example_connect(void) -{ -#if CONFIG_EXAMPLE_CONNECT_ETHERNET - if (example_ethernet_connect() != ESP_OK) { - return ESP_FAIL; - } - ESP_ERROR_CHECK(esp_register_shutdown_handler(&example_ethernet_shutdown)); -#endif -#if CONFIG_EXAMPLE_CONNECT_WIFI - if (example_wifi_connect() != ESP_OK) { - return ESP_FAIL; - } - ESP_ERROR_CHECK(esp_register_shutdown_handler(&example_wifi_shutdown)); -#endif -#if CONFIG_EXAMPLE_CONNECT_THREAD - if (example_thread_connect() != ESP_OK) { - return ESP_FAIL; - } - ESP_ERROR_CHECK(esp_register_shutdown_handler(&example_thread_shutdown)); -#endif -#if CONFIG_EXAMPLE_CONNECT_PPP - if (example_ppp_connect() != ESP_OK) { - return ESP_FAIL; - } - ESP_ERROR_CHECK(esp_register_shutdown_handler(&example_ppp_shutdown)); -#endif - -#if CONFIG_EXAMPLE_CONNECT_ETHERNET - example_print_all_netif_ips(EXAMPLE_NETIF_DESC_ETH); -#endif - -#if CONFIG_EXAMPLE_CONNECT_WIFI - example_print_all_netif_ips(EXAMPLE_NETIF_DESC_STA); -#endif - -#if CONFIG_EXAMPLE_CONNECT_THREAD - example_print_all_netif_ips(EXAMPLE_NETIF_DESC_THREAD); -#endif - -#if CONFIG_EXAMPLE_CONNECT_PPP - example_print_all_netif_ips(EXAMPLE_NETIF_DESC_PPP); -#endif - - return ESP_OK; -} - - -esp_err_t example_disconnect(void) -{ -#if CONFIG_EXAMPLE_CONNECT_ETHERNET - example_ethernet_shutdown(); - ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&example_ethernet_shutdown)); -#endif -#if CONFIG_EXAMPLE_CONNECT_WIFI - example_wifi_shutdown(); - ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&example_wifi_shutdown)); -#endif - return ESP_OK; -} diff --git a/components/protocol_examples_common/console_cmd.c b/components/protocol_examples_common/console_cmd.c deleted file mode 100644 index 342a92c..0000000 --- a/components/protocol_examples_common/console_cmd.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ - - -#include -#include "protocol_examples_common.h" -#include "example_common_private.h" -#include "esp_wifi.h" -#include "esp_log.h" -#include "esp_console.h" -#include "argtable3/argtable3.h" - - -static const char *TAG = "example_console"; - -typedef struct { - struct arg_str *ssid; - struct arg_str *password; - struct arg_int *channel; - struct arg_end *end; -} wifi_connect_args_t; -static wifi_connect_args_t connect_args; - -static int cmd_do_wifi_connect(int argc, char **argv) -{ - int nerrors = arg_parse(argc, argv, (void **) &connect_args); - - if (nerrors != 0) { - arg_print_errors(stderr, connect_args.end, argv[0]); - return 1; - } - - wifi_config_t wifi_config = { - .sta = { - .scan_method = WIFI_ALL_CHANNEL_SCAN, - .sort_method = WIFI_CONNECT_AP_BY_SIGNAL, - }, - }; - if (connect_args.channel->count > 0) { - wifi_config.sta.channel = (uint8_t)(connect_args.channel->ival[0]); - } - const char *ssid = connect_args.ssid->sval[0]; - const char *pass = connect_args.password->sval[0]; - strlcpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); - if (pass) { - strlcpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); - } - example_wifi_sta_do_connect(wifi_config, false); - return 0; -} - -static int cmd_do_wifi_disconnect(int argc, char **argv) -{ - example_wifi_sta_do_disconnect(); - return 0; -} - -void example_register_wifi_connect_commands(void) -{ - ESP_LOGI(TAG, "Registering WiFi connect commands."); - example_wifi_start(); - - connect_args.ssid = arg_str1(NULL, NULL, "", "SSID of AP"); - connect_args.password = arg_str0(NULL, NULL, "", "password of AP"); - connect_args.channel = arg_int0("n", "channel", "", "channel of AP"); - connect_args.end = arg_end(2); - const esp_console_cmd_t wifi_connect_cmd = { - .command = "wifi_connect", - .help = "WiFi is station mode, join specified soft-AP", - .hint = NULL, - .func = &cmd_do_wifi_connect, - .argtable = &connect_args - }; - ESP_ERROR_CHECK( esp_console_cmd_register(&wifi_connect_cmd) ); - - - const esp_console_cmd_t wifi_disconnect_cmd = { - .command = "wifi_disconnect", - .help = "Do wifi disconnect", - .hint = NULL, - .func = &cmd_do_wifi_disconnect, - }; - ESP_ERROR_CHECK( esp_console_cmd_register(&wifi_disconnect_cmd) ); -} diff --git a/components/protocol_examples_common/eth_connect.c b/components/protocol_examples_common/eth_connect.c deleted file mode 100644 index ad48069..0000000 --- a/components/protocol_examples_common/eth_connect.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ - -#include -#include "protocol_examples_common.h" -#include "example_common_private.h" -#include "esp_event.h" -#include "esp_eth.h" -#if CONFIG_ETH_USE_SPI_ETHERNET -#include "driver/spi_master.h" -#endif // CONFIG_ETH_USE_SPI_ETHERNET -#include "esp_log.h" -#include "esp_mac.h" -#include "driver/gpio.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" - - -static const char *TAG = "ethernet_connect"; -static SemaphoreHandle_t s_semph_get_ip_addrs = NULL; -#if CONFIG_EXAMPLE_CONNECT_IPV6 -static SemaphoreHandle_t s_semph_get_ip6_addrs = NULL; -#endif - -static esp_netif_t *eth_start(void); -static void eth_stop(void); - - -/** Event handler for Ethernet events */ - -static void eth_on_got_ip(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) -{ - ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; - if (!example_is_our_netif(EXAMPLE_NETIF_DESC_ETH, event->esp_netif)) { - return; - } - ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); - xSemaphoreGive(s_semph_get_ip_addrs); -} - -#if CONFIG_EXAMPLE_CONNECT_IPV6 - -static void eth_on_got_ipv6(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) -{ - ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; - if (!example_is_our_netif(EXAMPLE_NETIF_DESC_ETH, event->esp_netif)) { - return; - } - esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip); - ESP_LOGI(TAG, "Got IPv6 event: Interface \"%s\" address: " IPV6STR ", type: %s", esp_netif_get_desc(event->esp_netif), - IPV62STR(event->ip6_info.ip), example_ipv6_addr_types_to_str[ipv6_type]); - if (ipv6_type == EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) { - xSemaphoreGive(s_semph_get_ip6_addrs); - } -} - -static void on_eth_event(void *esp_netif, esp_event_base_t event_base, - int32_t event_id, void *event_data) -{ - switch (event_id) { - case ETHERNET_EVENT_CONNECTED: - ESP_LOGI(TAG, "Ethernet Link Up"); - ESP_ERROR_CHECK(esp_netif_create_ip6_linklocal(esp_netif)); - break; - default: - break; - } -} - -#endif // CONFIG_EXAMPLE_CONNECT_IPV6 - -static esp_eth_handle_t s_eth_handle = NULL; -static esp_eth_mac_t *s_mac = NULL; -static esp_eth_phy_t *s_phy = NULL; -static esp_eth_netif_glue_handle_t s_eth_glue = NULL; - -static esp_netif_t *eth_start(void) -{ - esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH(); - // Warning: the interface desc is used in tests to capture actual connection details (IP, gw, mask) - esp_netif_config.if_desc = EXAMPLE_NETIF_DESC_ETH; - esp_netif_config.route_prio = 64; - esp_netif_config_t netif_config = { - .base = &esp_netif_config, - .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH - }; - esp_netif_t *netif = esp_netif_new(&netif_config); - assert(netif); - - eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); - mac_config.rx_task_stack_size = CONFIG_EXAMPLE_ETHERNET_EMAC_TASK_STACK_SIZE; - eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); - phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR; - phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO; -#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET - eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); - esp32_emac_config.smi_gpio.mdc_num = CONFIG_EXAMPLE_ETH_MDC_GPIO; - esp32_emac_config.smi_gpio.mdio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO; - s_mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); -#if CONFIG_EXAMPLE_ETH_PHY_GENERIC - s_phy = esp_eth_phy_new_generic(&phy_config); -#elif CONFIG_EXAMPLE_ETH_PHY_IP101 - s_phy = esp_eth_phy_new_ip101(&phy_config); -#elif CONFIG_EXAMPLE_ETH_PHY_RTL8201 - s_phy = esp_eth_phy_new_rtl8201(&phy_config); -#elif CONFIG_EXAMPLE_ETH_PHY_LAN87XX - s_phy = esp_eth_phy_new_lan87xx(&phy_config); -#elif CONFIG_EXAMPLE_ETH_PHY_DP83848 - s_phy = esp_eth_phy_new_dp83848(&phy_config); -#elif CONFIG_EXAMPLE_ETH_PHY_KSZ80XX - s_phy = esp_eth_phy_new_ksz80xx(&phy_config); -#endif -#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET - gpio_install_isr_service(0); - spi_bus_config_t buscfg = { - .miso_io_num = CONFIG_EXAMPLE_ETH_SPI_MISO_GPIO, - .mosi_io_num = CONFIG_EXAMPLE_ETH_SPI_MOSI_GPIO, - .sclk_io_num = CONFIG_EXAMPLE_ETH_SPI_SCLK_GPIO, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - }; - ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO)); - spi_device_interface_config_t spi_devcfg = { - .mode = 0, - .clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000, - .spics_io_num = CONFIG_EXAMPLE_ETH_SPI_CS_GPIO, - .queue_size = 20 - }; -#if CONFIG_EXAMPLE_USE_DM9051 - /* dm9051 ethernet driver is based on spi driver */ - eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg); - dm9051_config.int_gpio_num = CONFIG_EXAMPLE_ETH_SPI_INT_GPIO; - s_mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config); - s_phy = esp_eth_phy_new_dm9051(&phy_config); -#elif CONFIG_EXAMPLE_USE_W5500 - /* w5500 ethernet driver is based on spi driver */ - eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg); - w5500_config.int_gpio_num = CONFIG_EXAMPLE_ETH_SPI_INT_GPIO; - s_mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); - s_phy = esp_eth_phy_new_w5500(&phy_config); -#endif -#elif CONFIG_EXAMPLE_USE_OPENETH - phy_config.autonego_timeout_ms = 100; - s_mac = esp_eth_mac_new_openeth(&mac_config); - s_phy = esp_eth_phy_new_dp83848(&phy_config); -#endif - - // Install Ethernet driver - esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy); - ESP_ERROR_CHECK(esp_eth_driver_install(&config, &s_eth_handle)); - -#if CONFIG_EXAMPLE_USE_SPI_ETHERNET - /* The SPI Ethernet module might doesn't have a burned factory MAC address, we cat to set it manually. - We set the ESP_MAC_ETH mac address as the default, if you want to use ESP_MAC_EFUSE_CUSTOM mac address, please enable the - configuration: `ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC` - */ - uint8_t eth_mac[6] = {0}; - ESP_ERROR_CHECK(esp_read_mac(eth_mac, ESP_MAC_ETH)); - ESP_ERROR_CHECK(esp_eth_ioctl(s_eth_handle, ETH_CMD_S_MAC_ADDR, eth_mac)); -#endif // CONFIG_EXAMPLE_USE_SPI_ETHERNET - - // combine driver with netif - s_eth_glue = esp_eth_new_netif_glue(s_eth_handle); - esp_netif_attach(netif, s_eth_glue); - - // Register user defined event handlers - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, ð_on_got_ip, NULL)); -#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 - ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &on_eth_event, netif)); - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, ð_on_got_ipv6, NULL)); -#endif - - esp_eth_start(s_eth_handle); - return netif; -} - -static void eth_stop(void) -{ - esp_netif_t *eth_netif = get_example_netif_from_desc(EXAMPLE_NETIF_DESC_ETH); - ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, ð_on_got_ip)); -#if CONFIG_EXAMPLE_CONNECT_IPV6 - ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, ð_on_got_ipv6)); - ESP_ERROR_CHECK(esp_event_handler_unregister(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &on_eth_event)); -#endif - ESP_ERROR_CHECK(esp_eth_stop(s_eth_handle)); - ESP_ERROR_CHECK(esp_eth_del_netif_glue(s_eth_glue)); - ESP_ERROR_CHECK(esp_eth_driver_uninstall(s_eth_handle)); - s_eth_handle = NULL; - ESP_ERROR_CHECK(s_phy->del(s_phy)); - ESP_ERROR_CHECK(s_mac->del(s_mac)); - - esp_netif_destroy(eth_netif); -} - -esp_eth_handle_t get_example_eth_handle(void) -{ - return s_eth_handle; -} - -/* tear down connection, release resources */ -void example_ethernet_shutdown(void) -{ - if (s_semph_get_ip_addrs == NULL) { - return; - } - vSemaphoreDelete(s_semph_get_ip_addrs); - s_semph_get_ip_addrs = NULL; -#if CONFIG_EXAMPLE_CONNECT_IPV6 - vSemaphoreDelete(s_semph_get_ip6_addrs); - s_semph_get_ip6_addrs = NULL; -#endif - eth_stop(); -} - -esp_err_t example_ethernet_connect(void) -{ -#if CONFIG_EXAMPLE_CONNECT_IPV4 - s_semph_get_ip_addrs = xSemaphoreCreateBinary(); - if (s_semph_get_ip_addrs == NULL) { - return ESP_ERR_NO_MEM; - } -#endif -#if CONFIG_EXAMPLE_CONNECT_IPV6 - s_semph_get_ip6_addrs = xSemaphoreCreateBinary(); - if (s_semph_get_ip6_addrs == NULL) { - vSemaphoreDelete(s_semph_get_ip_addrs); - return ESP_ERR_NO_MEM; - } -#endif - eth_start(); - ESP_LOGI(TAG, "Waiting for IP(s)."); -#if CONFIG_EXAMPLE_CONNECT_IPV4 - xSemaphoreTake(s_semph_get_ip_addrs, portMAX_DELAY); -#endif -#if CONFIG_EXAMPLE_CONNECT_IPV6 - xSemaphoreTake(s_semph_get_ip6_addrs, portMAX_DELAY); -#endif - return ESP_OK; -} diff --git a/components/protocol_examples_common/include/addr_from_stdin.h b/components/protocol_examples_common/include/addr_from_stdin.h deleted file mode 100644 index 827f8c2..0000000 --- a/components/protocol_examples_common/include/addr_from_stdin.h +++ /dev/null @@ -1,44 +0,0 @@ -/* Common utilities for socket address input interface: - The API get_addr_from_stdin() is mainly used by socket client examples which read IP address from stdin (if configured). - This option is typically used in the CI, but could be enabled in the project configuration. - In that case this component is used to receive a string that is evaluated and processed to output - socket structures to open a connectio - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. - */ - -#pragma once - -#include "lwip/sys.h" -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Read and evaluate IP address from stdin - * - * This API reads stdin and parses the input address using getaddrinfo() - * to fill in struct sockaddr_storage (for both IPv4 and IPv6) used to open - * a socket. IP protocol is guessed from the IP address string. - * - * @param[in] port port number of expected connection - * @param[in] sock_type expected protocol: SOCK_STREAM or SOCK_DGRAM - * @param[out] ip_protocol resultant IP protocol: IPPROTO_IP or IPPROTO_IP6 - * @param[out] addr_family resultant address family: AF_INET or AF_INET6 - * @param[out] dest_addr sockaddr_storage structure (for both IPv4 and IPv6) - * @return ESP_OK on success, ESP_FAIL otherwise - */ -esp_err_t get_addr_from_stdin(int port, int sock_type, - int *ip_protocol, - int *addr_family, - struct sockaddr_storage *dest_addr); - -#ifdef __cplusplus -} -#endif diff --git a/components/protocol_examples_common/include/addr_from_stdin.h:Zone.Identifier b/components/protocol_examples_common/include/addr_from_stdin.h:Zone.Identifier deleted file mode 100644 index d6c1ec682968c796b9f5e9e080cc6f674b57c766..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2xdl#JyUFr831@K2xdl#JyUFr831@K2x - -#include - -#ifdef CONFIG_OPENTHREAD_RADIO_NATIVE -#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ - { \ - .radio_mode = RADIO_MODE_NATIVE, \ - } - -#elif defined(CONFIG_OPENTHREAD_RADIO_SPINEL_UART) -#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ - { \ - .radio_mode = RADIO_MODE_UART_RCP, \ - .radio_uart_config = \ - { \ - .port = CONFIG_EXAMPLE_THREAD_UART_PORT, \ - .uart_config = \ - { \ - .baud_rate = CONFIG_EXAMPLE_THREAD_UART_BAUD, \ - .data_bits = UART_DATA_8_BITS, \ - .parity = UART_PARITY_DISABLE, \ - .stop_bits = UART_STOP_BITS_1, \ - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ - .rx_flow_ctrl_thresh = 0, \ - .source_clk = UART_SCLK_DEFAULT, \ - }, \ - .rx_pin = CONFIG_EXAMPLE_THREAD_UART_RX_PIN, \ - .tx_pin = CONFIG_EXAMPLE_THREAD_UART_TX_PIN, \ - }, \ - } -#elif defined(CONFIG_OPENTHREAD_RADIO_SPINEL_SPI) -#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ - { \ - .radio_mode = RADIO_MODE_SPI_RCP, \ - .radio_spi_config = \ - { \ - .host_device = SPI2_HOST, \ - .dma_channel = 2, \ - .spi_interface = \ - { \ - .mosi_io_num = CONFIG_EXAMPLE_THREAD_SPI_MOSI_PIN, \ - .miso_io_num = CONFIG_EXAMPLE_THREAD_SPI_MISO_PIN, \ - .sclk_io_num = CONFIG_EXAMPLE_THREAD_SPI_SCLK_PIN, \ - .quadwp_io_num = -1, \ - .quadhd_io_num = -1, \ - }, \ - .spi_device = \ - { \ - .cs_ena_pretrans = 2, \ - .input_delay_ns = 100, \ - .mode = 0, \ - .clock_speed_hz = 2500 * 1000, \ - .spics_io_num = CONFIG_EXAMPLE_THREAD_SPI_CS_PIN, \ - .queue_size = 5, \ - }, \ - .intr_pin = CONFIG_EXAMPLE_THREAD_SPI_INTR_PIN, \ - }, \ - } -#else -#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ - { \ - .radio_mode = RADIO_MODE_TREL, \ - } -#endif - -#if CONFIG_OPENTHREAD_CONSOLE_TYPE_UART -#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ - { \ - .host_connection_mode = HOST_CONNECTION_MODE_CLI_UART, \ - .host_uart_config = \ - { \ - .port = 0, \ - .uart_config = \ - { \ - .baud_rate = 115200, \ - .data_bits = UART_DATA_8_BITS, \ - .parity = UART_PARITY_DISABLE, \ - .stop_bits = UART_STOP_BITS_1, \ - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ - .rx_flow_ctrl_thresh = 0, \ - .source_clk = UART_SCLK_DEFAULT, \ - }, \ - .rx_pin = UART_PIN_NO_CHANGE, \ - .tx_pin = UART_PIN_NO_CHANGE, \ - }, \ - } -#elif CONFIG_OPENTHREAD_CONSOLE_TYPE_USB_SERIAL_JTAG -#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ - { \ - .host_connection_mode = HOST_CONNECTION_MODE_CLI_USB, \ - .host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(), \ - } -#else -#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ - { \ - .host_connection_mode = HOST_CONNECTION_MODE_NONE, \ - } -#endif - -#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \ - { \ - .storage_partition_name = "nvs", \ - .netif_queue_size = 10, \ - .task_queue_size = 10, \ - } diff --git a/components/protocol_examples_common/include/protocol_examples_thread_config.h:Zone.Identifier b/components/protocol_examples_common/include/protocol_examples_thread_config.h:Zone.Identifier deleted file mode 100644 index d6c1ec682968c796b9f5e9e080cc6f674b57c766..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2x - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * @brief Encode an URI - * - * @param dest a destination memory location - * @param src the source string - * @param len the length of the source string - * @return uint32_t the count of escaped characters - * - * @note Please allocate the destination buffer keeping in mind that encoding a - * special character will take up 3 bytes (for '%' and two hex digits). - * In the worst-case scenario, the destination buffer will have to be 3 times - * that of the source string. - */ -uint32_t example_uri_encode(char *dest, const char *src, size_t len); - -/** - * @brief Decode an URI - * - * @param dest a destination memory location - * @param src the source string - * @param len the length of the source string - * - * @note Please allocate the destination buffer keeping in mind that a decoded - * special character will take up 2 less bytes than its encoded form. - * In the worst-case scenario, the destination buffer will have to be - * the same size that of the source string. - */ -void example_uri_decode(char *dest, const char *src, size_t len); - -#ifdef __cplusplus -} -#endif diff --git a/components/protocol_examples_common/include/protocol_examples_utils.h:Zone.Identifier b/components/protocol_examples_common/include/protocol_examples_utils.h:Zone.Identifier deleted file mode 100644 index d6c1ec682968c796b9f5e9e080cc6f674b57c766..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2x -#include -#include "sdkconfig.h" -#include "protocol_examples_common.h" -#include "example_common_private.h" - -#if CONFIG_EXAMPLE_CONNECT_PPP -#include "esp_log.h" -#include "esp_netif.h" -#include "esp_netif_ppp.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" - -#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB -#include "tinyusb.h" -#include "tusb_cdc_acm.h" - -static int s_itf; -static uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE]; - -#else // DEVICE is UART - -#include "driver/uart.h" -#define BUF_SIZE (1024) -static bool s_stop_task = false; - -#endif // CONNECT_PPP_DEVICE - - -static const char *TAG = "example_connect_ppp"; -static int s_retry_num = 0; -static EventGroupHandle_t s_event_group = NULL; -static esp_netif_t *s_netif; -static const int GOT_IPV4 = BIT0; -static const int CONNECTION_FAILED = BIT1; -#if CONFIG_EXAMPLE_CONNECT_IPV6 -static const int GOT_IPV6 = BIT2; -#define CONNECT_BITS (GOT_IPV4|GOT_IPV6|CONNECTION_FAILED) -#else -#define CONNECT_BITS (GOT_IPV4|CONNECTION_FAILED) -#endif - -static esp_err_t transmit(void *h, void *buffer, size_t len) -{ - ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_VERBOSE); -#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB - tinyusb_cdcacm_write_queue(s_itf, buffer, len); - tinyusb_cdcacm_write_flush(s_itf, 0); -#else // DEVICE_UART - uart_write_bytes(UART_NUM_1, buffer, len); -#endif // CONNECT_PPP_DEVICE - return ESP_OK; -} - -static esp_netif_driver_ifconfig_t driver_cfg = { - .handle = (void *)1, // singleton driver, just to != NULL - .transmit = transmit, -}; -const esp_netif_driver_ifconfig_t *ppp_driver_cfg = &driver_cfg; - -static void on_ip_event(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) -{ - - if (event_id == IP_EVENT_PPP_GOT_IP) { - ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; - if (!example_is_our_netif(EXAMPLE_NETIF_DESC_PPP, event->esp_netif)) { - return; - } - esp_netif_t *netif = event->esp_netif; - esp_netif_dns_info_t dns_info; - ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); - esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info); - ESP_LOGI(TAG, "Main DNS server : " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); - xEventGroupSetBits(s_event_group, GOT_IPV4); -#if CONFIG_EXAMPLE_CONNECT_IPV6 - } else if (event_id == IP_EVENT_GOT_IP6) { - ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; - if (!example_is_our_netif(EXAMPLE_NETIF_DESC_PPP, event->esp_netif)) { - return; - } - esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip); - ESP_LOGI(TAG, "Got IPv6 event: Interface \"%s\" address: " IPV6STR ", type: %s", esp_netif_get_desc(event->esp_netif), - IPV62STR(event->ip6_info.ip), example_ipv6_addr_types_to_str[ipv6_type]); - if (ipv6_type == EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) { - xEventGroupSetBits(s_event_group, GOT_IPV6); - } -#endif - } else if (event_id == IP_EVENT_PPP_LOST_IP) { - ESP_LOGI(TAG, "Disconnect from PPP Server"); - s_retry_num++; - if (s_retry_num > CONFIG_EXAMPLE_PPP_CONN_MAX_RETRY) { - ESP_LOGE(TAG, "PPP Connection failed %d times, stop reconnecting.", s_retry_num); - xEventGroupSetBits(s_event_group, CONNECTION_FAILED); - } else { - ESP_LOGI(TAG, "PPP Connection failed %d times, try to reconnect.", s_retry_num); - esp_netif_action_start(s_netif, 0, 0, 0); - esp_netif_action_connected(s_netif, 0, 0, 0); - } - - } -} - -#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB -static void cdc_rx_callback(int itf, cdcacm_event_t *event) -{ - size_t rx_size = 0; - if (itf != s_itf) { - // Not our channel - return; - } - esp_err_t ret = tinyusb_cdcacm_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size); - if (ret == ESP_OK) { - ESP_LOG_BUFFER_HEXDUMP(TAG, buf, rx_size, ESP_LOG_VERBOSE); - // pass the received data to the network interface - esp_netif_receive(s_netif, buf, rx_size, NULL); - } else { - ESP_LOGE(TAG, "Read error"); - } -} - -static void line_state_changed(int itf, cdcacm_event_t *event) -{ - s_itf = itf; // use this channel for the netif communication - ESP_LOGI(TAG, "Line state changed on channel %d", itf); -} -#else // DEVICE is UART - -static void ppp_task(void *args) -{ - uart_config_t uart_config = {}; - uart_config.baud_rate = CONFIG_EXAMPLE_CONNECT_UART_BAUDRATE; - uart_config.data_bits = UART_DATA_8_BITS; - uart_config.parity = UART_PARITY_DISABLE; - uart_config.stop_bits = UART_STOP_BITS_1; - uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; - uart_config.source_clk = UART_SCLK_DEFAULT; - - QueueHandle_t event_queue; - ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, BUF_SIZE, 0, 16, &event_queue, 0)); - ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_config)); - ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, CONFIG_EXAMPLE_CONNECT_UART_TX_PIN, CONFIG_EXAMPLE_CONNECT_UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); - ESP_ERROR_CHECK(uart_set_rx_timeout(UART_NUM_1, 1)); - - char *buffer = (char*)malloc(BUF_SIZE); - uart_event_t event; - esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_GOT_IP, esp_netif_action_connected, s_netif); - esp_netif_action_start(s_netif, 0, 0, 0); - esp_netif_action_connected(s_netif, 0, 0, 0); - while (!s_stop_task) { - xQueueReceive(event_queue, &event, pdMS_TO_TICKS(1000)); - if (event.type == UART_DATA) { - size_t len; - uart_get_buffered_data_len(UART_NUM_1, &len); - if (len) { - len = uart_read_bytes(UART_NUM_1, buffer, BUF_SIZE, 0); - ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_VERBOSE); - esp_netif_receive(s_netif, buffer, len, NULL); - } - } else { - ESP_LOGW(TAG, "Received UART event: %d", event.type); - } - } - free(buffer); - vTaskDelete(NULL); -} - -#endif // CONNECT_PPP_DEVICE - -esp_err_t example_ppp_connect(void) -{ - ESP_LOGI(TAG, "Start example_connect."); - -#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB - ESP_LOGI(TAG, "USB initialization"); - const tinyusb_config_t tusb_cfg = { - .device_descriptor = NULL, - .string_descriptor = NULL, - .external_phy = false, - .configuration_descriptor = NULL, - }; - - ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); - - tinyusb_config_cdcacm_t acm_cfg = { - .usb_dev = TINYUSB_USBDEV_0, - .cdc_port = TINYUSB_CDC_ACM_0, - .callback_rx = &cdc_rx_callback, - .callback_rx_wanted_char = NULL, - .callback_line_state_changed = NULL, - .callback_line_coding_changed = NULL - }; - - ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); - /* the second way to register a callback */ - ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback( - TINYUSB_CDC_ACM_0, - CDC_EVENT_LINE_STATE_CHANGED, - &line_state_changed)); -#endif // CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB - - s_event_group = xEventGroupCreate(); - - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event, NULL)); - - esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP(); - base_netif_cfg.if_desc = EXAMPLE_NETIF_DESC_PPP; - esp_netif_config_t netif_ppp_config = { .base = &base_netif_cfg, - .driver = ppp_driver_cfg, - .stack = ESP_NETIF_NETSTACK_DEFAULT_PPP - }; - - s_netif = esp_netif_new(&netif_ppp_config); - assert(s_netif); -#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB - esp_netif_action_start(s_netif, 0, 0, 0); - esp_netif_action_connected(s_netif, 0, 0, 0); -#else // DEVICE is UART - s_stop_task = false; - if (xTaskCreate(ppp_task, "ppp connect", 4096, NULL, 5, NULL) != pdTRUE) { - ESP_LOGE(TAG, "Failed to create a ppp connection task"); - return ESP_FAIL; - } -#endif // CONNECT_PPP_DEVICE - - ESP_LOGI(TAG, "Waiting for IP address"); - EventBits_t bits = xEventGroupWaitBits(s_event_group, CONNECT_BITS, pdFALSE, pdFALSE, portMAX_DELAY); - if (bits & CONNECTION_FAILED) { - ESP_LOGE(TAG, "Connection failed!"); - return ESP_FAIL; - } - ESP_LOGI(TAG, "Connected!"); - - return ESP_OK; -} - -void example_ppp_shutdown(void) -{ - ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event)); -#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_UART - s_stop_task = true; - vTaskDelay(pdMS_TO_TICKS(1000)); // wait for the ppp task to stop -#endif - - esp_netif_action_disconnected(s_netif, 0, 0, 0); - - vEventGroupDelete(s_event_group); - esp_netif_action_stop(s_netif, 0, 0, 0); - esp_netif_destroy(s_netif); - s_netif = NULL; - s_event_group = NULL; -} - -#endif // CONFIG_EXAMPLE_CONNECT_PPP diff --git a/components/protocol_examples_common/ppp_connect.c:Zone.Identifier b/components/protocol_examples_common/ppp_connect.c:Zone.Identifier deleted file mode 100644 index d6c1ec682968c796b9f5e9e080cc6f674b57c766..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2x -#include -#include -#include - -#include "protocol_examples_utils.h" - -/* Type of Escape algorithms to be used */ -#define NGX_ESCAPE_URI (0) -#define NGX_ESCAPE_ARGS (1) -#define NGX_ESCAPE_URI_COMPONENT (2) -#define NGX_ESCAPE_HTML (3) -#define NGX_ESCAPE_REFRESH (4) -#define NGX_ESCAPE_MEMCACHED (5) -#define NGX_ESCAPE_MAIL_AUTH (6) - -/* Type of Unescape algorithms to be used */ -#define NGX_UNESCAPE_URI (1) -#define NGX_UNESCAPE_REDIRECT (2) - - -uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size, unsigned int type) -{ - unsigned int n; - uint32_t *escape; - static u_char hex[] = "0123456789ABCDEF"; - - /* - * Per RFC 3986 only the following chars are allowed in URIs unescaped: - * - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - * - * And "%" can appear as a part of escaping itself. The following - * characters are not allowed and need to be escaped: %00-%1F, %7F-%FF, - * " ", """, "<", ">", "\", "^", "`", "{", "|", "}". - */ - - /* " ", "#", "%", "?", not allowed */ - - static uint32_t uri[] = { - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - - /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ - 0xd000002d, /* 1101 0000 0000 0000 0000 0000 0010 1101 */ - - /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ - 0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */ - - /* ~}| {zyx wvut srqp onml kjih gfed cba` */ - 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ - - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - }; - - /* " ", "#", "%", "&", "+", ";", "?", not allowed */ - - static uint32_t args[] = { - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - - /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ - 0xd800086d, /* 1101 1000 0000 0000 0000 1000 0110 1101 */ - - /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ - 0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */ - - /* ~}| {zyx wvut srqp onml kjih gfed cba` */ - 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ - - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - }; - - /* not ALPHA, DIGIT, "-", ".", "_", "~" */ - - static uint32_t uri_component[] = { - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - - /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ - 0xfc009fff, /* 1111 1100 0000 0000 1001 1111 1111 1111 */ - - /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ - 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */ - - /* ~}| {zyx wvut srqp onml kjih gfed cba` */ - 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ - - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - }; - - /* " ", "#", """, "%", "'", not allowed */ - - static uint32_t html[] = { - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - - /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ - 0x500000ad, /* 0101 0000 0000 0000 0000 0000 1010 1101 */ - - /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ - 0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */ - - /* ~}| {zyx wvut srqp onml kjih gfed cba` */ - 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ - - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - }; - - /* " ", """, "'", not allowed */ - - static uint32_t refresh[] = { - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - - /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ - 0x50000085, /* 0101 0000 0000 0000 0000 0000 1000 0101 */ - - /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ - 0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */ - - /* ~}| {zyx wvut srqp onml kjih gfed cba` */ - 0xd8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ - - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - }; - - /* " ", "%", %00-%1F */ - - static uint32_t memcached[] = { - 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ - - /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ - 0x00000021, /* 0000 0000 0000 0000 0000 0000 0010 0001 */ - - /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ - 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ - - /* ~}| {zyx wvut srqp onml kjih gfed cba` */ - 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ - - 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ - 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ - 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ - 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ - }; - - /* mail_auth is the same as memcached */ - - static uint32_t *map[] = - { uri, args, uri_component, html, refresh, memcached, memcached }; - - - escape = map[type]; - - if (dst == NULL) { - - /* find the number of the characters to be escaped */ - - n = 0; - - while (size) { - if (escape[*src >> 5] & (1U << (*src & 0x1f))) { - n++; - } - src++; - size--; - } - - return (uintptr_t) n; - } - - while (size) { - if (escape[*src >> 5] & (1U << (*src & 0x1f))) { - *dst++ = '%'; - *dst++ = hex[*src >> 4]; - *dst++ = hex[*src & 0xf]; - src++; - - } else { - *dst++ = *src++; - } - size--; - } - - return (uintptr_t) dst; -} - - -void ngx_unescape_uri(u_char **dst, u_char **src, size_t size, unsigned int type) -{ - u_char *d, *s, ch, c, decoded; - enum { - sw_usual = 0, - sw_quoted, - sw_quoted_second - } state; - - d = *dst; - s = *src; - - state = 0; - decoded = 0; - - while (size--) { - - ch = *s++; - - switch (state) { - case sw_usual: - if (ch == '?' - && (type & (NGX_UNESCAPE_URI | NGX_UNESCAPE_REDIRECT))) { - *d++ = ch; - goto done; - } - - if (ch == '%') { - state = sw_quoted; - break; - } - - *d++ = ch; - break; - - case sw_quoted: - - if (ch >= '0' && ch <= '9') { - decoded = (u_char) (ch - '0'); - state = sw_quoted_second; - break; - } - - c = (u_char) (ch | 0x20); - if (c >= 'a' && c <= 'f') { - decoded = (u_char) (c - 'a' + 10); - state = sw_quoted_second; - break; - } - - /* the invalid quoted character */ - - state = sw_usual; - - *d++ = ch; - - break; - - case sw_quoted_second: - - state = sw_usual; - - if (ch >= '0' && ch <= '9') { - ch = (u_char) ((decoded << 4) + (ch - '0')); - - if (type & NGX_UNESCAPE_REDIRECT) { - if (ch > '%' && ch < 0x7f) { - *d++ = ch; - break; - } - - *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); - - break; - } - - *d++ = ch; - - break; - } - - c = (u_char) (ch | 0x20); - if (c >= 'a' && c <= 'f') { - ch = (u_char) ((decoded << 4) + (c - 'a') + 10); - - if (type & NGX_UNESCAPE_URI) { - if (ch == '?') { - *d++ = ch; - goto done; - } - - *d++ = ch; - break; - } - - if (type & NGX_UNESCAPE_REDIRECT) { - if (ch == '?') { - *d++ = ch; - goto done; - } - - if (ch > '%' && ch < 0x7f) { - *d++ = ch; - break; - } - - *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); - break; - } - - *d++ = ch; - - break; - } - - /* the invalid quoted character */ - - break; - } - } - -done: - - *dst = d; - *src = s; -} - - -uint32_t example_uri_encode(char *dest, const char *src, size_t len) -{ - if (!src || !dest) { - return 0; - } - - uintptr_t ret = ngx_escape_uri((unsigned char *)dest, (unsigned char *)src, len, NGX_ESCAPE_URI_COMPONENT); - return (uint32_t)(ret - (uintptr_t)dest); -} - - -void example_uri_decode(char *dest, const char *src, size_t len) -{ - if (!src || !dest) { - return; - } - - unsigned char *src_ptr = (unsigned char *)src; - unsigned char *dst_ptr = (unsigned char *)dest; - ngx_unescape_uri(&dst_ptr, &src_ptr, len, NGX_UNESCAPE_URI); -} diff --git a/components/protocol_examples_common/protocol_examples_utils.c:Zone.Identifier b/components/protocol_examples_common/protocol_examples_utils.c:Zone.Identifier deleted file mode 100644 index d6c1ec682968c796b9f5e9e080cc6f674b57c766..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2xdl#JyUFr831@K2x - -#include -#include -#include -#include -#include -#include -#include - -static TaskHandle_t s_ot_task_handle = NULL; -static esp_netif_t *s_openthread_netif = NULL; -static SemaphoreHandle_t s_semph_thread_attached = NULL; -static SemaphoreHandle_t s_semph_thread_set_dns_server = NULL; -static const char *TAG = "example_connect"; - -static void thread_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, - void* event_data) -{ - if (event_base == OPENTHREAD_EVENT) { - if (event_id == OPENTHREAD_EVENT_ATTACHED) { - xSemaphoreGive(s_semph_thread_attached); - } else if (event_id == OPENTHREAD_EVENT_SET_DNS_SERVER) { - xSemaphoreGive(s_semph_thread_set_dns_server); - } - } -} - -static void ot_task_worker(void *aContext) -{ - esp_openthread_platform_config_t config = { - .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), - .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), - .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), - }; - - esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_OPENTHREAD(); - esp_netif_config.if_desc = EXAMPLE_NETIF_DESC_THREAD; - esp_netif_config_t cfg = { - .base = &esp_netif_config, - .stack = &g_esp_netif_netstack_default_openthread, - }; - s_openthread_netif = esp_netif_new(&cfg); - assert(s_openthread_netif != NULL); - - // Initialize the OpenThread stack - ESP_ERROR_CHECK(esp_openthread_init(&config)); - ESP_ERROR_CHECK(esp_netif_attach(s_openthread_netif, esp_openthread_netif_glue_init(&config))); - esp_openthread_lock_acquire(portMAX_DELAY); - (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); - esp_openthread_cli_init(); - esp_openthread_cli_create_task(); - otOperationalDatasetTlvs dataset; - otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); - if (error != OT_ERROR_NONE) { - ESP_ERROR_CHECK(esp_openthread_auto_start(NULL)); - } else { - ESP_ERROR_CHECK(esp_openthread_auto_start(&dataset)); - } - esp_openthread_lock_release(); - - // Run the main loop - esp_openthread_launch_mainloop(); - - // Clean up - esp_openthread_netif_glue_deinit(); - esp_netif_destroy(s_openthread_netif); - esp_vfs_eventfd_unregister(); - vTaskDelete(NULL); -} - -/* tear down connection, release resources */ -void example_thread_shutdown(void) -{ - vTaskDelete(s_ot_task_handle); - esp_openthread_netif_glue_deinit(); - esp_netif_destroy(s_openthread_netif); - esp_vfs_eventfd_unregister(); - vSemaphoreDelete(s_semph_thread_set_dns_server); - vSemaphoreDelete(s_semph_thread_attached); -} - -esp_err_t example_thread_connect(void) -{ - s_semph_thread_attached = xSemaphoreCreateBinary(); - if (s_semph_thread_attached == NULL) { - return ESP_ERR_NO_MEM; - } - s_semph_thread_set_dns_server = xSemaphoreCreateBinary(); - if (s_semph_thread_set_dns_server == NULL) { - vSemaphoreDelete(s_semph_thread_attached); - return ESP_ERR_NO_MEM; - } - // 4 eventfds might be used for Thread - // * netif - // * ot task queue - // * radio driver - // * border router - esp_vfs_eventfd_config_t eventfd_config = { - .max_fds = 4, - }; - esp_vfs_eventfd_register(&eventfd_config); - ESP_ERROR_CHECK(esp_event_handler_register(OPENTHREAD_EVENT, ESP_EVENT_ANY_ID, thread_event_handler, NULL)); - if (xTaskCreate(ot_task_worker, "ot_br_main", CONFIG_EXAMPLE_THREAD_TASK_STACK_SIZE, NULL, 5, &s_ot_task_handle) != pdPASS) { - vSemaphoreDelete(s_semph_thread_attached); - vSemaphoreDelete(s_semph_thread_set_dns_server); - ESP_LOGE(TAG, "Failed to create openthread task"); - return ESP_FAIL; - } - xSemaphoreTake(s_semph_thread_attached, portMAX_DELAY); - // Wait 1s for the Thread device to set its DNS server with the NAT64 prefix. - if (xSemaphoreTake(s_semph_thread_set_dns_server, 1000 / portTICK_PERIOD_MS) != pdPASS) { - ESP_LOGW(TAG, "DNS server is not set for the Thread device, might be unable to access the Internet"); - } - return ESP_OK; -} diff --git a/components/protocol_examples_common/thread_connect.c:Zone.Identifier b/components/protocol_examples_common/thread_connect.c:Zone.Identifier deleted file mode 100644 index d6c1ec682968c796b9f5e9e080cc6f674b57c766..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2x -#include "protocol_examples_common.h" -#include "example_common_private.h" -#include "esp_log.h" - -#if CONFIG_EXAMPLE_CONNECT_WIFI - -static const char *TAG = "example_connect"; -static esp_netif_t *s_example_sta_netif = NULL; -static SemaphoreHandle_t s_semph_get_ip_addrs = NULL; -#if CONFIG_EXAMPLE_CONNECT_IPV6 -static SemaphoreHandle_t s_semph_get_ip6_addrs = NULL; -#endif - -static int s_retry_num = 0; - -static void example_handler_on_wifi_disconnect(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) -{ - s_retry_num++; - if (s_retry_num > CONFIG_EXAMPLE_WIFI_CONN_MAX_RETRY) { - ESP_LOGI(TAG, "WiFi Connect failed %d times, stop reconnect.", s_retry_num); - /* let example_wifi_sta_do_connect() return */ - if (s_semph_get_ip_addrs) { - xSemaphoreGive(s_semph_get_ip_addrs); - } -#if CONFIG_EXAMPLE_CONNECT_IPV6 - if (s_semph_get_ip6_addrs) { - xSemaphoreGive(s_semph_get_ip6_addrs); - } -#endif - example_wifi_sta_do_disconnect(); - return; - } - wifi_event_sta_disconnected_t *disconn = event_data; - if (disconn->reason == WIFI_REASON_ROAMING) { - ESP_LOGD(TAG, "station roaming, do nothing"); - return; - } - ESP_LOGI(TAG, "Wi-Fi disconnected %d, trying to reconnect...", disconn->reason); - esp_err_t err = esp_wifi_connect(); - if (err == ESP_ERR_WIFI_NOT_STARTED) { - return; - } - ESP_ERROR_CHECK(err); -} - -static void example_handler_on_wifi_connect(void *esp_netif, esp_event_base_t event_base, - int32_t event_id, void *event_data) -{ -#if CONFIG_EXAMPLE_CONNECT_IPV6 - esp_netif_create_ip6_linklocal(esp_netif); -#endif // CONFIG_EXAMPLE_CONNECT_IPV6 -} - -static void example_handler_on_sta_got_ip(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) -{ - s_retry_num = 0; - ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; - if (!example_is_our_netif(EXAMPLE_NETIF_DESC_STA, event->esp_netif)) { - return; - } - ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); - if (s_semph_get_ip_addrs) { - xSemaphoreGive(s_semph_get_ip_addrs); - } else { - ESP_LOGI(TAG, "- IPv4 address: " IPSTR ",", IP2STR(&event->ip_info.ip)); - } -} - -#if CONFIG_EXAMPLE_CONNECT_IPV6 -static void example_handler_on_sta_got_ipv6(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) -{ - ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; - if (!example_is_our_netif(EXAMPLE_NETIF_DESC_STA, event->esp_netif)) { - return; - } - esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip); - ESP_LOGI(TAG, "Got IPv6 event: Interface \"%s\" address: " IPV6STR ", type: %s", esp_netif_get_desc(event->esp_netif), - IPV62STR(event->ip6_info.ip), example_ipv6_addr_types_to_str[ipv6_type]); - - if (ipv6_type == EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) { - if (s_semph_get_ip6_addrs) { - xSemaphoreGive(s_semph_get_ip6_addrs); - } else { - ESP_LOGI(TAG, "- IPv6 address: " IPV6STR ", type: %s", IPV62STR(event->ip6_info.ip), example_ipv6_addr_types_to_str[ipv6_type]); - } - } -} -#endif // CONFIG_EXAMPLE_CONNECT_IPV6 - - -void example_wifi_start(void) -{ - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA(); - // Warning: the interface desc is used in tests to capture actual connection details (IP, gw, mask) - esp_netif_config.if_desc = EXAMPLE_NETIF_DESC_STA; - esp_netif_config.route_prio = 128; - s_example_sta_netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config); - esp_wifi_set_default_wifi_sta_handlers(); - - ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); - ESP_ERROR_CHECK(esp_wifi_start()); -} - - -void example_wifi_stop(void) -{ - esp_err_t err = esp_wifi_stop(); - if (err == ESP_ERR_WIFI_NOT_INIT) { - return; - } - ESP_ERROR_CHECK(err); - ESP_ERROR_CHECK(esp_wifi_deinit()); - ESP_ERROR_CHECK(esp_wifi_clear_default_wifi_driver_and_handlers(s_example_sta_netif)); - esp_netif_destroy(s_example_sta_netif); - s_example_sta_netif = NULL; -} - - -esp_err_t example_wifi_sta_do_connect(wifi_config_t wifi_config, bool wait) -{ - if (wait) { - s_semph_get_ip_addrs = xSemaphoreCreateBinary(); - if (s_semph_get_ip_addrs == NULL) { - return ESP_ERR_NO_MEM; - } -#if CONFIG_EXAMPLE_CONNECT_IPV6 - s_semph_get_ip6_addrs = xSemaphoreCreateBinary(); - if (s_semph_get_ip6_addrs == NULL) { - vSemaphoreDelete(s_semph_get_ip_addrs); - return ESP_ERR_NO_MEM; - } -#endif - } - s_retry_num = 0; - ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &example_handler_on_wifi_disconnect, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &example_handler_on_sta_got_ip, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &example_handler_on_wifi_connect, s_example_sta_netif)); -#if CONFIG_EXAMPLE_CONNECT_IPV6 - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &example_handler_on_sta_got_ipv6, NULL)); -#endif - - ESP_LOGI(TAG, "Connecting to %s...", wifi_config.sta.ssid); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); - esp_err_t ret = esp_wifi_connect(); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "WiFi connect failed! ret:%x", ret); - return ret; - } - if (wait) { - ESP_LOGI(TAG, "Waiting for IP(s)"); -#if CONFIG_EXAMPLE_CONNECT_IPV4 - xSemaphoreTake(s_semph_get_ip_addrs, portMAX_DELAY); - vSemaphoreDelete(s_semph_get_ip_addrs); - s_semph_get_ip_addrs = NULL; -#endif -#if CONFIG_EXAMPLE_CONNECT_IPV6 - xSemaphoreTake(s_semph_get_ip6_addrs, portMAX_DELAY); - vSemaphoreDelete(s_semph_get_ip6_addrs); - s_semph_get_ip6_addrs = NULL; -#endif - if (s_retry_num > CONFIG_EXAMPLE_WIFI_CONN_MAX_RETRY) { - return ESP_FAIL; - } - } - return ESP_OK; -} - -esp_err_t example_wifi_sta_do_disconnect(void) -{ - ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &example_handler_on_wifi_disconnect)); - ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &example_handler_on_sta_got_ip)); - ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &example_handler_on_wifi_connect)); -#if CONFIG_EXAMPLE_CONNECT_IPV6 - ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &example_handler_on_sta_got_ipv6)); -#endif - return esp_wifi_disconnect(); -} - -void example_wifi_shutdown(void) -{ - example_wifi_sta_do_disconnect(); - example_wifi_stop(); -} - -esp_err_t example_wifi_connect(void) -{ - ESP_LOGI(TAG, "Start example_connect."); - example_wifi_start(); - wifi_config_t wifi_config = { - .sta = { -#if !CONFIG_EXAMPLE_WIFI_SSID_PWD_FROM_STDIN - .ssid = CONFIG_EXAMPLE_WIFI_SSID, - .password = CONFIG_EXAMPLE_WIFI_PASSWORD, -#endif - .scan_method = EXAMPLE_WIFI_SCAN_METHOD, - .sort_method = EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD, - .threshold.rssi = CONFIG_EXAMPLE_WIFI_SCAN_RSSI_THRESHOLD, - .threshold.authmode = EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD, - }, - }; -#if CONFIG_EXAMPLE_WIFI_SSID_PWD_FROM_STDIN - example_configure_stdin_stdout(); - char buf[sizeof(wifi_config.sta.ssid)+sizeof(wifi_config.sta.password)+2] = {0}; - ESP_LOGI(TAG, "Please input ssid password:"); - fgets(buf, sizeof(buf), stdin); - int len = strlen(buf); - buf[len-1] = '\0'; /* removes '\n' */ - memset(wifi_config.sta.ssid, 0, sizeof(wifi_config.sta.ssid)); - - char *rest = NULL; - char *temp = strtok_r(buf, " ", &rest); - strncpy((char*)wifi_config.sta.ssid, temp, sizeof(wifi_config.sta.ssid)); - memset(wifi_config.sta.password, 0, sizeof(wifi_config.sta.password)); - temp = strtok_r(NULL, " ", &rest); - if (temp) { - strncpy((char*)wifi_config.sta.password, temp, sizeof(wifi_config.sta.password)); - } else { - wifi_config.sta.threshold.authmode = WIFI_AUTH_OPEN; - } -#endif - return example_wifi_sta_do_connect(wifi_config, true); -} - - -#endif /* CONFIG_EXAMPLE_CONNECT_WIFI */ diff --git a/components/protocol_examples_common/wifi_connect.c:Zone.Identifier b/components/protocol_examples_common/wifi_connect.c:Zone.Identifier deleted file mode 100644 index d6c1ec682968c796b9f5e9e080cc6f674b57c766..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2x 适用对象:现场测试、家人用户、非开发人员 +> 目标:让设备快速连上家里 Wi-Fi + +--- + +## 1. 开始前准备 + +- 手机已打开 Wi-Fi +- 记住家里 Wi-Fi 名称和密码 +- 设备已上电 + +--- + +## 2. 进入配网模式 + +有两种工作模式,请按项目配置使用: + +1. 按键触发模式:长按设备上的配网按键(约 2 秒) +2. 常驻配网模式:设备上电后会自动开启配网 +3. 当看到“配网已启动”后,手机 Wi-Fi 列表会出现:`ESP32-xxxxxx` + +--- + +## 3. 手机连接设备热点 + +1. 在手机 Wi-Fi 中连接 `ESP32-xxxxxx` +2. 若系统自动弹出页面,直接进入下一步 +3. 若没有自动弹出,手动打开浏览器输入: + +`http://192.168.4.1` + +> 注意:必须是 `http`,不要用 `https` + +--- + +## 4. 选择路由器并提交 + +1. 点击“扫描网络” +2. 选择你家的 Wi-Fi +3. 输入 Wi-Fi 密码 +4. 点击“连接” + +成功后页面会提示连接成功: + +- 按键触发模式:设备热点会在几秒后自动关闭(正常现象) +- 常驻配网模式:设备热点保持开启(正常现象) + +--- + +## 5. 成功后的现象 + +- 设备不再广播 `ESP32-xxxxxx` +- 串口会显示“联网成功,获取 IP=... ” +- 设备进入正常工作状态 + +--- + +## 6. 常见问题 + +### Q1:手机连上热点但打不开页面 + +- 手动访问:`http://192.168.4.1` +- 关闭手机“私人 DNS / 智能网络切换 / VPN”后重试 +- 确认没有强制跳 HTTPS + +### Q2:提示连接失败 + +- 检查 Wi-Fi 密码是否正确 +- 确认路由器 2.4G 可用(ESP32-C3 使用 2.4G) +- 路由器信号太弱时,靠近路由器后重试 + +### Q3:配网成功后热点消失了 + +- 这是正常设计:设备连上路由器后会自动关闭配网热点 + +--- + +## 7. 一句话速记 + +长按按键 → 连 `ESP32-xxxxxx` → 打开 `http://192.168.4.1` → 选 Wi-Fi + 输密码 → 等待成功提示。 diff --git a/components/protocol_examples_common/addr_from_stdin.c:Zone.Identifier b/components/wifi-connect/USER_GUIDE.md:Zone.Identifier similarity index 100% rename from components/protocol_examples_common/addr_from_stdin.c:Zone.Identifier rename to components/wifi-connect/USER_GUIDE.md:Zone.Identifier diff --git a/components/wifi-connect/include/wifi-connect.h b/components/wifi-connect/include/wifi-connect.h new file mode 100644 index 0000000..37a325f --- /dev/null +++ b/components/wifi-connect/include/wifi-connect.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + WIFI_CONNECT_STATUS_IDLE = 0, + WIFI_CONNECT_STATUS_PROVISIONING, + WIFI_CONNECT_STATUS_CONNECTING, + WIFI_CONNECT_STATUS_CONNECTED, + WIFI_CONNECT_STATUS_FAILED, + WIFI_CONNECT_STATUS_TIMEOUT, +} wifi_connect_status_t; + +typedef struct { + bool has_config; + char ssid[33]; + char password[65]; +} wifi_connect_config_t; + +esp_err_t wifi_connect_init(void); +esp_err_t wifi_connect_start(void); +esp_err_t wifi_connect_stop(void); +wifi_connect_status_t wifi_connect_get_status(void); +esp_err_t wifi_connect_get_config(wifi_connect_config_t *config); +esp_err_t wifi_connect_clear_config(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/protocol_examples_common/connect.c:Zone.Identifier b/components/wifi-connect/include/wifi-connect.h:Zone.Identifier similarity index 100% rename from components/protocol_examples_common/connect.c:Zone.Identifier rename to components/wifi-connect/include/wifi-connect.h:Zone.Identifier diff --git a/components/wifi-connect/wifi-connect.c b/components/wifi-connect/wifi-connect.c new file mode 100644 index 0000000..fdc26a5 --- /dev/null +++ b/components/wifi-connect/wifi-connect.c @@ -0,0 +1,1618 @@ +#include + +/* + * 轻量级 ESP32 Wi-Fi 配网组件 + * + * 【配网流程总览】 + * 1. 启动阶段: + * - 初始化 NVS、网络栈、Wi-Fi 驱动、事件循环。 + * - 根据 Kconfig 选择“自动配网”或“按键触发配网”模式。 + * - 自动配网模式下,开机即启动 AP 热点和 HTTP 配网页面。 + * - 按键模式下,长按指定 GPIO 进入配网。 + * + * 2. 手机连接 ESP32 热点: + * - 手机 Wi-Fi 连接 ESP32-xxxxxx 热点,自动跳转到 http://192.168.4.1 配网页面。 + * - 支持 DNS 劫持,任意域名都跳转到配网页面。 + * + * 3. 配网页面操作: + * - 用户扫描附近 Wi-Fi,选择目标路由器并输入密码,点击“连接”。 + * - ESP32 断开当前 STA,配置新 Wi-Fi 并尝试连接。 + * - 连接成功后保存凭据到 NVS。 + * - 自动配网模式下,连接成功后热点/AP会延迟关闭,仅保留 STA 联网。 + * + * 4. 失败与重试: + * - 若连接超时或失败,前端页面显示错误,用户可重试。 + * - 支持清除已保存 Wi-Fi 配置,恢复配网模式。 + * + * 5. 事件与状态管理: + * - 通过事件回调处理 Wi-Fi 连接、断开、获取 IP 等状态。 + * - 通过 HTTP API 实时反馈配网状态给前端页面。 + * + * 适用场景: + * - 适合物联网设备首次部署、Wi-Fi 变更、远程维护等场景。 + * - 支持多种配网入口,兼容绝大多数手机和路由器。 + * + * 主要文件结构: + * - wifi_connect_init:初始化配网组件 + * - wifi_connect_start/stop:启动/停止配网热点与服务 + * - HTTP handler:处理网页扫描、连接、状态、清除等接口 + * - 事件回调:处理 Wi-Fi/IP 事件,管理状态机 + * - NVS 操作:保存/读取/清除 Wi-Fi 凭据 + */ +#include +#include +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#include "driver/gpio.h" +#include "esp_check.h" +#include "esp_event.h" +#include "esp_http_server.h" +#include "esp_log.h" +#include "esp_mac.h" +#include "esp_netif.h" +#include "esp_netif_ip_addr.h" +#include "esp_timer.h" +#include "esp_wifi.h" +#include "nvs.h" +#include "nvs_flash.h" + +#include "lwip/sockets.h" +#include "lwip/inet.h" + +#include "wifi-connect.h" + +/* + * wifi-connect.c + * + * 简要说明: + * 该文件实现了一个轻量的 Wi-Fi 配网模块,包含: + * - 在设备上开启一个 AP(热点)和内嵌的 HTTP 配网页面,供手机扫码/连接后配置目标路由器SSID/密码; + * - DNS 劫持(将所有域名解析到设备),便于在手机打开任意地址时跳转到配网页面; + * - 本地保存/清除 Wi-Fi 凭据(NVS); + * - 支持按键长按进入配网或常驻配网模式(由 Kconfig 控制); + * - 自动尝试连接已保存的网络并在成功后可选择关闭热点。 + * + * 代码中大量使用了 ESP-IDF 的事件、定时器、FreeRTOS 任务与 NVS。 + */ + +#define WIFI_CONNECT_NVS_NAMESPACE "wifi_connect" +#define WIFI_CONNECT_NVS_KEY_SSID "ssid" +#define WIFI_CONNECT_NVS_KEY_PASS "pass" + +#define WIFI_CONNECT_HTTP_BUF_SIZE 256 + +#ifndef CONFIG_WIFI_CONNECT_BUTTON_STARTUP_GUARD_MS +#define CONFIG_WIFI_CONNECT_BUTTON_STARTUP_GUARD_MS 5000 +#endif + +#ifndef CONFIG_WIFI_CONNECT_BUTTON_RELEASE_ARM_MS +#define CONFIG_WIFI_CONNECT_BUTTON_RELEASE_ARM_MS 200 +#endif + +static const char *TAG = "wifi_connect"; + + +// 判断当前是否为“按键触发配网”模式 +// 若 Kconfig 选择了 BUTTON,则返回 true,否则 false +static inline bool wifi_connect_is_button_mode(void) +{ +#if CONFIG_WIFI_CONNECT_PROVISION_MODE_BUTTON + return true; +#else + return false; +#endif +} + +/* + * Kconfig 帮助函数:判断当前是否配置为自动配网模式(上电自动开启,连接后自动关闭) + */ + +// 判断当前是否为“自动配网(连接后关闭)”模式 +// 若 Kconfig 选择了 AUTO,则返回 true,否则 false +static inline bool wifi_connect_is_auto_mode(void) +{ +#if CONFIG_WIFI_CONNECT_PROVISION_MODE_AUTO + return true; +#else + return false; +#endif +} + +// 日志辅助函数(info) +// 统一输出带“状态”标签的 info 日志,便于串口快速定位配网流程 +static void wifi_connect_log_state_i(const char *state, const char *detail) +{ + if (detail != NULL && detail[0] != '\0') + { + ESP_LOGI(TAG, "【状态】%s:%s", state, detail); + } + else + { + ESP_LOGI(TAG, "【状态】%s", state); + } +} + +// 日志辅助函数(warn) +static void wifi_connect_log_state_w(const char *state, const char *detail) +{ + if (detail != NULL && detail[0] != '\0') + { + ESP_LOGW(TAG, "【状态】%s:%s", state, detail); + } + else + { + ESP_LOGW(TAG, "【状态】%s", state); + } +} + +// 日志辅助函数(error) +static void wifi_connect_log_state_e(const char *state, const char *detail) +{ + if (detail != NULL && detail[0] != '\0') + { + ESP_LOGE(TAG, "【状态】%s:%s", state, detail); + } + else + { + ESP_LOGE(TAG, "【状态】%s", state); + } +} + +/* + * 日志辅助函数(info/warn/error) + * 统一输出带中文“状态”标签的日志,便于在串口日志中快速定位配网流程状态。 + */ + +// 配网全局上下文结构体 +// 保存配网状态、资源句柄、互斥锁、定时器等 +typedef struct +{ + // 当前配网组件运行状态 + wifi_connect_status_t status; + bool initialized; + bool wifi_started; + bool provisioning_active; + bool sta_connected; + bool sta_connect_requested; + bool auto_connecting; + + esp_netif_t *sta_netif; + esp_netif_t *ap_netif; + httpd_handle_t http_server; + esp_event_handler_instance_t wifi_event_instance; + esp_event_handler_instance_t ip_event_instance; + + TaskHandle_t button_task; + TaskHandle_t dns_task; + + SemaphoreHandle_t lock; + + esp_timer_handle_t connect_timer; + esp_timer_handle_t idle_timer; + esp_timer_handle_t ap_stop_timer; + + int dns_sock; + bool dns_running; + + char ap_ssid[32]; + char pending_ssid[33]; + char pending_password[65]; + char last_error[96]; +} wifi_connect_ctx_t; + +/* + * 全局上下文结构体 `wifi_connect_ctx_t`: + * - 保存配网组件的运行状态(是否初始化、是否已启动 Wi-Fi、热点 SSID、待连接凭据等); + * - 存放各类资源句柄(netif、http server、event 句柄、定时器、任务句柄、互斥锁等); + * - 该结构仅在内部使用,并通过互斥锁保护对关键字段的并发访问。 + */ + +// 全局唯一配网上下文实例 +static wifi_connect_ctx_t s_ctx = { + .status = WIFI_CONNECT_STATUS_IDLE, + .dns_sock = -1, +}; + +// 配网页面(内嵌 HTML + JS) +static const char *s_html_page = + "" + "" + "ESP32 Wi-Fi Setup" + "
" + "

连接 Wi-Fi

请选择网络并输入密码。

" + "
" + "" + "" + "
" + "
" + "
"; + + /* + * 内嵌的配网页面 HTML/JS(s_html_page)说明: + * - 页面提供:扫描可用网络、输入密码、提交连接请求、清除保存项、轮询状态等功能; + * - 前端与设备通过 `/api/` HTTP 接口交互(详见下方的 http handler 实现); + * - 为了简洁将页面作为 C 字符串内嵌,便于无需额外文件即可启动配网服务。 + */ + +// 线程安全设置配网状态(需持有互斥锁) +static void wifi_connect_set_status_locked(wifi_connect_status_t status) +{ + s_ctx.status = status; +} + +/* + * 线程安全的状态/错误设置函数: + * - 这些函数假定调用者已持有 s_ctx.lock(互斥锁),用于在多任务环境下安全更新状态与错误消息。 + */ + +// 线程安全设置错误信息(需持有互斥锁) +static void wifi_connect_set_error_locked(const char *message) +{ + if (message == NULL) + { + s_ctx.last_error[0] = '\0'; + return; + } + snprintf(s_ctx.last_error, sizeof(s_ctx.last_error), "%s", message); +} + +// 若为自动配网模式,重置/启动空闲超时定时器,长时间无操作自动关闭热点 +static void wifi_connect_refresh_idle_timeout(void) +{ + // 始终开启配网模式已移除 + if (s_ctx.idle_timer == NULL) + { + return; + } + esp_timer_stop(s_ctx.idle_timer); + esp_timer_start_once(s_ctx.idle_timer, (uint64_t)CONFIG_WIFI_CONNECT_IDLE_TIMEOUT_SEC * 1000000ULL); +} + +/* + * 若组件处于非常驻配网模式,则重置/启动空闲超时定时器: + * - 当长时间无操作时会触发 `wifi_connect_idle_timeout_cb` 以自动关闭配网热点,节省电量。 + */ + +// 保存 Wi-Fi 凭据到 NVS +static esp_err_t wifi_connect_save_credentials(const char *ssid, const char *password) +{ + nvs_handle_t handle; + ESP_RETURN_ON_ERROR(nvs_open(WIFI_CONNECT_NVS_NAMESPACE, NVS_READWRITE, &handle), TAG, "open nvs failed"); + esp_err_t err = nvs_set_str(handle, WIFI_CONNECT_NVS_KEY_SSID, ssid); + if (err == ESP_OK) + { + err = nvs_set_str(handle, WIFI_CONNECT_NVS_KEY_PASS, password); + } + if (err == ESP_OK) + { + err = nvs_commit(handle); + } + nvs_close(handle); + return err; +} + +/* + * NVS 操作:保存/清除/读取 Wi-Fi 凭据 + * - `wifi_connect_save_credentials` 将 ssid/password 写入 NVS 命名空间; + * - `wifi_connect_clear_config` 删除保存的项并在内部同步清理运行时状态; + * - `wifi_connect_get_config` 从 NVS 读取并填充结构返回是否存在配置。 + */ + +// 清除已保存的 Wi-Fi 配置(NVS),并同步清理运行时状态 +esp_err_t wifi_connect_clear_config(void) +{ + nvs_handle_t handle; + ESP_RETURN_ON_ERROR(nvs_open(WIFI_CONNECT_NVS_NAMESPACE, NVS_READWRITE, &handle), TAG, "open nvs failed"); + + esp_err_t err_ssid = nvs_erase_key(handle, WIFI_CONNECT_NVS_KEY_SSID); + esp_err_t err_pass = nvs_erase_key(handle, WIFI_CONNECT_NVS_KEY_PASS); + + if (err_ssid != ESP_OK && err_ssid != ESP_ERR_NVS_NOT_FOUND) + { + nvs_close(handle); + return err_ssid; + } + if (err_pass != ESP_OK && err_pass != ESP_ERR_NVS_NOT_FOUND) + { + nvs_close(handle); + return err_pass; + } + + esp_err_t err = nvs_commit(handle); + nvs_close(handle); + if (err != ESP_OK) + { + return err; + } + + if (s_ctx.initialized && s_ctx.lock != NULL) + { + bool should_disconnect = false; + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + s_ctx.pending_ssid[0] = '\0'; + s_ctx.pending_password[0] = '\0'; + wifi_connect_set_error_locked(NULL); + if (s_ctx.provisioning_active) + { + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_PROVISIONING); + } + if (s_ctx.status == WIFI_CONNECT_STATUS_CONNECTING) + { + s_ctx.sta_connect_requested = false; + s_ctx.auto_connecting = false; + wifi_connect_set_status_locked(s_ctx.provisioning_active ? WIFI_CONNECT_STATUS_PROVISIONING : WIFI_CONNECT_STATUS_IDLE); + should_disconnect = true; + } + xSemaphoreGive(s_ctx.lock); + + if (should_disconnect) + { + esp_wifi_disconnect(); + } + } + + wifi_connect_log_state_i("已清除保存的 Wi-Fi 配置", "下次上电将不会自动重连"); + return ESP_OK; +} + +// 读取已保存的 Wi-Fi 配置(NVS),填充到 config 结构体 +esp_err_t wifi_connect_get_config(wifi_connect_config_t *config) +{ + ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, TAG, "config is null"); + memset(config, 0, sizeof(*config)); + + nvs_handle_t handle; + esp_err_t err = nvs_open(WIFI_CONNECT_NVS_NAMESPACE, NVS_READONLY, &handle); + if (err != ESP_OK) + { + return err; + } + + size_t ssid_len = sizeof(config->ssid); + size_t pass_len = sizeof(config->password); + err = nvs_get_str(handle, WIFI_CONNECT_NVS_KEY_SSID, config->ssid, &ssid_len); + if (err == ESP_OK) + { + err = nvs_get_str(handle, WIFI_CONNECT_NVS_KEY_PASS, config->password, &pass_len); + } + nvs_close(handle); + config->has_config = (err == ESP_OK && config->ssid[0] != '\0'); + return err; +} + +// 配网状态枚举转字符串,供前端/日志使用 +static const char *wifi_connect_status_to_string(wifi_connect_status_t status) +{ + switch (status) + { + case WIFI_CONNECT_STATUS_IDLE: + return "idle"; + case WIFI_CONNECT_STATUS_PROVISIONING: + return "provisioning"; + case WIFI_CONNECT_STATUS_CONNECTING: + return "connecting"; + case WIFI_CONNECT_STATUS_CONNECTED: + return "connected"; + case WIFI_CONNECT_STATUS_FAILED: + return "failed"; + case WIFI_CONNECT_STATUS_TIMEOUT: + return "timeout"; + default: + return "unknown"; + } +} + +/* + * 状态转换:将 enum 状态转换为供前端/日志使用的字符串。 + */ + +// 获取当前配网状态(线程安全) +wifi_connect_status_t wifi_connect_get_status(void) +{ + if (!s_ctx.initialized || s_ctx.lock == NULL) + { + return WIFI_CONNECT_STATUS_IDLE; + } + + wifi_connect_status_t status; + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + status = s_ctx.status; + xSemaphoreGive(s_ctx.lock); + return status; +} + +// HTTP JSON 响应辅助函数 +static esp_err_t wifi_connect_send_json(httpd_req_t *req, const char *json) +{ + httpd_resp_set_type(req, "application/json"); + return httpd_resp_sendstr(req, json); +} + +/* + * 简单的 HTTP JSON 响应帮助函数:设置 Content-Type 并发送字符串。 + */ + +// JSON 字符串转义,防止注入和格式破坏 +static void wifi_connect_json_escape(const char *src, char *dst, size_t dst_size) +{ + size_t j = 0; + for (size_t i = 0; src != NULL && src[i] != '\0' && j + 2 < dst_size; ++i) + { + char c = src[i]; + if (c == '"' || c == '\\') + { + dst[j++] = '\\'; + dst[j++] = c; + } + else if ((unsigned char)c >= 32 && (unsigned char)c <= 126) + { + dst[j++] = c; + } + } + dst[j] = '\0'; +} + +/* + * 将字符串转义以安全嵌入 JSON 值中: + * - 转义双引号与反斜杠;仅保留可打印字符(32-126),避免注入或破坏 JSON 格式。 + */ + +// Wi-Fi 加密类型枚举转字符串,供前端显示 +static const char *wifi_connect_auth_to_string(wifi_auth_mode_t auth) +{ + switch (auth) + { + case WIFI_AUTH_OPEN: + return "OPEN"; + case WIFI_AUTH_WEP: + return "WEP"; + case WIFI_AUTH_WPA_PSK: + return "WPA"; + case WIFI_AUTH_WPA2_PSK: + return "WPA2"; + case WIFI_AUTH_WPA_WPA2_PSK: + return "WPA/WPA2"; + case WIFI_AUTH_WPA2_ENTERPRISE: + return "WPA2-ENT"; + case WIFI_AUTH_WPA3_PSK: + return "WPA3"; + case WIFI_AUTH_WPA2_WPA3_PSK: + return "WPA2/WPA3"; + default: + return "UNKNOWN"; + } +} + +/* + * 将 wifi_auth_mode_t 转为可读字符串,供扫描结果返回前端显示。 + */ + +// HTTP handler:扫描附近 Wi-Fi 并返回 JSON 列表 +// 前端页面点击“扫描网络”时触发 +static esp_err_t wifi_connect_http_scan_handler(httpd_req_t *req) +{ + wifi_connect_refresh_idle_timeout(); + + wifi_scan_config_t scan_cfg = { + .show_hidden = false, + }; + esp_err_t err = esp_wifi_scan_start(&scan_cfg, true); + if (err != ESP_OK) + { + char msg[64] = {0}; + snprintf(msg, sizeof(msg), "scan_start failed: %s", esp_err_to_name(err)); + wifi_connect_log_state_w("扫描启动失败", msg); + return wifi_connect_send_json(req, "{\"networks\":[],\"error\":\"scan_failed\"}"); + } + + uint16_t count = CONFIG_WIFI_CONNECT_MAX_SCAN_RESULTS; + wifi_ap_record_t *records = calloc(count, sizeof(wifi_ap_record_t)); + if (records == NULL) + { + return ESP_ERR_NO_MEM; + } + + err = esp_wifi_scan_get_ap_records(&count, records); + if (err != ESP_OK) + { + char msg[64] = {0}; + snprintf(msg, sizeof(msg), "scan_get failed: %s", esp_err_to_name(err)); + wifi_connect_log_state_w("扫描读取失败", msg); + free(records); + return wifi_connect_send_json(req, "{\"networks\":[],\"error\":\"scan_read_failed\"}"); + } + + size_t out_size = 512 + count * 96; + char *out = calloc(1, out_size); + if (out == NULL) + { + free(records); + return ESP_ERR_NO_MEM; + } + + size_t used = snprintf(out, out_size, "{\"networks\":["); + for (uint16_t i = 0; i < count && used + 96 < out_size; ++i) + { + char ssid[65] = {0}; + char escaped[130] = {0}; + snprintf(ssid, sizeof(ssid), "%s", (char *)records[i].ssid); + wifi_connect_json_escape(ssid, escaped, sizeof(escaped)); + used += snprintf(out + used, out_size - used, + "%s{\"ssid\":\"%s\",\"rssi\":%d,\"auth\":\"%s\"}", + (i == 0 ? "" : ","), escaped, records[i].rssi, + wifi_connect_auth_to_string(records[i].authmode)); + } + snprintf(out + used, out_size - used, "]}"); + + err = wifi_connect_send_json(req, out); + free(out); + free(records); + return err; +} + +/* + * HTTP 接口说明(主要处理函数): + * - /api/scan : 扫描附近 Wi-Fi 并返回 JSON 列表; + * - /api/connect : 接收前端提交的 ssid/password,启动连接流程; + * - /api/status : 返回当前配网状态(idle/provisioning/connecting/connected/etc); + * - /api/clear : 清除已保存的 Wi-Fi 配置。 + * + * 具体的处理函数实现见下方各 handler(扫描、连接、状态、清除、index 页面等)。 + */ + +// 从 JSON 字符串中提取指定 key 的字符串值 +static bool wifi_connect_extract_json_string(const char *json, const char *key, char *out, size_t out_len) +{ + char pattern[32]; + snprintf(pattern, sizeof(pattern), "\"%s\":\"", key); + const char *start = strstr(json, pattern); + if (start == NULL) + { + return false; + } + start += strlen(pattern); + size_t idx = 0; + while (*start != '\0' && *start != '"' && idx + 1 < out_len) + { + if (*start == '\\' && *(start + 1) != '\0') + { + start++; + } + out[idx++] = *start++; + } + out[idx] = '\0'; + return idx > 0 || strcmp(key, "password") == 0; +} + +// 应用 STA 配置,断开当前连接,设置新 SSID/密码并连接 +static esp_err_t wifi_connect_apply_sta_credentials(const char *ssid, const char *password) +{ + wifi_config_t sta_cfg = {0}; + snprintf((char *)sta_cfg.sta.ssid, sizeof(sta_cfg.sta.ssid), "%s", ssid); + snprintf((char *)sta_cfg.sta.password, sizeof(sta_cfg.sta.password), "%s", password); + sta_cfg.sta.scan_method = WIFI_FAST_SCAN; + sta_cfg.sta.threshold.authmode = WIFI_AUTH_OPEN; + sta_cfg.sta.pmf_cfg.capable = true; + sta_cfg.sta.pmf_cfg.required = false; + + esp_err_t dis_err = esp_wifi_disconnect(); + if (dis_err != ESP_OK && dis_err != ESP_ERR_WIFI_NOT_CONNECT) + { + char dis_msg[96] = {0}; + snprintf(dis_msg, sizeof(dis_msg), "切换网络前断开 STA 失败,错误=%s", esp_err_to_name(dis_err)); + wifi_connect_log_state_w("预断开当前连接失败", dis_msg); + } + + ESP_RETURN_ON_ERROR(esp_wifi_set_config(WIFI_IF_STA, &sta_cfg), TAG, "set sta config failed"); + ESP_RETURN_ON_ERROR(esp_wifi_connect(), TAG, "wifi connect failed"); + return ESP_OK; +} + +// 自动重连:尝试连接已保存的 Wi-Fi 配置 +static esp_err_t wifi_connect_try_auto_connect(void) +{ + wifi_connect_config_t config = {0}; + esp_err_t err = wifi_connect_get_config(&config); + if (err != ESP_OK || !config.has_config) + { + wifi_connect_log_state_i("未发现已保存的 Wi-Fi 配置", "设备保持待机"); + return ESP_OK; + } + + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + snprintf(s_ctx.pending_ssid, sizeof(s_ctx.pending_ssid), "%s", config.ssid); + snprintf(s_ctx.pending_password, sizeof(s_ctx.pending_password), "%s", config.password); + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_CONNECTING); + wifi_connect_set_error_locked(NULL); + s_ctx.sta_connect_requested = true; + s_ctx.auto_connecting = true; + xSemaphoreGive(s_ctx.lock); + + err = wifi_connect_apply_sta_credentials(config.ssid, config.password); + if (err != ESP_OK) + { + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + s_ctx.sta_connect_requested = false; + s_ctx.auto_connecting = false; + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_IDLE); + wifi_connect_set_error_locked(NULL); + xSemaphoreGive(s_ctx.lock); + char msg[96] = {0}; + snprintf(msg, sizeof(msg), "自动重连启动失败,错误=%s", esp_err_to_name(err)); + wifi_connect_log_state_w("自动重连失败", msg); + return err; + } + + esp_timer_stop(s_ctx.connect_timer); + esp_timer_start_once(s_ctx.connect_timer, (uint64_t)CONFIG_WIFI_CONNECT_CONNECT_TIMEOUT_SEC * 1000000ULL); + char msg[96] = {0}; + snprintf(msg, sizeof(msg), "尝试连接已保存网络:%s", config.ssid); + wifi_connect_log_state_i("自动重连中", msg); + return ESP_OK; +} + +// HTTP handler:处理前端提交的连接请求,启动连接流程 +static esp_err_t wifi_connect_http_connect_handler(httpd_req_t *req) +{ + wifi_connect_refresh_idle_timeout(); + + if (req->content_len <= 0 || req->content_len >= WIFI_CONNECT_HTTP_BUF_SIZE) + { + return wifi_connect_send_json(req, "{\"ok\":false,\"error\":\"invalid_payload\"}"); + } + + char body[WIFI_CONNECT_HTTP_BUF_SIZE] = {0}; + int received = httpd_req_recv(req, body, sizeof(body) - 1); + if (received <= 0) + { + return wifi_connect_send_json(req, "{\"ok\":false,\"error\":\"read_failed\"}"); + } + body[received] = '\0'; + + char ssid[33] = {0}; + char password[65] = {0}; + if (!wifi_connect_extract_json_string(body, "ssid", ssid, sizeof(ssid))) + { + return wifi_connect_send_json(req, "{\"ok\":false,\"error\":\"ssid_missing\"}"); + } + wifi_connect_extract_json_string(body, "password", password, sizeof(password)); + char req_msg[96] = {0}; + snprintf(req_msg, sizeof(req_msg), "收到配网请求,目标网络:%s", ssid); + wifi_connect_log_state_i("开始连接路由器", req_msg); + + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + snprintf(s_ctx.pending_ssid, sizeof(s_ctx.pending_ssid), "%s", ssid); + snprintf(s_ctx.pending_password, sizeof(s_ctx.pending_password), "%s", password); + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_CONNECTING); + wifi_connect_set_error_locked(NULL); + s_ctx.sta_connect_requested = true; + s_ctx.auto_connecting = false; + xSemaphoreGive(s_ctx.lock); + + esp_err_t err = wifi_connect_apply_sta_credentials(ssid, password); + if (err != ESP_OK) + { + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_FAILED); + wifi_connect_set_error_locked("启动连接失败"); + xSemaphoreGive(s_ctx.lock); + char err_msg[96] = {0}; + snprintf(err_msg, sizeof(err_msg), "提交连接失败,错误=%s", esp_err_to_name(err)); + wifi_connect_log_state_w("连接启动失败", err_msg); + return wifi_connect_send_json(req, "{\"ok\":false,\"error\":\"connect_start_failed\"}"); + } + + esp_timer_stop(s_ctx.connect_timer); + esp_timer_start_once(s_ctx.connect_timer, (uint64_t)CONFIG_WIFI_CONNECT_CONNECT_TIMEOUT_SEC * 1000000ULL); + return wifi_connect_send_json(req, "{\"ok\":true}"); +} + +// HTTP handler:返回当前配网状态和错误信息,供前端轮询 +static esp_err_t wifi_connect_http_status_handler(httpd_req_t *req) +{ + wifi_connect_refresh_idle_timeout(); + + wifi_connect_status_t status; + char error[96] = {0}; + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + status = s_ctx.status; + snprintf(error, sizeof(error), "%s", s_ctx.last_error); + xSemaphoreGive(s_ctx.lock); + + char escaped[192] = {0}; + wifi_connect_json_escape(error, escaped, sizeof(escaped)); + + char payload[260]; + snprintf(payload, sizeof(payload), "{\"status\":\"%s\",\"error\":\"%s\"}", + wifi_connect_status_to_string(status), escaped); + return wifi_connect_send_json(req, payload); +} + +// HTTP handler:清除已保存的 Wi-Fi 配置 +static esp_err_t wifi_connect_http_clear_handler(httpd_req_t *req) +{ + wifi_connect_refresh_idle_timeout(); + esp_err_t err = wifi_connect_clear_config(); + if (err != ESP_OK) + { + return wifi_connect_send_json(req, "{\"ok\":false,\"error\":\"clear_failed\"}"); + } + return wifi_connect_send_json(req, "{\"ok\":true}"); +} + +// HTTP handler:返回配网页面 HTML +static esp_err_t wifi_connect_http_index_handler(httpd_req_t *req) +{ + wifi_connect_refresh_idle_timeout(); + httpd_resp_set_type(req, "text/html"); + return httpd_resp_send(req, s_html_page, HTTPD_RESP_USE_STRLEN); +} + +// 获取当前 AP 的 HTTP 访问地址(用于重定向) +static void wifi_connect_get_ap_http_url(char *out, size_t out_len) +{ + esp_netif_ip_info_t ip_info = {0}; + if (s_ctx.ap_netif != NULL && esp_netif_get_ip_info(s_ctx.ap_netif, &ip_info) == ESP_OK) + { + uint32_t ip = ntohl(ip_info.ip.addr); + snprintf(out, out_len, "http://%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 "/", + (ip >> 24) & 0xFF, + (ip >> 16) & 0xFF, + (ip >> 8) & 0xFF, + ip & 0xFF); + return; + } + snprintf(out, out_len, "http://192.168.4.1/"); +} + +// HTTP handler:处理探测/重定向请求,兼容各类手机/系统 +static esp_err_t wifi_connect_http_probe_handler(httpd_req_t *req) +{ + wifi_connect_refresh_idle_timeout(); + char location[48] = {0}; + wifi_connect_get_ap_http_url(location, sizeof(location)); + httpd_resp_set_status(req, "302 Found"); + httpd_resp_set_hdr(req, "Location", location); + httpd_resp_set_hdr(req, "Cache-Control", "no-store"); + return httpd_resp_send(req, NULL, 0); +} + +// 启动 HTTP 服务器,注册所有配网相关接口 +static esp_err_t wifi_connect_http_start(void) +{ + esp_err_t ret = ESP_OK; + + if (s_ctx.http_server != NULL) + { + return ESP_OK; + } + + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.max_uri_handlers = 20; + config.uri_match_fn = httpd_uri_match_wildcard; + config.max_open_sockets = 2; // 配网页面只需处理单个客户端 + ESP_RETURN_ON_ERROR(httpd_start(&s_ctx.http_server, &config), TAG, "start http server failed"); + + const httpd_uri_t index_uri = { + .uri = "/", + .method = HTTP_GET, + .handler = wifi_connect_http_index_handler, + }; + const httpd_uri_t scan_uri = { + .uri = "/api/scan", + .method = HTTP_GET, + .handler = wifi_connect_http_scan_handler, + }; + const httpd_uri_t connect_uri = { + .uri = "/api/connect", + .method = HTTP_POST, + .handler = wifi_connect_http_connect_handler, + }; + const httpd_uri_t status_uri = { + .uri = "/api/status", + .method = HTTP_GET, + .handler = wifi_connect_http_status_handler, + }; + const httpd_uri_t clear_uri = { + .uri = "/api/clear", + .method = HTTP_POST, + .handler = wifi_connect_http_clear_handler, + }; + const httpd_uri_t probe_1 = { + .uri = "/generate_204", + .method = HTTP_GET, + .handler = wifi_connect_http_probe_handler, + }; + const httpd_uri_t probe_2 = { + .uri = "/hotspot-detect.html", + .method = HTTP_GET, + .handler = wifi_connect_http_probe_handler, + }; + const httpd_uri_t probe_3 = { + .uri = "/ncsi.txt", + .method = HTTP_GET, + .handler = wifi_connect_http_probe_handler, + }; + const httpd_uri_t probe_4 = { + .uri = "/connecttest.txt", + .method = HTTP_GET, + .handler = wifi_connect_http_probe_handler, + }; + const httpd_uri_t probe_5 = { + .uri = "/redirect", + .method = HTTP_GET, + .handler = wifi_connect_http_probe_handler, + }; + const httpd_uri_t probe_6 = { + .uri = "/canonical.html", + .method = HTTP_GET, + .handler = wifi_connect_http_probe_handler, + }; + const httpd_uri_t probe_7 = { + .uri = "/mobile/status.php", + .method = HTTP_GET, + .handler = wifi_connect_http_probe_handler, + }; + const httpd_uri_t probe_8 = { + .uri = "/success.txt", + .method = HTTP_GET, + .handler = wifi_connect_http_probe_handler, + }; + const httpd_uri_t probe_9 = { + .uri = "/library/test/success.html", + .method = HTTP_GET, + .handler = wifi_connect_http_probe_handler, + }; + const httpd_uri_t wildcard = { + .uri = "/*", + .method = HTTP_GET, + .handler = wifi_connect_http_probe_handler, + }; + + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &index_uri), fail, TAG, "register / failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &scan_uri), fail, TAG, "register /api/scan failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &connect_uri), fail, TAG, "register /api/connect failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &status_uri), fail, TAG, "register /api/status failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &clear_uri), fail, TAG, "register /api/clear failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &probe_1), fail, TAG, "register /generate_204 failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &probe_2), fail, TAG, "register /hotspot-detect.html failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &probe_3), fail, TAG, "register /ncsi.txt failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &probe_4), fail, TAG, "register /connecttest.txt failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &probe_5), fail, TAG, "register /redirect failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &probe_6), fail, TAG, "register /canonical.html failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &probe_7), fail, TAG, "register /mobile/status.php failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &probe_8), fail, TAG, "register /success.txt failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &probe_9), fail, TAG, "register /library/test/success.html failed"); + ESP_GOTO_ON_ERROR(httpd_register_uri_handler(s_ctx.http_server, &wildcard), fail, TAG, "register wildcard failed"); + + return ESP_OK; + +fail: + httpd_stop(s_ctx.http_server); + s_ctx.http_server = NULL; + return ret; +} + +// 停止 HTTP 服务器 +static void wifi_connect_http_stop(void) +{ + if (s_ctx.http_server != NULL) + { + httpd_stop(s_ctx.http_server); + s_ctx.http_server = NULL; + } +} + +// 构建 DNS 劫持响应,将所有域名解析到本机 AP IP +static size_t wifi_connect_build_dns_response(const uint8_t *req, size_t req_len, uint8_t *resp, size_t resp_max, uint32_t ip_addr) +{ + if (req_len < 12 || resp_max < 64) + { + return 0; + } + + const size_t q_offset = 12; + size_t q_name_end = q_offset; + while (q_name_end < req_len && req[q_name_end] != 0) + { + q_name_end += req[q_name_end] + 1; + } + if (q_name_end + 5 >= req_len) + { + return 0; + } + size_t question_len = (q_name_end + 5) - q_offset; + + resp[0] = req[0]; + resp[1] = req[1]; + resp[2] = 0x81; + resp[3] = 0x80; + resp[4] = 0x00; + resp[5] = 0x01; + resp[6] = 0x00; + resp[7] = 0x01; + resp[8] = 0x00; + resp[9] = 0x00; + resp[10] = 0x00; + resp[11] = 0x00; + + memcpy(&resp[12], &req[q_offset], question_len); + size_t pos = 12 + question_len; + if (pos + 16 > resp_max) + { + return 0; + } + + resp[pos++] = 0xC0; + resp[pos++] = 0x0C; + resp[pos++] = 0x00; + resp[pos++] = 0x01; + resp[pos++] = 0x00; + resp[pos++] = 0x01; + resp[pos++] = 0x00; + resp[pos++] = 0x00; + resp[pos++] = 0x00; + resp[pos++] = 0x3C; + resp[pos++] = 0x00; + resp[pos++] = 0x04; + resp[pos++] = (ip_addr >> 24) & 0xFF; + resp[pos++] = (ip_addr >> 16) & 0xFF; + resp[pos++] = (ip_addr >> 8) & 0xFF; + resp[pos++] = (ip_addr) & 0xFF; + + return pos; +} + +/* + * DNS 劫持:构建 DNS 响应并监听 53 端口 + * - `wifi_connect_build_dns_response` 负责根据请求组装一个标准的 A 记录响应,返回长度; + * - `wifi_connect_dns_task` 创建 UDP socket,绑定 53 端口,循环接收 DNS 查询并返回固定 IP(AP IP),实现劫持效果; + * - 该机制用于在手机打开任意 URL 时将请求导向设备的配网页面。 + */ + +// DNS 劫持任务,监听 53 端口,将所有请求指向配网页面 +static void wifi_connect_dns_task(void *arg) +{ + (void)arg; + uint8_t rx_buf[256]; + uint8_t tx_buf[512]; + + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(53), + .sin_addr.s_addr = htonl(INADDR_ANY), + }; + + s_ctx.dns_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (s_ctx.dns_sock < 0) + { + wifi_connect_log_state_e("DNS 服务启动失败", "创建 socket 失败"); + s_ctx.dns_running = false; + vTaskDelete(NULL); + return; + } + + if (bind(s_ctx.dns_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) + { + char err_msg[96] = {0}; + snprintf(err_msg, sizeof(err_msg), "绑定 53 端口失败,errno=%d", errno); + wifi_connect_log_state_e("DNS 服务启动失败", err_msg); + close(s_ctx.dns_sock); + s_ctx.dns_sock = -1; + s_ctx.dns_running = false; + vTaskDelete(NULL); + return; + } + wifi_connect_log_state_i("DNS 劫持服务已启动", "手机访问任意域名将跳转配网页面"); + + esp_netif_ip_info_t ip_info; + esp_netif_get_ip_info(s_ctx.ap_netif, &ip_info); + uint32_t ip = ntohl(ip_info.ip.addr); + + while (s_ctx.dns_running) + { + struct sockaddr_in from_addr; + socklen_t from_len = sizeof(from_addr); + struct timeval tv = {.tv_sec = 1, .tv_usec = 0}; + setsockopt(s_ctx.dns_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + int len = recvfrom(s_ctx.dns_sock, rx_buf, sizeof(rx_buf), 0, + (struct sockaddr *)&from_addr, &from_len); + if (len <= 0) + { + continue; + } + + size_t resp_len = wifi_connect_build_dns_response(rx_buf, (size_t)len, tx_buf, sizeof(tx_buf), ip); + if (resp_len > 0) + { + sendto(s_ctx.dns_sock, tx_buf, resp_len, 0, (struct sockaddr *)&from_addr, from_len); + } + } + + close(s_ctx.dns_sock); + s_ctx.dns_sock = -1; + vTaskDelete(NULL); +} + +// 启动 DNS 劫持服务 +static esp_err_t wifi_connect_dns_start(void) +{ + if (s_ctx.dns_running) + { + return ESP_OK; + } + s_ctx.dns_running = true; + BaseType_t ok = xTaskCreate(wifi_connect_dns_task, "wifi_dns", 3072, NULL, 4, &s_ctx.dns_task); + if (ok != pdPASS) + { + s_ctx.dns_running = false; + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +/* + * 启动/停止 DNS 劫持服务的包装函数: + * - `wifi_connect_dns_start` 创建接收任务并设置运行标志; + * - `wifi_connect_dns_stop` 停止任务并关闭 socket(通过 shutdown 触发 recvfrom 退出循环)。 + */ + +// 停止 DNS 劫持服务 +static void wifi_connect_dns_stop(void) +{ + if (!s_ctx.dns_running) + { + return; + } + s_ctx.dns_running = false; + if (s_ctx.dns_sock >= 0) + { + shutdown(s_ctx.dns_sock, 0); + } + s_ctx.dns_task = NULL; +} + +// 连接超时定时器回调,处理连接失败/重试逻辑 +static void wifi_connect_connect_timeout_cb(void *arg) +{ + (void)arg; + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + if (s_ctx.status == WIFI_CONNECT_STATUS_CONNECTING) + { + if (s_ctx.auto_connecting) + { + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_IDLE); + wifi_connect_set_error_locked(NULL); + s_ctx.auto_connecting = false; + wifi_connect_log_state_w("自动重连超时", "回到待机状态"); + } + else + { + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_FAILED); + wifi_connect_set_error_locked("连接超时"); + wifi_connect_log_state_w("连接路由器超时", "请确认密码和路由器信号"); + } + s_ctx.sta_connect_requested = false; + esp_wifi_disconnect(); + } + xSemaphoreGive(s_ctx.lock); +} + +/* + * 定时器回调:连接超时 / AP 优雅关闭 / 空闲超时 + * - `wifi_connect_connect_timeout_cb` 在连接阶段超时被调用,根据是否为自动重连调整状态; + * - `wifi_connect_ap_stop_timer_cb` 在配置为短时间优雅关闭时触发停止 AP; + * - `wifi_connect_idle_timeout_cb` 在长时间无用户交互时触发并关闭配网(非常驻模式)。 + */ + +// AP 优雅关闭定时器回调,连接成功后延迟关闭热点 +static void wifi_connect_ap_stop_timer_cb(void *arg) +{ + (void)arg; + // 始终开启配网模式已移除 + wifi_connect_stop(); +} + +// 配网空闲超时定时器回调,长时间无操作自动关闭热点 +static void wifi_connect_idle_timeout_cb(void *arg) +{ + (void)arg; + // 始终开启配网模式已移除 + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + bool should_stop = s_ctx.provisioning_active; + if (should_stop) + { + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_TIMEOUT); + wifi_connect_set_error_locked("配网空闲超时"); + wifi_connect_log_state_w("配网超时", "长时间无操作,正在关闭配网热点"); + } + xSemaphoreGive(s_ctx.lock); + + if (should_stop) + { + wifi_connect_stop(); + } +} + +// Wi-Fi/IP 事件回调,处理连接、断开、获取 IP 等状态 +// 关键:连接成功后保存凭据,失败时反馈错误,自动关闭热点 +static void wifi_connect_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + (void)arg; + // STA 获取到 IP:判定联网成功,并根据配置决定是否关闭配网热点 + + if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) + { + ip_event_got_ip_t *got_ip = (ip_event_got_ip_t *)event_data; + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + bool should_save = (s_ctx.status == WIFI_CONNECT_STATUS_CONNECTING || s_ctx.status == WIFI_CONNECT_STATUS_PROVISIONING); + bool provisioning_active = s_ctx.provisioning_active; + s_ctx.sta_connected = true; + s_ctx.sta_connect_requested = false; + s_ctx.auto_connecting = false; + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_CONNECTED); + wifi_connect_set_error_locked(NULL); + char ssid[33]; + char password[65]; + snprintf(ssid, sizeof(ssid), "%s", s_ctx.pending_ssid); + snprintf(password, sizeof(password), "%s", s_ctx.pending_password); + xSemaphoreGive(s_ctx.lock); + char success_msg[128] = {0}; + snprintf(success_msg, sizeof(success_msg), "已连接 %s,获取 IP=" IPSTR, ssid, IP2STR(&got_ip->ip_info.ip)); + wifi_connect_log_state_i("联网成功", success_msg); + + esp_timer_stop(s_ctx.connect_timer); + if (should_save) + { + esp_err_t err = wifi_connect_save_credentials(ssid, password); + if (err != ESP_OK) + { + char save_msg[96] = {0}; + snprintf(save_msg, sizeof(save_msg), "保存凭据失败,错误=%s", esp_err_to_name(err)); + wifi_connect_log_state_w("保存 Wi-Fi 信息失败", save_msg); + } + } + + if (provisioning_active) + { + if (CONFIG_WIFI_CONNECT_AP_GRACEFUL_STOP_SEC == 0) + { + wifi_connect_stop(); + } + else + { + esp_timer_stop(s_ctx.ap_stop_timer); + esp_timer_start_once(s_ctx.ap_stop_timer, (uint64_t)CONFIG_WIFI_CONNECT_AP_GRACEFUL_STOP_SEC * 1000000ULL); + } + } + return; + } + + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) + { + // 仅在“正在连接”阶段把断开视为失败;避免影响普通联网后的波动处理 + wifi_event_sta_disconnected_t *dis = (wifi_event_sta_disconnected_t *)event_data; + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + bool connecting = (s_ctx.status == WIFI_CONNECT_STATUS_CONNECTING); + bool auto_connecting = s_ctx.auto_connecting; + s_ctx.sta_connected = false; + if (connecting) + { + if (auto_connecting) + { + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_IDLE); + wifi_connect_set_error_locked(NULL); + s_ctx.auto_connecting = false; + char dis_msg[96] = {0}; + snprintf(dis_msg, sizeof(dis_msg), "自动重连断开,原因=%d", dis->reason); + wifi_connect_log_state_w("自动重连中断", dis_msg); + } + else + { + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_FAILED); + snprintf(s_ctx.last_error, sizeof(s_ctx.last_error), "连接失败,原因=%d", dis->reason); + char dis_msg[96] = {0}; + snprintf(dis_msg, sizeof(dis_msg), "连接失败,原因=%d", dis->reason); + wifi_connect_log_state_w("连接路由器失败", dis_msg); + } + s_ctx.sta_connect_requested = false; + esp_timer_stop(s_ctx.connect_timer); + } + xSemaphoreGive(s_ctx.lock); + } +} + +/* + * 事件处理器:处理 Wi-Fi / IP 事件 + * - 监听 `IP_EVENT_STA_GOT_IP`:表示 STA 成功获取到 IP,判定为联网成功; + * 在此处会保存凭据(如是由配网/连接流程触发)、停止连接超时定时器,并根据配置决定是否关闭 AP; + * - 监听 `WIFI_EVENT_STA_DISCONNECTED`:在连接阶段视为失败并记录原因; + * - 通过事件回调可以把底层 wifi 状态与本模块的运行态(s_ctx.status)进行映射与同步。 + */ + +// 生成唯一 AP SSID,便于多设备区分 +static void wifi_connect_generate_ap_ssid(char *out, size_t out_len) +{ + uint8_t mac[6] = {0}; + esp_wifi_get_mac(WIFI_IF_STA, mac); + snprintf(out, out_len, "ESP32-%02X%02X%02X%02X%02X%02X-192.168.4.1", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + +/* + * 生成 AP SSID:使用设备 MAC 地址的全部 6 个字节拼接,便于区分多设备 + * 示例结果:ESP32-112233AABBCC-192.168.4.1 + */ + +// 启动 AP+STA 模式,配置热点参数,若未启动则启动 Wi-Fi +static esp_err_t wifi_connect_start_apsta_locked(void) +{ + wifi_config_t ap_cfg = {0}; + wifi_connect_generate_ap_ssid(s_ctx.ap_ssid, sizeof(s_ctx.ap_ssid)); + snprintf((char *)ap_cfg.ap.ssid, sizeof(ap_cfg.ap.ssid), "%s", s_ctx.ap_ssid); + ap_cfg.ap.ssid_len = strlen(s_ctx.ap_ssid); + ap_cfg.ap.channel = 1; + ap_cfg.ap.authmode = WIFI_AUTH_OPEN; + ap_cfg.ap.max_connection = CONFIG_WIFI_CONNECT_AP_MAX_CONNECTIONS; + ap_cfg.ap.pmf_cfg.required = false; + + ESP_RETURN_ON_ERROR(esp_wifi_set_mode(WIFI_MODE_APSTA), TAG, "set mode apsta failed"); + ESP_RETURN_ON_ERROR(esp_wifi_set_config(WIFI_IF_AP, &ap_cfg), TAG, "set ap config failed"); + + if (!s_ctx.wifi_started) + { + ESP_RETURN_ON_ERROR(esp_wifi_start(), TAG, "wifi start failed"); + s_ctx.wifi_started = true; + } + return ESP_OK; +} + +/* + * 启动 AP+STA 模式并配置 AP: + * - 生成 AP SSID、设置频道、认证模式以及最大连接数; + * - 切换 Wi-Fi 模式为 APSTA 并应用 AP 配置; + * - 若 Wi-Fi 尚未 start 则调用 `esp_wifi_start()`。 + * + * 注意:该函数假定调用者已持有 s_ctx.lock(因此加了 `_locked` 后缀)。 + */ + +// 按键检测任务,支持消抖、上电保护、长按触发配网 +static void wifi_connect_button_task(void *arg) +{ + (void)arg; + + const TickType_t interval = pdMS_TO_TICKS(CONFIG_WIFI_CONNECT_DEBOUNCE_MS); + const TickType_t startup_guard_ticks = pdMS_TO_TICKS(CONFIG_WIFI_CONNECT_BUTTON_STARTUP_GUARD_MS); + const TickType_t release_arm_ticks = pdMS_TO_TICKS(CONFIG_WIFI_CONNECT_BUTTON_RELEASE_ARM_MS); + int stable_level = gpio_get_level(CONFIG_WIFI_CONNECT_BUTTON_GPIO); + int last_level = stable_level; + TickType_t changed_at = xTaskGetTickCount(); + TickType_t low_since = 0; + TickType_t released_since = 0; + TickType_t startup_at = changed_at; + bool triggered = false; + bool armed = false; + + wifi_connect_log_state_i("按键防误触保护", "已启用上电保护窗口与松手解锁机制"); + + while (true) + { + vTaskDelay(interval); + int level = gpio_get_level(CONFIG_WIFI_CONNECT_BUTTON_GPIO); + TickType_t now = xTaskGetTickCount(); + + if (level != last_level) + { + last_level = level; + changed_at = now; + } + + if ((now - changed_at) >= interval && stable_level != level) + { + stable_level = level; + if (stable_level == CONFIG_WIFI_CONNECT_BUTTON_ACTIVE_LEVEL) + { + low_since = now; + released_since = 0; + triggered = false; + } + else + { + low_since = 0; + released_since = now; + triggered = false; + armed = false; + } + } + + if (!armed && stable_level != CONFIG_WIFI_CONNECT_BUTTON_ACTIVE_LEVEL && released_since != 0) + { + if ((now - released_since) >= release_arm_ticks) + { + armed = true; + } + } + + if ((now - startup_at) < startup_guard_ticks) + { + continue; + } + + if (armed && stable_level == CONFIG_WIFI_CONNECT_BUTTON_ACTIVE_LEVEL && low_since != 0 && !triggered) + { + TickType_t held = now - low_since; + if (held >= pdMS_TO_TICKS(CONFIG_WIFI_CONNECT_LONG_PRESS_MS)) + { + triggered = true; + armed = false; + wifi_connect_log_state_i("检测到按键长按", "开始进入配网模式"); + wifi_connect_start(); + } + } + } +} + +/* + * 按键任务说明: + * - 负责按键防抖、上电保护窗口(避免误触)以及“松手解锁”机制; + * - 通过检测长按(CONFIG_WIFI_CONNECT_LONG_PRESS_MS)触发配网启动; + * - 使用了多项时间窗(上电保护、释放装配窗口、消抖)来减少误触发概率。 + */ + +// 启动配网流程:开启 AP、HTTP、DNS,进入可配网状态 +esp_err_t wifi_connect_start(void) +{ + ESP_RETURN_ON_FALSE(s_ctx.initialized, ESP_ERR_INVALID_STATE, TAG, "not initialized"); + // 启动 AP+STA、HTTP 配网页面和 DNS 劫持,进入可配网状态 + + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + if (s_ctx.provisioning_active) + { + xSemaphoreGive(s_ctx.lock); + return ESP_OK; + } + esp_err_t err = wifi_connect_start_apsta_locked(); + if (err != ESP_OK) + { + xSemaphoreGive(s_ctx.lock); + return err; + } + + err = wifi_connect_http_start(); + if (err != ESP_OK) + { + xSemaphoreGive(s_ctx.lock); + return err; + } + + err = wifi_connect_dns_start(); + if (err != ESP_OK) + { + wifi_connect_http_stop(); + xSemaphoreGive(s_ctx.lock); + return err; + } + + s_ctx.provisioning_active = true; + s_ctx.sta_connect_requested = false; + s_ctx.auto_connecting = false; + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_PROVISIONING); + wifi_connect_set_error_locked(NULL); + xSemaphoreGive(s_ctx.lock); + + wifi_connect_refresh_idle_timeout(); + char ap_msg[96] = {0}; + snprintf(ap_msg, sizeof(ap_msg), "配网热点已开启,SSID=%s,访问 http://192.168.4.1", s_ctx.ap_ssid); + wifi_connect_log_state_i("配网已启动", ap_msg); + // 始终开启配网模式已移除 + return ESP_OK; +} + +/* + * 对外 API:start/stop/init + * - `wifi_connect_start`:启动配网(AP+HTTP+DNS)并更改模块状态; + * - `wifi_connect_stop`:停止所有配网服务,若已成功联网则保持 STA 模式; + * - `wifi_connect_init`:一次性初始化 NVS、netif、事件回调、定时器、按键任务,并尝试自动连接已保存网络。 + * + * 这些函数构成模块对外的主要入口,外部模块只需调用 `wifi_connect_init()`,并在需要时调用 `wifi_connect_start()`/`wifi_connect_stop()`。 + */ + +// 停止配网流程:关闭 AP、HTTP、DNS,恢复为 STA 联网 +esp_err_t wifi_connect_stop(void) +{ + if (!s_ctx.initialized) + { + return ESP_ERR_INVALID_STATE; + } + // 停止配网相关服务,若已联网则回到 STA 模式 + + xSemaphoreTake(s_ctx.lock, portMAX_DELAY); + s_ctx.provisioning_active = false; + s_ctx.sta_connect_requested = false; + s_ctx.auto_connecting = false; + + esp_timer_stop(s_ctx.connect_timer); + esp_timer_stop(s_ctx.idle_timer); + esp_timer_stop(s_ctx.ap_stop_timer); + + wifi_connect_http_stop(); + wifi_connect_dns_stop(); + + if (s_ctx.sta_connected) + { + esp_wifi_set_mode(WIFI_MODE_STA); + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_CONNECTED); + } + else if (s_ctx.status != WIFI_CONNECT_STATUS_TIMEOUT) + { + wifi_connect_set_status_locked(WIFI_CONNECT_STATUS_IDLE); + } + + xSemaphoreGive(s_ctx.lock); + wifi_connect_log_state_i("配网已停止", "热点已关闭,设备继续以 STA 模式运行"); + return ESP_OK; +} + +// 初始化配网组件,注册事件、定时器、HTTP、DNS等,自动重连 +esp_err_t wifi_connect_init(void) +{ + if (s_ctx.initialized) + { + return ESP_OK; + } + // 一次性初始化 NVS/Wi-Fi/事件/按键任务,并尝试自动连接已保存网络 + + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + ESP_RETURN_ON_ERROR(err, TAG, "nvs init failed"); + + ESP_RETURN_ON_ERROR(esp_netif_init(), TAG, "netif init failed"); + err = esp_event_loop_create_default(); + if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) + { + ESP_RETURN_ON_ERROR(err, TAG, "event loop create failed"); + } + + wifi_init_config_t wifi_init_cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_RETURN_ON_ERROR(esp_wifi_init(&wifi_init_cfg), TAG, "wifi init failed"); + ESP_RETURN_ON_ERROR(esp_wifi_set_storage(WIFI_STORAGE_RAM), TAG, "wifi storage set failed"); + + s_ctx.sta_netif = esp_netif_create_default_wifi_sta(); + s_ctx.ap_netif = esp_netif_create_default_wifi_ap(); + + ESP_RETURN_ON_ERROR(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, + &wifi_connect_event_handler, NULL, &s_ctx.wifi_event_instance), + TAG, "register wifi handler failed"); + ESP_RETURN_ON_ERROR(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, + &wifi_connect_event_handler, NULL, &s_ctx.ip_event_instance), + TAG, "register ip handler failed"); + + ESP_RETURN_ON_ERROR(esp_wifi_set_mode(WIFI_MODE_STA), TAG, "set mode sta failed"); + ESP_RETURN_ON_ERROR(esp_wifi_start(), TAG, "wifi start failed"); + s_ctx.wifi_started = true; + + if (wifi_connect_is_button_mode()) + { + gpio_config_t io = { + .pin_bit_mask = (1ULL << CONFIG_WIFI_CONNECT_BUTTON_GPIO), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + ESP_RETURN_ON_ERROR(gpio_config(&io), TAG, "button gpio config failed"); + } + + s_ctx.lock = xSemaphoreCreateMutex(); + ESP_RETURN_ON_FALSE(s_ctx.lock != NULL, ESP_ERR_NO_MEM, TAG, "create lock failed"); + + esp_timer_create_args_t connect_timer_args = { + .callback = wifi_connect_connect_timeout_cb, + .name = "wifi_conn_to", + }; + esp_timer_create_args_t idle_timer_args = { + .callback = wifi_connect_idle_timeout_cb, + .name = "wifi_idle_to", + }; + esp_timer_create_args_t ap_stop_timer_args = { + .callback = wifi_connect_ap_stop_timer_cb, + .name = "wifi_ap_stop", + }; + ESP_RETURN_ON_ERROR(esp_timer_create(&connect_timer_args, &s_ctx.connect_timer), TAG, "connect timer create failed"); + ESP_RETURN_ON_ERROR(esp_timer_create(&idle_timer_args, &s_ctx.idle_timer), TAG, "idle timer create failed"); + ESP_RETURN_ON_ERROR(esp_timer_create(&ap_stop_timer_args, &s_ctx.ap_stop_timer), TAG, "ap stop timer create failed"); + + if (wifi_connect_is_button_mode()) + { + BaseType_t ok = xTaskCreate(wifi_connect_button_task, "wifi_btn", 3072, NULL, 4, &s_ctx.button_task); + ESP_RETURN_ON_FALSE(ok == pdPASS, ESP_ERR_NO_MEM, TAG, "button task create failed"); + } + + s_ctx.initialized = true; + + if (wifi_connect_is_auto_mode()) + { + wifi_connect_log_state_i("配网模式", "自动配网(上电自动开启,连接后关闭)"); + } + else + { + wifi_connect_log_state_i("配网模式", "按键触发配网(长按进入)"); + } + + err = wifi_connect_try_auto_connect(); + if (err != ESP_OK) + { + char skip_msg[96] = {0}; + snprintf(skip_msg, sizeof(skip_msg), "自动重连已跳过,错误=%s", esp_err_to_name(err)); + wifi_connect_log_state_w("初始化后自动重连未执行", skip_msg); + } + + if (wifi_connect_is_auto_mode()) + { + wifi_connect_log_state_i("wifi-connect 初始化完成", "自动配网模式已启用"); + err = wifi_connect_start(); + if (err != ESP_OK) + { + char mode_msg[96] = {0}; + snprintf(mode_msg, sizeof(mode_msg), "自动开启配网失败,错误=%s", esp_err_to_name(err)); + wifi_connect_log_state_w("配网启动失败", mode_msg); + return err; + } + } + else + { + wifi_connect_log_state_i("wifi-connect 初始化完成", "长按按键可进入配网"); + } + return ESP_OK; +} diff --git a/components/protocol_examples_common/console_cmd.c:Zone.Identifier b/components/wifi-connect/wifi-connect.c:Zone.Identifier similarity index 100% rename from components/protocol_examples_common/console_cmd.c:Zone.Identifier rename to components/wifi-connect/wifi-connect.c:Zone.Identifier diff --git a/dependencies.lock b/dependencies.lock index b642695..ff4b2ff 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -1,4 +1,14 @@ dependencies: + chiehmin/sgp30: + component_hash: a3736a8c9494cd72d26bf6a69fcd805c597d7373488e05f377ee0c60539085fa + dependencies: + - name: idf + require: private + version: '>=4.0.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.0 espressif/bh1750: component_hash: e898130f6b2fc4bc0d6022a2e431752bae808b9c94d4cc91596e36ecaf4cb7c6 dependencies: @@ -82,19 +92,14 @@ dependencies: type: service version: 1.2.7 lvgl/lvgl: - component_hash: 17e68bfd21f0edf4c3ee838e2273da840bf3930e5dbc3bfa6c1190c3aed41f9f + component_hash: 184e532558c1c45fefed631f3e235423d22582aafb4630f3e8885c35281a49ae dependencies: [] source: registry_url: https://components.espressif.com type: service - version: 9.4.0 - protocol_examples_common: - dependencies: [] - source: - path: /home/beihong/esp_projects/iot-home/components/protocol_examples_common - type: local - version: '*' + version: 9.5.0 direct_dependencies: +- chiehmin/sgp30 - espressif/bh1750 - espressif/cjson - espressif/esp_lvgl_port @@ -102,7 +107,6 @@ direct_dependencies: - espressif/servo - idf - k0i05/esp_ahtxx -- protocol_examples_common -manifest_hash: 1bbfddd3f393c249aa2425f33f0a08f08c36fdbae7efc3287e867104a15c7d27 +manifest_hash: c14e29556e4f383d6e0935023c722b1c110a210b759fe9e199e6a9b9842d9fff target: esp32c3 version: 2.0.0 diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 6220cc0..3ccedcb 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,6 +1,6 @@ idf_component_register(SRCS "main.c" INCLUDE_DIRS "." - PRIV_REQUIRES esp_wifi cjson nvs_flash lvgl_st7735s_use esp_driver_i2c esp_type_utils esp_timer espressif__servo esp_event esp_netif serial_mcu mqtt + PRIV_REQUIRES wifi-connect cjson nvs_flash lvgl_st7735s_use esp_driver_i2c esp_type_utils esp_timer espressif__servo esp_event esp_netif serial_mcu mqtt WHOLE_ARCHIVE ) diff --git a/main/idf_component.yml b/main/idf_component.yml index 91d1b27..641678a 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -15,10 +15,7 @@ dependencies: # # All dependencies of `main` are public by default. # public: true - protocol_examples_common: - path: ../components/protocol_examples_common - espressif/esp_lvgl_port: ^2.7.0 k0i05/esp_ahtxx: ^1.2.7 @@ -27,5 +24,4 @@ dependencies: espressif/mqtt: ^1.0.0 espressif/cjson: ^1.7.19 - - + chiehmin/sgp30: ^1.0.0 diff --git a/main/main.c b/main/main.c index 45c12d1..e6e7cea 100644 --- a/main/main.c +++ b/main/main.c @@ -37,14 +37,14 @@ #include "ui_display.h" #include "iot_servo.h" #include "serial_mcu.h" -#include "protocol_examples_common.h" +#include "wifi-connect.h" // WiFi连接管理 /* ========================= 2. 宏定义 ========================= */ // 日志标签 #define TAG "main" #define AHT30_TAG "i2c0_ahtxx_task" #define BH1750_TAG "i2c0_bh1750_task" -#define MQ135_TAG "mq135_task" +#define MQ135_TAG "mq135_task" // (removed) MQ135-related functionality disabled #define LVGL_TAG "lvgl_task" #define SERVO_TAG "servo_task" #define SNTP_TAG "sntp_task" @@ -123,10 +123,9 @@ typedef struct float temperature; float humidity; float lux; - float air_quality; bool ahtxx_valid; bool bh1750_valid; - bool mq135_valid; + // MQ135 fields removed: air_quality, mq135_valid } sensor_data_t; // 设备状态结构体 @@ -145,7 +144,7 @@ typedef struct float temperature; float humidity; float light_intensity; - uint16_t air_quality; + // air_quality removed (MQ135) char curtain_state[10]; char led_state[10]; uint8_t led_power; @@ -181,9 +180,10 @@ bool g_high_temp_alerted = false; // 高温提醒是否已发送(避免重复 // 自动通风控制模式配置 #define VENTILATION_MODE_TAG "ventilation_mode" -#define AIR_QUALITY_THRESHOLD 50.0f // 空气质量阈值 -bool g_ventilation_mode_enabled = true; // 通风模式是否启用 -bool g_air_quality_alerted = false; // 空气质量提醒是否已发送(避免重复提醒) +// AIR quality and ventilation functionality removed +// #define AIR_QUALITY_THRESHOLD 50.0f +// bool g_ventilation_mode_enabled = true; +// bool g_air_quality_alerted = false; // 时间段配置 time_period_config_t g_day_period = { @@ -276,7 +276,7 @@ void init_gpio_output(void); // 传感器任务 void i2c0_ahtxx_task(void *pvParameters); void i2c0_bh1750_task(void *pvParameters); -void mq135_task(void *pvParameters); +// MQ135 task removed: void mq135_task(void *pvParameters); // 其他任务 static void rx_task(void *arg); @@ -1194,7 +1194,7 @@ void mqtt_publish_task(void *pvParameters) g_device_message.telemetry.temperature = 0; g_device_message.telemetry.humidity = 0; g_device_message.telemetry.light_intensity = 0; - g_device_message.telemetry.air_quality = 0; + // air_quality removed from telemetry strcpy(g_device_message.telemetry.curtain_state, "close"); strcpy(g_device_message.telemetry.led_state, "close"); g_device_message.telemetry.led_power = 0; @@ -1224,11 +1224,7 @@ void mqtt_publish_task(void *pvParameters) g_device_message.telemetry.light_intensity = g_sensor_data.lux; } - // 更新空气质量数据 - if (g_sensor_data.mq135_valid) - { - g_device_message.telemetry.air_quality = g_sensor_data.air_quality; - } + // MQ135 data removed: air_quality not populated xSemaphoreGive(xMqttMessageMutex); } @@ -1269,7 +1265,7 @@ void mqtt_publish_task(void *pvParameters) cJSON_AddNumberToObject(telemetry_obj, "humidity", roundf(g_device_message.telemetry.humidity * 100) / 100); cJSON_AddNumberToObject(telemetry_obj, "light_intensity", roundf(g_device_message.telemetry.light_intensity * 100) / 100); - cJSON_AddNumberToObject(telemetry_obj, "air_quality", g_device_message.telemetry.air_quality); + // air_quality removed from telemetry cJSON_AddStringToObject(telemetry_obj, "curtain_state", g_device_message.telemetry.curtain_state); cJSON_AddStringToObject(telemetry_obj, "led_state", g_device_message.telemetry.led_state); cJSON_AddNumberToObject(telemetry_obj, "led_power", g_device_message.telemetry.led_power); @@ -2073,12 +2069,7 @@ void i2c0_ahtxx_task(void *pvParameters) ui_update_sensor_data( g_sensor_data.ahtxx_valid ? g_sensor_data.temperature : -1.0f, g_sensor_data.ahtxx_valid ? g_sensor_data.humidity : -1.0f, - g_sensor_data.bh1750_valid ? g_sensor_data.lux : -1.0f, - g_sensor_data.mq135_valid ? g_sensor_data.air_quality : -1.0f, - g_sensor_data.mq135_valid ? (g_sensor_data.air_quality <= 20 ? "Excellent" : g_sensor_data.air_quality <= 100 ? "Good" - : g_sensor_data.air_quality <= 300 ? "Moderate" - : "High") - : "N/A"); + g_sensor_data.bh1750_valid ? g_sensor_data.lux : -1.0f); xSemaphoreGive(xSensorDataMutex); } @@ -2182,12 +2173,7 @@ void i2c0_bh1750_task(void *pvParameters) ui_update_sensor_data( g_sensor_data.ahtxx_valid ? g_sensor_data.temperature : -1.0f, g_sensor_data.ahtxx_valid ? g_sensor_data.humidity : -1.0f, - g_sensor_data.bh1750_valid ? g_sensor_data.lux : -1.0f, - g_sensor_data.mq135_valid ? g_sensor_data.air_quality : -1.0f, - g_sensor_data.mq135_valid ? (g_sensor_data.air_quality <= 20 ? "Excellent" : g_sensor_data.air_quality <= 100 ? "Good" - : g_sensor_data.air_quality <= 300 ? "Moderate" - : "High") - : "N/A"); + g_sensor_data.bh1750_valid ? g_sensor_data.lux : -1.0f); xSemaphoreGive(xSensorDataMutex); } @@ -2213,36 +2199,7 @@ void print_sensor_data(void) { if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE) { - ESP_LOGI(TAG, "======= Sensor Data ========"); - if (g_sensor_data.ahtxx_valid) - { - ESP_LOGI(TAG, "Temperature: %.2f°C", g_sensor_data.temperature); - ESP_LOGI(TAG, "Humidity: %.2f%%", g_sensor_data.humidity); - } - else - { - ESP_LOGI(TAG, "Temperature: Invalid"); - ESP_LOGI(TAG, "Humidity: Invalid"); - } - - if (g_sensor_data.bh1750_valid) - { - ESP_LOGI(TAG, "Light Intensity: %.2flx", g_sensor_data.lux); - } - else - { - ESP_LOGI(TAG, "Light Intensity: Invalid"); - } - - if (g_sensor_data.mq135_valid) - { - ESP_LOGI(TAG, "Air Quality: %.2f Index", g_sensor_data.air_quality); - } - else - { - ESP_LOGI(TAG, "Air Quality: Invalid"); - } - ESP_LOGI(TAG, "=========================="); + // 已移除所有传感器数据日志打印 xSemaphoreGive(xSensorDataMutex); } } @@ -2585,34 +2542,34 @@ static void alarm_clock_main_task(void *arg) void app_main(void) { - esp_log_level_set("*", ESP_LOG_INFO); - esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); - esp_log_level_set("mqtt_example", ESP_LOG_VERBOSE); - esp_log_level_set("transport_base", ESP_LOG_VERBOSE); - esp_log_level_set("esp-tls", ESP_LOG_VERBOSE); - esp_log_level_set("transport", ESP_LOG_VERBOSE); - esp_log_level_set("outbox", ESP_LOG_VERBOSE); - esp_log_level_set("dhcps", ESP_LOG_DEBUG); - esp_log_level_set("esp_netif", ESP_LOG_DEBUG); - esp_log_level_set("esp_netif_lwip", ESP_LOG_DEBUG); - esp_log_level_set("wifi", ESP_LOG_DEBUG); - initialize_nvs(); // 初始化NVS + // esp_log_level_set("*", ESP_LOG_INFO); + // esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); + // esp_log_level_set("mqtt_example", ESP_LOG_VERBOSE); + // esp_log_level_set("transport_base", ESP_LOG_VERBOSE); + // esp_log_level_set("esp-tls", ESP_LOG_VERBOSE); + // esp_log_level_set("transport", ESP_LOG_VERBOSE); + // esp_log_level_set("outbox", ESP_LOG_VERBOSE); + // esp_log_level_set("dhcps", ESP_LOG_DEBUG); + // esp_log_level_set("esp_netif", ESP_LOG_DEBUG); + // esp_log_level_set("esp_netif_lwip", ESP_LOG_DEBUG); + // esp_log_level_set("wifi", ESP_LOG_DEBUG); + + // 初始化 Wi-Fi 配网组件,支持长按按键进入配网 + ESP_ERROR_CHECK(wifi_connect_init()); + printf("设备启动完成:进入配网模式,手机连接 ESP32-* 后访问 http://192.168.4.1\n"); - ESP_ERROR_CHECK(esp_netif_init()); // Initialize ESP-NETIF - // 创建默认事件循环 - ESP_ERROR_CHECK(esp_event_loop_create_default()); // 连接WIFI - ESP_ERROR_CHECK(example_connect()); + // ESP_ERROR_CHECK(example_connect()); - // Print out Access Point Information - wifi_ap_record_t ap_info; - ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info)); - ESP_LOGI(TAG, "--- Access Point Information ---"); - ESP_LOG_BUFFER_HEX("MAC Address", ap_info.bssid, sizeof(ap_info.bssid)); - ESP_LOG_BUFFER_CHAR("SSID", ap_info.ssid, sizeof(ap_info.ssid)); - ESP_LOGI(TAG, "Primary Channel: %d", ap_info.primary); - ESP_LOGI(TAG, "RSSI: %d", ap_info.rssi); + // // Print out Access Point Information + // wifi_ap_record_t ap_info; + // ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info)); + // ESP_LOGI(TAG, "--- Access Point Information ---"); + // ESP_LOG_BUFFER_HEX("MAC Address", ap_info.bssid, sizeof(ap_info.bssid)); + // ESP_LOG_BUFFER_CHAR("SSID", ap_info.ssid, sizeof(ap_info.ssid)); + // ESP_LOGI(TAG, "Primary Channel: %d", ap_info.primary); + // ESP_LOGI(TAG, "RSSI: %d", ap_info.rssi); // 初始化I2C总线 ESP_ERROR_CHECK(i2c_master_init()); @@ -2663,10 +2620,9 @@ void app_main(void) xTaskCreate(time_period_check_task, "time_period_task", 4096, NULL, 5, NULL); // 创建降温模式任务 xTaskCreate(cooling_mode_task, "cooling_mode_task", 4096, NULL, 5, NULL); - // 创建自动通风控制模式任务 - xTaskCreate(ventilation_mode_task, "ventilation_mode_task", 4096, NULL, 5, NULL); - // 创建MQ135传感器任务 - xTaskCreate(mq135_task, "mq135_task", 4096, NULL, 5, NULL); + // 自动通风与 MQ135 相关任务已移除 + // xTaskCreate(ventilation_mode_task, "ventilation_mode_task", 4096, NULL, 5, NULL); + // xTaskCreate(mq135_task, "mq135_task", 4096, NULL, 5, NULL); // 创建外设控制任务 xTaskCreate(peripheral_control_task, "peripheral_control_task", 4096, NULL, 5, NULL); @@ -2679,7 +2635,7 @@ void app_main(void) while (1) { // 定期打印传感器数据 - // print_sensor_data(); + print_sensor_data(); vTaskDelay(5000 / portTICK_PERIOD_MS); } } @@ -2697,7 +2653,7 @@ static void cooling_mode_task(void *pvParameters) cooling_mode_load_from_nvs(); // 高温阈值 - const float HIGH_TEMP_THRESHOLD = 35.0f; + const float HIGH_TEMP_THRESHOLD = 48.0f; while (1) { @@ -2761,7 +2717,7 @@ static void cooling_mode_task(void *pvParameters) xSemaphoreGive(xControlFlagMutex); } - ESP_LOGW(COOLING_MODE_TAG, "High temperature alert: %.1f°C (>35°C)", current_temp); + ESP_LOGW(COOLING_MODE_TAG, "High temperature alert: %.1f°C (>40°C)", current_temp); // 发送MQTT提醒消息 if (g_mqtt_client != NULL) @@ -2819,309 +2775,15 @@ static void cooling_mode_task(void *pvParameters) */ static void ventilation_mode_task(void *pvParameters) { - ESP_LOGI(VENTILATION_MODE_TAG, "Ventilation mode task started"); - - while (1) - { - if (g_ventilation_mode_enabled) - { - float current_air_quality = 0; - bool air_quality_valid = false; - - // 获取当前空气质量 - if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE) - { - current_air_quality = g_sensor_data.air_quality; - air_quality_valid = g_sensor_data.mq135_valid; - xSemaphoreGive(xSensorDataMutex); - } - - if (air_quality_valid) - { - // 检测空气质量是否超过阈值 - if (current_air_quality > AIR_QUALITY_THRESHOLD) - { - // 需要通风,确保风扇开启 - if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE) - { - if (!fan_control_flag) - { - fan_control_flag = true; - ESP_LOGW(VENTILATION_MODE_TAG, "Air quality %.2f > %.1f, Fan ON", - current_air_quality, AIR_QUALITY_THRESHOLD); - update_telemetry_and_report(); - } - xSemaphoreGive(xControlFlagMutex); - } - - // 发送提醒(只发送一次) - if (!g_air_quality_alerted) - { - g_air_quality_alerted = true; - - // 蜂鸣器发出轻微提示音(短促) - sendControlFrame(0x02, 1); - vTaskDelay(pdMS_TO_TICKS(200)); // 持续200ms - sendControlFrame(0x02, 0); - - // 发送MQTT提醒消息 - if (g_mqtt_client != NULL) - { - cJSON *root = cJSON_CreateObject(); - cJSON_AddStringToObject(root, "type", "device_message"); - cJSON_AddStringToObject(root, "device_id", g_device_message.device_id); - cJSON_AddStringToObject(root, "device_type", g_device_message.device_type); - cJSON_AddStringToObject(root, "message_type", "air_quality_alert"); - - cJSON *data_obj = cJSON_CreateObject(); - cJSON_AddStringToObject(data_obj, "alert", "卧室需要通风"); - cJSON_AddNumberToObject(data_obj, "air_quality", roundf(current_air_quality * 10) / 10); - cJSON_AddItemToObject(root, "data", data_obj); - - char *json_message = cJSON_Print(root); - if (json_message) - { - esp_mqtt_client_publish(g_mqtt_client, MQTT_PUBLISH_TOPIC_QOS0, json_message, 0, 0, 0); - ESP_LOGI(VENTILATION_MODE_TAG, "Air quality alert sent: %s", json_message); - free(json_message); - } - cJSON_Delete(root); - } - } - } - else if (current_air_quality < (AIR_QUALITY_THRESHOLD - 10)) // 添加滞后,防止频繁切换 - { - // 空气质量恢复良好,关闭风扇 - if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE) - { - if (fan_control_flag) - { - fan_control_flag = false; - ESP_LOGI(VENTILATION_MODE_TAG, "Air quality %.2f < %.1f, Fan OFF", - current_air_quality, AIR_QUALITY_THRESHOLD - 10); - update_telemetry_and_report(); - } - xSemaphoreGive(xControlFlagMutex); - } - - // 重置提醒标志 - if (g_air_quality_alerted) - { - g_air_quality_alerted = false; - ESP_LOGI(VENTILATION_MODE_TAG, "Air quality normalized: %.2f, reset alert", current_air_quality); - } - } - } - } - - // 每5秒检查一次 - vTaskDelay(pdMS_TO_TICKS(5000)); - } + ESP_LOGI("ventilation_mode_task", "ventilation mode removed"); + vTaskDelete(NULL); } -// MQ135传感器任务 +// MQ135 task removed; provide a short stub to avoid undefined references void mq135_task(void *pvParameters) { - // 初始化ADC - adc1_config_width(ADC_WIDTH_BIT_12); // ADC1 分辨率 12 位 - adc1_config_channel_atten(ADC_CHANNEL_0, ADC_ATTEN_DB_12); // GPIO34, 12dB - - // 动态校准变量 - static bool r0_calibrated = false; - static float R0 = 10.0f; // 初始 R0(更合理的经验值,10kΩ左右) - static int calibrate_count = 0; // 校准采样计数器 - static float rs_sum = 0.0f; // Rs累加值 - - // 采样平滑滤波窗口 - #define SAMPLE_WINDOW_SIZE 20 // 滑动平均窗口大小 - static float voltage_buffer[SAMPLE_WINDOW_SIZE] = {0}; - static int buffer_index = 0; - static bool buffer_filled = false; - - // 预热阶段 - #define WARMUP_SAMPLES 100 // 预热采样次数 - static int warmup_count = 0; - - ESP_LOGI(MQ135_TAG, "MQ135 sensor task started - Preheating for %d samples...", WARMUP_SAMPLES); - - while (1) - { - int adc_value = adc1_get_raw(ADC_CHANNEL_0); - - // ADC -> 电压(工程近似) - float voltage = (adc_value * 3.3f) / 4095.0f; - - // 电压保护 - if (voltage >= 3.3f) - voltage = 3.29f; - if (voltage <= 0.0f) - voltage = 0.01f; - - // === 采样平滑滤波 === - // 存入滑动窗口 - voltage_buffer[buffer_index] = voltage; - buffer_index = (buffer_index + 1) % SAMPLE_WINDOW_SIZE; - - // 检查窗口是否填满 - if (buffer_index == 0) - { - buffer_filled = true; - } - - // 计算滑动平均电压 - float smoothed_voltage = 0.0f; - int window_size = buffer_filled ? SAMPLE_WINDOW_SIZE : buffer_index; - - for (int i = 0; i < window_size; i++) - { - smoothed_voltage += voltage_buffer[i]; - } - smoothed_voltage /= window_size; - - // 计算 Rs(RL = 10kΩ) - float rs = ((3.3f - smoothed_voltage) / smoothed_voltage) * 10.0f; - - // === 预热阶段 === - if (warmup_count < WARMUP_SAMPLES) - { - warmup_count++; - if (warmup_count % 20 == 0) // 每20个采样打印一次进度 - { - ESP_LOGI(MQ135_TAG, "MQ135 preheating... %d/%d", warmup_count, WARMUP_SAMPLES); - } - vTaskDelay(5000 / portTICK_PERIOD_MS); - continue; - } - else if (warmup_count == WARMUP_SAMPLES) - { - warmup_count++; - ESP_LOGI(MQ135_TAG, "MQ135 preheating completed, starting calibration"); - vTaskDelay(100 / portTICK_PERIOD_MS); // 短暂延迟 - continue; - } - - // === 温湿度补偿 === - // MQ135传感器对温度和湿度敏感,需要进行补偿 - float temperature = 20.0f; // 默认20°C - float humidity = 65.0f; // 默认65%RH - - // 获取实际温湿度 - if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE) - { - if (g_sensor_data.ahtxx_valid) - { - temperature = g_sensor_data.temperature; - humidity = g_sensor_data.humidity; - } - xSemaphoreGive(xSensorDataMutex); - } - - // 温度补偿公式 (参考MQ135数据手册) - // R0(T) = R0(T0) * exp(A * (1/T - 1/T0)) - // A为温度系数,约为0.00025 - float temp_compensation = 1.0f + 0.00025f * (temperature - 20.0f); - - // 湿度补偿 (经验公式) - float hum_compensation = 1.0f + 0.0005f * (humidity - 65.0f); - - // 应用补偿后的Rs - float rs_compensated = rs / (temp_compensation * hum_compensation); - - // === R0校准阶段 === - if (!r0_calibrated && calibrate_count < 100) - { - rs_sum += rs_compensated; - calibrate_count++; - - if (calibrate_count == 100) - { - // 使用100次采样的平均值作为R0(假设在干净空气中启动) - R0 = rs_sum / 100.0f; - ESP_LOGI(MQ135_TAG, "MQ135 R0 calibrated to: %.2f (from 100 samples, temp=%.1f°C, hum=%.1f%%)", - R0, temperature, humidity); - r0_calibrated = true; - } - else if (calibrate_count % 20 == 0) - { - ESP_LOGI(MQ135_TAG, "Calibrating R0... %d/100", calibrate_count); - } - } - else if (!r0_calibrated) - { - // 如果校准未完成,使用初始R0值 - ESP_LOGI(MQ135_TAG, "Using initial R0: %.2f", R0); - } - - // Rs/R0 比值 - float ratio = rs_compensated / R0; - - // 防止ratio异常(传感器故障或校准错误) - if (ratio <= 0.1f || ratio > 100.0f) - { - ESP_LOGW(MQ135_TAG, "Abnormal ratio detected: %.3f, skipping", ratio); - vTaskDelay(5000 / portTICK_PERIOD_MS); - continue; - } - - /* ===================================================== - * MQ135 传感器方程 - * Rs/R0 = a * (ppm)^b - * ppm = (Rs/R0 / a)^(1/b) - * 对于 MQ135: a=116.602, b=-2.769 - * ===================================================== */ - float mq135_concentration = 116.602f * powf(ratio, -2.769f); - - // 工程修正:不使用放大系数 - mq135_concentration = mq135_concentration * 1.0f; - - // 下限保护,避免负值或过小值 - if (mq135_concentration < 1.0f) - mq135_concentration = 1.0f; - - // 上限保护 - if (mq135_concentration > 1000.0f) - { - ESP_LOGW(MQ135_TAG, "Concentration too high: %.2f, clamped to 1000", mq135_concentration); - mq135_concentration = 1000.0f; - } - - // 保留两位小数 - mq135_concentration = roundf(mq135_concentration * 100.0f) / 100.0f; - - // 更新全局数据 - if (xSensorDataMutex != NULL && - xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE) - { - g_sensor_data.air_quality = mq135_concentration; - g_sensor_data.mq135_valid = true; - xSemaphoreGive(xSensorDataMutex); - } - - // UI 更新 - if (xSensorDataMutex != NULL && - xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE) - { - ui_update_sensor_data( - g_sensor_data.ahtxx_valid ? g_sensor_data.temperature : -1.0f, - g_sensor_data.ahtxx_valid ? g_sensor_data.humidity : -1.0f, - g_sensor_data.bh1750_valid ? g_sensor_data.lux : -1.0f, - mq135_concentration, - mq135_concentration <= 20 ? "Excellent" : mq135_concentration <= 100 ? "Good" - : mq135_concentration <= 300 ? "Moderate" - : "High"); - xSemaphoreGive(xSensorDataMutex); - } - - // 每10次采样打印一次详细日志 - static int log_count = 0; - log_count++; - if (log_count >= 10) - { - log_count = 0; - ESP_LOGI(MQ135_TAG, "ADC:%d, Volt:%.3fV->%.3fV, Rs:%.2f, Ratio:%.3f, PPM:%.2f (T:%.1f°C, H:%.1f%%)", - adc_value, voltage, smoothed_voltage, rs, ratio, mq135_concentration, temperature, humidity); - } - - vTaskDelay(5000 / portTICK_PERIOD_MS); - } + ESP_LOGI("mq135_task", "mq135 task removed"); + vTaskDelete(NULL); } + + // Remaining MQ135 implementation removed diff --git a/partitions.csv b/partitions.csv index bd1a774..a3c3a13 100644 --- a/partitions.csv +++ b/partitions.csv @@ -1,6 +1,4 @@ -# Name, Type, SubType, Offset, Size, Flags -# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x200000, -wifi_spiffs, data, spiffs, 0x210000, 0x180000, \ No newline at end of file +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 0x200000, diff --git a/components/protocol_examples_common/eth_connect.c:Zone.Identifier b/partitions.csv:Zone.Identifier similarity index 100% rename from components/protocol_examples_common/eth_connect.c:Zone.Identifier rename to partitions.csv:Zone.Identifier