ESP32- 项目应用1 智能手表之网络配置 #6
研究目的:为了方便更新时间、温度和天气参数,增加了一个网络配置界面
1 网络配置界面
1.1 配置界面
1.1.1 绘制配置按键
创建一个配置按键, 添加一个配置符号;
config_btn = lv_btn_create(scr);lv_obj_set_size(config_btn, 50, 50);lv_obj_align(config_btn, LV_ALIGN_BOTTOM_LEFT, 10, -10);lv_obj_set_style_radius(config_btn, 25, 0); // 圆形按钮// 设置按钮样式lv_obj_set_style_bg_color(config_btn, lv_color_hex(0x4A89F7), 0);lv_obj_set_style_bg_color(config_btn, lv_color_hex(0x357AE8), LV_STATE_PRESSED);// 添加配置图标lv_obj_t * wifi_icon = lv_label_create(config_btn);lv_label_set_text(wifi_icon, LV_SYMBOL_SETTINGS);lv_obj_set_style_text_color(wifi_icon, lv_color_white(), 0);lv_obj_set_style_text_font(wifi_icon, &lv_font_montserrat_48, 0);lv_obj_center(wifi_icon);// 添加点击事件lv_obj_add_event_cb(config_btn, config_btn_cb, LV_EVENT_CLICKED, NULL);
1.1.2 添加回调函数
切换屏幕的时候,需要清空的当前的界面。因为内存不够的原因,这样导致切换前,需要清空屏幕。
static void config_btn_cb(lv_event_t * e)
{lv_event_code_t code = lv_event_get_code(e);if(code == LV_EVENT_CLICKED) {if(timer_clock!=NULL){lv_timer_del(timer_clock);timer_clock=NULL;}ESP_LOGI(TAG,"delete old screen\r\n");if(update_time_task_handle!=NULL) // 退出界面的时候,删除更新时间,避免更新时间程序和wifi扫描程序冲突{vTaskDelete(update_time_task_handle); update_time_task_handle=NULL;}// 显示WiFi配置界面lv_obj_t *scr=lv_obj_create(NULL);create_wifi_screen(scr);lv_scr_load_anim(scr,LV_SCR_LOAD_ANIM_OVER_LEFT,300, 0, true);label_wifi=NULL;}
}
1.2 扫描网络
1.2.1 开发思路
- 将 ESP32 设置为 Station(STA)模式(不是AP)。
- 初始化 WiFi 子系统、网络接口和事件循环。
- 发起扫描(主动/被动、同步/异步可选),等待扫描完成。
- 读取扫描结果(SSID、RSSI、信道、加密类型、BSSID 等),按需过滤或展示
扫描是耗时操作(几十到几百毫秒,取决于通道数和扫描类型),生产代码中通常使用异步事件或在独立任务中执行,避免阻塞主线程/UI。
void wifi_scan_task(void *pvParameters)
{// // 等待扫描命令// ulTaskNotifyTake(pdTRUE, portMAX_DELAY);wifi_event_group = xEventGroupCreate();// 开始扫描wifi_scan_start();// 等待扫描完成xEventGroupWaitBits(wifi_event_group, WIFI_SCAN_DONE_BIT, pdTRUE, pdFALSE, portMAX_DELAY);// 处理扫描结果wifi_scan_process_results();xSemaphoreGive(wifi_scan_sem); // 通知LVGL_WIFI的扫描结束// 等待扫描完成xEventGroupWaitBits(wifi_event_group, WIFI_SCAN_DELE_BIT, pdTRUE, pdFALSE, portMAX_DELAY);if (ap_list) {free(ap_list);ap_list = NULL;}if (wifi_event_group != NULL) {vEventGroupDelete(wifi_event_group);wifi_event_group = NULL; // 避免悬空句柄}while(1); // 等待程序,不让其自动删除,手动删除任务}
1.2.2 配置回调函数
ESP-IDF 异步扫描(事件驱动)要点
注册事件处理器 esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_SCAN_DONE, handler, NULL)。
调用 esp_wifi_scan_start(&cfg, true)(true = non-blocking),在事件处理器里用 esp_wifi_scan_get_ap_num / esp_wifi_scan_get_ap_records 获取结果。
1.3 输入密码虚拟键盘
因为屏幕不大,所以虚拟键盘设计成为了数字键盘和字母键盘。可以来回切换。
static void create_enhanced_keyboard(lv_obj_t * parent)
{// // 创建键盘kb = lv_btnmatrix_create(parent);lv_obj_set_size(kb, LV_PCT(100), 210);lv_obj_align(kb,LV_ALIGN_TOP_LEFT,0,30); lv_obj_set_style_bg_color(kb, lv_color_hex(0x333333), 0);lv_obj_set_style_border_width(kb, 1, 0);lv_obj_set_style_border_color(kb, lv_color_hex(0x555555), 0);lv_obj_set_style_pad_all(kb, 5, 0);// // 初始设置为数字模式set_keyboard_mode(true);lv_obj_add_event_cb(kb, keyboard_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
}
1.4 连接网络
ESP-IDF 里连接 WiFi 的流程是:
- 初始化 NVS(存储 WiFi 配置需要)。
- 初始化 TCP/IP 栈和事件循环。
- 设置为 Station 模式。
- 配置 WiFi(SSID、密码)。
- 注册事件回调(连接成功 / 获取 IP / 断开)。
- 调用 esp_wifi_connect() 开始连接。
void wifi_connect_task(void *pvParameters)
{sys_wifi *p_wifi=pvParameters;const char *ssid = p_wifi->ssid;const char *password = p_wifi->passwd;wifi_config_t wifi_config = { 0 };strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));strncpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password));// ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));ESP_ERROR_CHECK(esp_wifi_connect());ESP_LOGI("wifi", "Connecting to SSID:%s, PASS:%s", ssid, password);
}
ESP-IDF 默认会自动重连 WiFi(你也可以自己处理 WIFI_EVENT_STA_DISCONNECTED 事件)。
如果需要同时做 扫描 + 连接,可以先扫描,找到目标 SSID,再把结果写入 wifi_config_t,最后调用 esp_wifi_connect()。
连接成功后一般还要等 IP_EVENT_STA_GOT_IP 才能开始访问网络。
2 保存和加载 ssid和password
esp_err_t save_wifi_config(const char *ssid, const char *password)
{nvs_handle_t nvs_handle;esp_err_t err;// 打开NVS命名空间err = nvs_open("wifi_config", NVS_READWRITE, &nvs_handle);if (err != ESP_OK) {ESP_LOGE(TAG, "Error opening NVS handle: %s", esp_err_to_name(err));return err;}// 保存SSIDerr = nvs_set_str(nvs_handle, "ssid", ssid);if (err != ESP_OK) {ESP_LOGE(TAG, "Error saving SSID: %s", esp_err_to_name(err));nvs_close(nvs_handle);return err;}// 保存密码err = nvs_set_str(nvs_handle, "password", password);if (err != ESP_OK) {ESP_LOGE(TAG, "Error saving password: %s", esp_err_to_name(err));nvs_close(nvs_handle);return err;}// 提交更改err = nvs_commit(nvs_handle);if (err != ESP_OK) {ESP_LOGE(TAG, "Error committing NVS: %s", esp_err_to_name(err));}nvs_close(nvs_handle);ESP_LOGI(TAG, "WiFi config saved: SSID=%s", ssid);return err;
}// 从NVS读取WiFi配置
esp_err_t load_wifi_config(char *ssid, size_t ssid_len, char *password, size_t password_len)
{nvs_handle_t nvs_handle;esp_err_t err;// 打开NVS命名空间err = nvs_open("wifi_config", NVS_READONLY, &nvs_handle);if (err != ESP_OK) {ESP_LOGE(TAG, "Error opening NVS handle: %s", esp_err_to_name(err));return err;}// 读取SSIDerr = nvs_get_str(nvs_handle, "ssid", ssid, &ssid_len);if (err != ESP_OK) {ESP_LOGE(TAG, "Error reading SSID: %s", esp_err_to_name(err));nvs_close(nvs_handle);return err;}// 读取密码err = nvs_get_str(nvs_handle, "password", password, &password_len);if (err != ESP_OK) {ESP_LOGE(TAG, "Error reading password: %s", esp_err_to_name(err));nvs_close(nvs_handle);return err;}nvs_close(nvs_handle);ESP_LOGI(TAG, "WiFi config loaded: SSID=%s", ssid);return ESP_OK;
}
3 仓库
https://gitee.com/hongqi11/smart_clock
4 效果视频
6.1 WFI的配置效果
时钟创建- 增加了网络配置i界面
6.2 总体效果图
手表的最后一版本