准备:
装好ESP-IDF插件的VScode;ESP32开发板(ESP32-S2、ESP32-S3都行)。ESP-IDF 4.4版本以上第一步 用WIFI-STA示例项目改造
打开VScode,按Shift + Ctrl + P,输入Show Examples Projects后,搜索station,创建station例程。这是一个添加ssid和密码后就能连接无线网络的例程。修改station_example_main.c代码,连接热点的SSID与PASS改成自定义的数组。
修改事件处理函数,判断STA开始的标志语句内创建一键配网的任务。
station_example_main.c 完整代码
extern uint8_t wifi_userid[20];extern uint8_t wifi_password[20];#define EXAMPLE_ESP_MAXIMUM_RETRY 6#if CONFIG_ESP_WIFI_AUTH_OPEN#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN#elif CONFIG_ESP_WIFI_AUTH_WEP#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK#endif/* FreeRTOS event group to signal when we are connected*/static EventGroupHandle_t s_wifi_event_group;/* The event group allows multiple bits for each event, but we only care about two events: * - we are connected to the AP with an IP * - we failed to connect after the maximum amount of retries */#define WIFI_CONNECTED_BIT BIT0#define WIFI_FAIL_BIT BIT1static const char *TAG = "wifi station";static int s_retry_num = 0;static void smartconfig_task(void * parm);static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){ if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { xTaskCreate(smartconfig_task, "smartconfig_task", 4096, NULL, 3, NULL); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) { esp_wifi_connect(); s_retry_num++; ESP_LOGI(TAG, "retry to connect to the AP"); } else { xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); } ESP_LOGI(TAG,"connect to the AP fail"); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); s_retry_num = 0; xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); }}extern void webserver_init(void);void wifi_init_sta(void){ s_wifi_event_group = xEventGroupCreate(); // esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); esp_event_handler_instance_t instance_any_id; esp_event_handler_instance_t instance_got_ip; ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_start()); EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually * happened. */ if (bits & WIFI_CONNECTED_BIT) { ESP_LOGI(TAG, "WiFi Connected to ap");// 成功连接路由器 xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT); //清除连接成功事件位 } else if (bits & WIFI_FAIL_BIT) { ESP_LOGI(TAG, "WiFi Connected Failed");// 失败连接路由器 s_retry_num = 0; xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT); //清除连接失败事件位 esp_wifi_restore(); // 恢复WiFi出厂设置 webserver_init(); //开启WebServer } else { ESP_LOGE(TAG, "UNEXPECTED EVENT"); s_retry_num = 0; xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT); //清除连接失败事件位 esp_wifi_restore(); webserver_init(); } /* The event will not be processed after unregister */ ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); vEventGroupDelete(s_wifi_event_group);}static void smartconfig_task(void * parm){ wifi_config_t myconfig = {0}; ESP_LOGI(TAG, "creat smartconfig_task"); // 获取wifi配置信息,如果配置过,就直接连接wifi esp_wifi_get_config(ESP_IF_WIFI_STA, &myconfig); if (strlen((char*)myconfig.sta.ssid) > 0) { ESP_LOGI(TAG, "alrealy set, SSID is :%s,start connect", myconfig.sta.ssid); esp_wifi_connect(); } // 如果没有配置过,就进行配网操作 else { ESP_LOGI(TAG, "have no set, start to config"); wifi_config_t wifi_config = { .sta = { /* Setting a password implies station will connect to all security modes including WEP/WPA. * However these modes are deprecated and not advisable to be used. Incase your Access point * doesn't support WPA2, these mode can be enabled by commenting below line */ .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD, .sae_pwe_h2e = WPA3_SAE_PWE_BOTH, }, }; strcpy((char*)wifi_config.sta.ssid, (char*)wifi_userid); strcpy((char*)wifi_config.sta.password, (char*)wifi_password); ESP_ERROR_CHECK(esp_wifi_disconnect()); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); esp_wifi_connect(); ESP_LOGI(TAG, "wifi_init_sta finished."); esp_restart(); } vTaskDelete(NULL);// 关闭线程}
第二步 menuconfig选项设置
点击menuconfig后搜索“websocket”,勾选“WebSocket server support”以启用web socket,保存,退出。修改Max HTTP Request Header Length为2048,以保证HTTP的报头发出不报错。
然后 保存 menuconfig
第三步 创建主程序和DNS服务器程序文件
在左侧main目录下创建 main.c、dns_server.h、root.html。添加这些文件到mian目录下“CMakeLists.txt”中,其中root.html的路径添加为EMBED_FILES,如果设计的页面有图片,图片路径也要添加其中,用空格隔开。CMakeLists.txt 导入全部文件
idf_component_register(SRCS "station_example_main.c" "main.c" "dns_server.c" INCLUDE_DIRS "include" EMBED_FILES root.html)
DNS服务器,该服务器将所有查询重定向到软接入点(softAP)的IP地址,用于实现Captive Portal - 连接WiFi自动弹出认证页面。dns_server.c 完整代码 #define DNS_PORT (53)#define DNS_MAX_LEN (256)#define OPCODE_MASK (0x7800)#define QR_FLAG (1 << 7)#define QD_TYPE_A (0x0001)#define ANS_TTL_SEC (300)static const char *TAG = "example_dns_redirect_server";// DNS Header Packettypedef struct __attribute__((__packed__)){ uint16_t id; uint16_t flags; uint16_t qd_count; uint16_t an_count; uint16_t ns_count; uint16_t ar_count;} dns_header_t;// DNS Question Packettypedef struct { uint16_t type; uint16_t class;} dns_question_t;// DNS Answer Packettypedef struct __attribute__((__packed__)){ uint16_t ptr_offset; uint16_t type; uint16_t class; uint32_t ttl; uint16_t addr_len; uint32_t ip_addr;} dns_answer_t;/* Parse the name from the packet from the DNS name format to a regular .-seperated name returns the pointer to the next part of the packet*/static char *parse_dns_name(char *raw_name, char *parsed_name, size_t parsed_name_max_len){ char *label = raw_name; char *name_itr = parsed_name; int name_len = 0; do { int sub_name_len = *label; // (len + 1) since we are adding a '.' name_len += (sub_name_len + 1); if (name_len > parsed_name_max_len) { return NULL; } // Copy the sub name that follows the the label memcpy(name_itr, label + 1, sub_name_len); name_itr[sub_name_len] = '.'; name_itr += (sub_name_len + 1); label += sub_name_len + 1; } while (*label != 0); // Terminate the final string, replacing the last '.' parsed_name[name_len - 1] = '\0'; // Return pointer to first char after the name return label + 1;}// Parses the DNS request and prepares a DNS response with the IP of the softAPstatic int parse_dns_request(char *req, size_t req_len, char *dns_reply, size_t dns_reply_max_len){ if (req_len > dns_reply_max_len) { return -1; } // Prepare the reply memset(dns_reply, 0, dns_reply_max_len); memcpy(dns_reply, req, req_len); // Endianess of NW packet different from chip dns_header_t *header = (dns_header_t *)dns_reply; ESP_LOGD(TAG, "DNS query with header id: 0x%X, flags: 0x%X, qd_count: %d", ntohs(header->id), ntohs(header->flags), ntohs(header->qd_count)); // Not a standard query if ((header->flags & OPCODE_MASK) != 0) { return 0; } // Set question response flag header->flags |= QR_FLAG; uint16_t qd_count = ntohs(header->qd_count); header->an_count = htons(qd_count); int reply_len = qd_count * sizeof(dns_answer_t) + req_len; if (reply_len > dns_reply_max_len) { return -1; } // Pointer to current answer and question char *cur_ans_ptr = dns_reply + req_len; char *cur_qd_ptr = dns_reply + sizeof(dns_header_t); char name[128]; // Respond to all questions with the ESP32's IP address for (int i = 0; i < qd_count; i++) { char *name_end_ptr = parse_dns_name(cur_qd_ptr, name, sizeof(name)); if (name_end_ptr == NULL) { ESP_LOGE(TAG, "Failed to parse DNS question: %s", cur_qd_ptr); return -1; } dns_question_t *question = (dns_question_t *)(name_end_ptr); uint16_t qd_type = ntohs(question->type); uint16_t qd_class = ntohs(question->class); ESP_LOGD(TAG, "Received type: %d | Class: %d | Question for: %s", qd_type, qd_class, name); if (qd_type == QD_TYPE_A) { dns_answer_t *answer = (dns_answer_t *)cur_ans_ptr; answer->ptr_offset = htons(0xC000 | (cur_qd_ptr - dns_reply)); answer->type = htons(qd_type); answer->class = htons(qd_class); answer->ttl = htonl(ANS_TTL_SEC); esp_netif_ip_info_t ip_info; esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info); ESP_LOGD(TAG, "Answer with PTR offset: 0x%" PRIX16 " and IP 0x%" PRIX32, ntohs(answer->ptr_offset), ip_info.ip.addr); answer->addr_len = htons(sizeof(ip_info.ip.addr)); answer->ip_addr = ip_info.ip.addr; } } return reply_len;}/* Sets up a socket and listen for DNS queries, replies to all type A queries with the IP of the softAP*/void dns_server_task(void *pvParameters){ char rx_buffer[128]; char addr_str[128]; int addr_family; int ip_protocol; while (1) { struct sockaddr_in dest_addr; dest_addr.sin_addr.s_addr = htonl(INADDR_ANY); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(DNS_PORT); addr_family = AF_INET; ip_protocol = IPPROTO_IP; inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1); int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); if (sock < 0) { ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); break; } ESP_LOGI(TAG, "Socket created"); int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); if (err < 0) { ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); } ESP_LOGI(TAG, "Socket bound, port %d", DNS_PORT); while (1) { ESP_LOGI(TAG, "Waiting for data"); struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6 socklen_t socklen = sizeof(source_addr); int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen); // Error occurred during receiving if (len < 0) { ESP_LOGE(TAG, "recvfrom failed: errno %d", errno); close(sock); break; } // Data received else { // Get the sender's ip address as string if (source_addr.sin6_family == PF_INET) { inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1); } else if (source_addr.sin6_family == PF_INET6) { inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1); } // Null-terminate whatever we received and treat like a string... rx_buffer[len] = 0; char reply[DNS_MAX_LEN]; int reply_len = parse_dns_request(rx_buffer, len, reply, DNS_MAX_LEN); ESP_LOGI(TAG, "Received %d bytes from %s | DNS reply with len: %d", len, addr_str, reply_len); if (reply_len <= 0) { ESP_LOGE(TAG, "Failed to prepare a DNS reply"); } else { int err = sendto(sock, reply, reply_len, 0, (struct sockaddr *)&source_addr, sizeof(source_addr)); if (err < 0) { ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); break; } } } } if (sock != -1) { ESP_LOGE(TAG, "Shutting down socket"); shutdown(sock, 0); close(sock); } } vTaskDelete(NULL);}void start_dns_server(void){ xTaskCreate(dns_server_task, "dns_server", 4096, NULL, 5, NULL);}
在root.html中添加网页布局HTML代码,设计了一个简单WIFI信息传输的页面,寻到roott.html文件存放目录,双击运行。若只是要修改页面效果,可将客户端连接的地址固定,在VScode修改保存后在浏览器中按F5刷新,能直接看到最新设计效果。用JavaScript语言,创建客户端套接字“ws_client”,JavaScript代码添加在HTML代码的下方。此处设计的功能为:将从web server收到的字符串数据打印log,并回发给ESP32服务器。此脚本嵌入在ESP32的flash后,在使用时,将会在被HTTP客户端请求时发出去。 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>OAK Wi-Fi Configuration</title> <style> body { font-family: Arial, sans-serif; max-width: 400px; margin: 50px auto; padding: 20px; background-color: #f4f4f4; border-radius: 5px; } h1 { text-align: center; color: #333; margin-top: -30px; /* 上移标题 */ } form { display: flex; flex-direction: column; } label { display: block; margin-bottom: 5px; color: #666; } input[type="text"] { width: 100%; height: 38px; padding: 8px; margin-bottom: 15px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } input[type="password"] { width: 100%; height: 38px; padding: 8px; margin-bottom: 15px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } button { width: 60%; padding: 12px 0; /* 调整垂直内边距以保持文本居中,水平内边距已隐含为0 */ margin: 0 auto; /* 添加这行代码使按钮水平居中 */ background-color: #4CAF50; color: white; border: none; cursor: pointer; border-radius: 5px; margin-top: 12px; /* 下移按钮 */ } button:hover { background-color: #45a049; } </style></head><body> <h1>OAK Wi-Fi 设置</h1> <form onsubmit="event.preventDefault(); sendData();"> <label for="UserID">Wi-Fi 名称:</label> <input type="text" id="UserID" name="UserID" placeholder="请输入Wi-Fi账号..."> <label for="PassWord">Wi-Fi 密码:</label> <input type="password" id="PassWord" name="PassWord" placeholder="请输入Wi-Fi密码..."> <button type="submit">发送WIFI数据至OAK测试盒</button> </form></body></html><script>// 服务器地址const ws_client = new WebSocket("ws://" + window.location.host + "/ws");// const ws_client = new WebSocket("ws://192.168.4.1/ws");/* ws_client连接成功事件 */ws_client.onopen = function (event) { };/* ws_client错误事件 */ws_client.onerror = function (error) { };/* ws_client接收数据 */ws_client.onmessage = function (event) { };/* 发送数据到服务器 */function sendData() { const userid = document.getElementById("UserID").value; const password = document.getElementById("PassWord").value; if (userid.trim()) { ws_client.send(userid); } if (password.trim()) { ws_client.send(password); } console.log(`Wi-Fi 名称: ${userid}, Wi-Fi 密码: ${password}`);}</script>
页面效果重点便是Web Server的代码实现部分。首先在主程序app_main里,需要先进入STA的连接模式,如果同一时间段连续6次连接失败,再打开WebServer,配置为AP模式,传输本地WIFI的相关信息。
当成功从HTTP页面接收到WIFI名称和WIFI密码时,再次重新进入STA连接模式。
main.c 完整代码
#define EXAMPLE_ESP_WIFI_SSID #define EXAMPLE_ESP_WIFI_PASS #define EXAMPLE_MAX_STA_CONN 2extern const char root_start[] asm("_binary_root_html_start");extern const char root_end[] asm("_binary_root_html_end");static const char *TAG = "main";#define BUFFER_LEN 1024 typedef struct { char data[BUFFER_LEN]; int len; int client_socket;}DATA_PARCEL;static QueueHandle_t ws_server_rece_queue = NULL;//收到的消息传给任务处理httpd_handle_t server = NULL; //httpd_handle_tvoid stop_webserver(httpd_handle_t server);static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data){ if (event_id == WIFI_EVENT_AP_STACONNECTED) { wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data; ESP_LOGI(TAG, "station " MACSTR " join, AID=%d", MAC2STR(event->mac), event->aid); } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) { wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data; ESP_LOGI(TAG, "station " MACSTR " leave, AID=%d", MAC2STR(event->mac), event->aid); }}static void wifi_init_softap(void){ wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL)); wifi_config_t wifi_config = { .ap = { .ssid = EXAMPLE_ESP_WIFI_SSID, .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID), .password = EXAMPLE_ESP_WIFI_PASS, .max_connection = EXAMPLE_MAX_STA_CONN, .authmode = WIFI_AUTH_WPA_WPA2_PSK }, }; if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) { wifi_config.ap.authmode = WIFI_AUTH_OPEN; } ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); esp_netif_ip_info_t ip_info; esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info); char ip_addr[16]; inet_ntoa_r(ip_info.ip.addr, ip_addr, 16); ESP_LOGI(TAG, "Set up softAP with IP: %s", ip_addr); ESP_LOGI(TAG, "wifi_init_softap finished. SSID:'%s' password:'%s'", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);}// HTTP GET Handlerstatic esp_err_t root_get_handler(httpd_req_t *req){ const uint32_t root_len = root_end - root_start; ESP_LOGI(TAG, "Serve root"); httpd_resp_set_type(req, "text/html"); httpd_resp_send(req, root_start, root_len); return ESP_OK;}static const httpd_uri_t root = { .uri = "/", .method = HTTP_GET, .handler = root_get_handler};/*ws服务器接收数据*/static DATA_PARCEL ws_rece_parcel; static esp_err_t ws_server_rece_data(httpd_req_t *req){ if (req->method == HTTP_GET) { // ws_client_list_add(httpd_req_to_sockfd(req)); return ESP_OK; } esp_err_t ret = ESP_FAIL; httpd_ws_frame_t ws_pkt; memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); memset(&ws_rece_parcel, 0, sizeof(DATA_PARCEL)); ws_pkt.type = HTTPD_WS_TYPE_TEXT; ws_pkt.payload = (uint8_t*)ws_rece_parcel.data; //指向缓存区 ret = httpd_ws_recv_frame(req, &ws_pkt, 0);//设置参数max_len = 0来获取帧长度 if (ret != ESP_OK) { printf("ws_server_rece_data data receiving failure!"); return ret; } if (ws_pkt.len>0) { ret = httpd_ws_recv_frame(req, &ws_pkt, ws_pkt.len);/*设置参数max_len 为 ws_pkt.len以获取帧有效负载 */ if (ret != ESP_OK) { printf("ws_server_rece_data data receiving failure!"); return ret; } ws_rece_parcel.len = ws_pkt.len; ws_rece_parcel.client_socket = httpd_req_to_sockfd(req); if (xQueueSend(ws_server_rece_queue ,&ws_rece_parcel,pdMS_TO_TICKS(1))){ ret = ESP_OK; } } else { printf("ws_pkt.len<=0"); } return ret;}/*WEB SOCKET*/static const httpd_uri_t ws = { .uri = "/ws", .method = HTTP_GET, .handler = ws_server_rece_data, .user_ctx = NULL, .is_websocket = true};/*数据接收处理任务*/static DATA_PARCEL rece_buffer; uint8_t wifi_userid[20] = "12345678"; //默认WIFI名称uint8_t wifi_password[20] = "12345678"; //默认WIFI密码uint8_t wifi_config = 0; //WIFI配置标志 0:未配置 1:已配置extern void wifi_init_sta(void);static void ws_server_rece_task(void *p){ int rece_step = 1; while (1) { if(xQueueReceive(ws_server_rece_queue,&rece_buffer,portMAX_DELAY)) { if(rece_step == 1) { strcpy((char*)wifi_userid, rece_buffer.data); wifi_userid[rece_buffer.len] = '\0'; rece_step = 2; } else if(rece_step == 2) { strcpy((char*)wifi_password, rece_buffer.data); wifi_password[rece_buffer.len] = '\0'; rece_step = 3; wifi_config = 1; stop_webserver(server); } printf("socket : %d\tdata len : %d\tpayload : %s\r\n",rece_buffer.client_socket,rece_buffer.len,rece_buffer.data); } if(wifi_config == 1) { wifi_init_sta(); vTaskDelete(NULL);// 关闭线程 } }}// HTTP Error (404) Handler - Redirects all requests to the root pageesp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err){ // Set status httpd_resp_set_status(req, "302 Temporary Redirect"); // Redirect to the "/" root directory httpd_resp_set_hdr(req, "Location", "/"); // iOS requires content in the response to detect a captive portal, simply redirecting is not sufficient. httpd_resp_send(req, "Redirect to the captive portal", HTTPD_RESP_USE_STRLEN); ESP_LOGI(TAG, "Redirecting to root"); return ESP_OK;}void start_webserver(void){ httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.max_open_sockets = 13; config.lru_purge_enable = true; // Start the httpd server ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); if (httpd_start(&server, &config) == ESP_OK) { // Set URI handlers httpd_register_uri_handler(server, &root); httpd_register_uri_handler(server, &ws);//注册uri处理程序 httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, http_404_error_handler); } /*创建接收队列*/ ws_server_rece_queue = xQueueCreate( 3 , sizeof(DATA_PARCEL)); if (ws_server_rece_queue == NULL ) { printf("ws_server_rece_queue ERROR\r\n"); } BaseType_t xReturn ; /*创建接收处理任务*/ xReturn = xTaskCreatePinnedToCore(ws_server_rece_task,"ws_server_rece_task",4096,NULL,15, NULL, tskNO_AFFINITY); if(xReturn != pdPASS) { printf("xTaskCreatePinnedToCore ws_server_rece_task error!\r\n"); }}void stop_webserver(httpd_handle_t server){ // Stop the httpd server httpd_stop(server);}void webserver_init(void){ // Initialize Wi-Fi including netif with default config esp_netif_create_default_wifi_ap(); // Initialise ESP32 in SoftAP mode wifi_init_softap(); // Start the server for the first time start_webserver(); // Start the DNS server that will redirect all queries to the softAP IP start_dns_server();}void app_main(void){ /* Turn of warnings from HTTP server as redirecting traffic will yield lots of invalid requests */ esp_log_level_set("httpd_uri", ESP_LOG_ERROR); esp_log_level_set("httpd_txrx", ESP_LOG_ERROR); esp_log_level_set("httpd_parse", ESP_LOG_ERROR); // Initialize networking stack ESP_ERROR_CHECK(esp_netif_init()); // Create default event loop needed by the main app ESP_ERROR_CHECK(esp_event_loop_create_default()); // Initialize NVS needed by Wi-Fi ESP_ERROR_CHECK(nvs_flash_init()); esp_netif_create_default_wifi_sta(); wifi_init_sta();}
为了适应修改后的WIFI配置,Kconfig.projbuild里的内容要进行特定修改。 menu "Example Configuration" config ESP_WIFI_SSID string "SoftAP SSID" default "esp32_ssid" help SSID (network name) to set up the softAP with. config ESP_WIFI_PASSWORD string "SoftAP Password" default "esp32_pwd" help WiFi password (WPA or WPA2) for the example to use for the softAP. config ESP_MAX_STA_CONN int "Maximal STA connections" default 4 help Max number of the STA connects to AP. config ESP_WIFI_SSID string "WiFi SSID" default "myssid" help SSID (network name) for the example to connect to. config ESP_WIFI_PASSWORD string "WiFi Password" default "mypassword" help WiFi password (WPA or WPA2) for the example to use. config ESP_MAXIMUM_RETRY int "Maximum retry" default 5 help Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. choice ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD prompt "WiFi Scan auth mode threshold" default ESP_WIFI_AUTH_OPEN help The weakest authmode to accept in the scan mode. config ESP_WIFI_AUTH_OPEN bool "OPEN" config ESP_WIFI_AUTH_WEP bool "WEP" config ESP_WIFI_AUTH_WPA_PSK bool "WPA PSK" config ESP_WIFI_AUTH_WPA2_PSK bool "WPA2 PSK" config ESP_WIFI_AUTH_WPA_WPA2_PSK bool "WPA/WPA2 PSK" config ESP_WIFI_AUTH_WPA3_PSK bool "WPA3 PSK" config ESP_WIFI_AUTH_WPA2_WPA3_PSK bool "WPA2/WPA3 PSK" config ESP_WIFI_AUTH_WAPI_PSK bool "WAPI PSK" endchoiceendmenu
调试效果
连续6次连接失败跳转至WebServer。在HTTP页面完成WIFI配置,成功连接WIFI。
结束语
未完待续…