当前位置:首页 » 《关于电脑》 » 正文

VScode+ESP-IDF在ESP32上搭建web server,并实现Captive Portal - 连接WiFi自动弹出认证页面,此页面输入WIFI账号密码一键配网

8 人参与  2024年09月14日 16:40  分类 : 《关于电脑》  评论

点击全文阅读


准备:

装好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。
在这里插入图片描述

结束语

未完待续…


点击全文阅读


本文链接:http://zhangshiyu.com/post/159920.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1