avatar

松果工作室

欢迎光临

  • 首页
  • freeRTOS
  • ESP
  • 开发手册
  • 快速笔记
  • 个人收藏
  • 工具
Home ESP32(八) 简单的webserver
文章

ESP32(八) 简单的webserver

Posted 2025-03-18 Updated 2025-03- 18
By YCP
47~60 min read
#include <esp_wifi.h>
#include <nvs.h>
#include <sys/param.h>
#include "esp_mac.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_http_server.h"
#include "cJSON.h"
#include "web_server.h"
#include "nvs_wificfg.h"
#include "rainmaker_task.h"

#define MAX_FILE_SIZE   (200*1024)

static const char *TAG = "web_server";

esp_event_handler_instance_t sta_instance_got_ip;
esp_event_handler_instance_t sta_instance_any_id;
esp_event_handler_instance_t ap_instance_any_id;

static esp_err_t post_wifi_handler(httpd_req_t *req) {
    char buf[128];
    int ret, remaining = req->content_len;
    bzero(buf, sizeof(buf));

    /*
     * [ycp]: 接收 POST 数据
     */
    while (remaining > 0) {
        ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)));
        if (ret <= 0) {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
                continue;
            }
            httpd_resp_sendstr(req, "Failed to receive data");
            return ESP_FAIL;
        }
        remaining -= ret;
    }
    /*
     * [ycp]: 解析网页操作传入的 CJSON
     */
    cJSON *root = cJSON_Parse(buf);
    if (root == NULL) {
        httpd_resp_sendstr(req, "Failed to parse JSON data");
        return ESP_FAIL;
    }
    char *ssid = cJSON_GetObjectItem(root, "wifi_ssid")->valuestring;
    char *passwd = cJSON_GetObjectItem(root, "wifi_passwd")->valuestring;

    extern nvs_handle_t my_handle;
    nvsset_wificfg((const uint8_t*)"WiFi_NVS",&my_handle,(uint8_t*)ssid,(uint8_t*)passwd);

    cJSON_Delete(root);
    httpd_resp_sendstr(req, "WiFi configuration successful");
    return ESP_OK;
}

/*
 * [ycp]: 主页的 GET
 */
static esp_err_t root_get_handler(httpd_req_t *req) {
    extern const unsigned char upload_script_start[] asm("_binary_upload_script_html_start");
    extern const unsigned char upload_script_end[] asm("_binary_upload_script_html_end");
    const size_t upload_script_size = (upload_script_end - upload_script_start);
    httpd_resp_send(req, (const char *) upload_script_start, upload_script_size);
    return ESP_OK;
}
httpd_handle_t server = NULL;
esp_err_t start_web_server() {
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();

    ESP_LOGI(TAG, "Starting HTTP Server");
    if (httpd_start(&server, &config) != ESP_OK) {
        ESP_LOGE(TAG, "Failed to start file server!");
        return ESP_FAIL;
    } else{
        ESP_LOGI(TAG, "start file server ok!");
    }

    httpd_uri_t wifi_data = {
            .uri       = "/wifi_data",
            .method    = HTTP_POST,
            .handler   = post_wifi_handler,
            .user_ctx  = NULL
    };
    httpd_register_uri_handler(server, &wifi_data);

    httpd_uri_t root = {
            .uri       = "/",
            .method    = HTTP_GET,
            .handler   = root_get_handler,
            .user_ctx  = NULL
    };
    httpd_register_uri_handler(server, &root);

    return ESP_OK;
}


#define EXAMPLE_ESP_WIFI_SSID      "ycp666"
#define EXAMPLE_ESP_WIFI_PASS      "88888888"
#define EXAMPLE_ESP_WIFI_CHANNEL   1
#define EXAMPLE_MAX_STA_CONN       4
#define EXAMPLE_ESP_MAXIMUM_RETRY  2

static int s_retry_num = 0;

static EventGroupHandle_t s_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

/*
 * [ycp]: 连接断开回调,仅用于 LOG
 */
static void ap_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);
    }
}

/*
 * [ycp]: WIFI 回调事件,用于执行 WIFI 的操作
 */
static void sta_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) {
        esp_wifi_connect();
    } 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);
    }
}

// 完整的反初始化 WiFi 相关资源,讲究顺序的
void wifi_uninit(uint8_t if_ap) {
    esp_wifi_stop();
}

void wifi_init_softap(void) {
    esp_netif_create_default_wifi_ap();
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &ap_event_handler,
                                                        NULL,
                                                        ap_instance_any_id));
    wifi_config_t wifi_config = {
            .ap = {
                    .ssid = EXAMPLE_ESP_WIFI_SSID,
                    .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
                    .channel = EXAMPLE_ESP_WIFI_CHANNEL,
                    .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(WIFI_IF_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}

int8_t wifi_init_sta(uint8_t wifi_ssid[],uint8_t wifi_passwd[]) {
    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_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &sta_event_handler,
                                                        NULL,
                                                        &sta_instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &sta_event_handler,
                                                        NULL,
                                                        &sta_instance_got_ip));
    wifi_config_t wifi_config = {
            .sta = {
                    //.ssid = wifi_ssid,
                    //.password = wifi_password,
                    .pmf_cfg = {
                            .capable = true,
                            .required = false
                    },
            },
    };
    memcpy(wifi_config.sta.ssid, wifi_ssid, 20);
    memcpy(wifi_config.sta.password, wifi_passwd, 20);

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
                                           WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 wifi_ssid, wifi_passwd);
        //rainmaker_init();
        return 0;
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 wifi_ssid, wifi_passwd);
        return -1;
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
        return -1;
    }
}
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WiFi 密码配置</title>
    <style>
        .form-container {
            max-width: 500px;
            margin: 0 auto;
            padding: 2rem;
            border: 1px solid #ccc;
            border-radius: 10px;
        }
        label, input {
            display: block;
            margin-bottom: 1rem;
        }
        @media (max-width: 600px) {
            .form-container {
                padding: 1rem;
            }
        }
    </style>
</head>
<body>
    <div class="form_container">
        <h3>WiFi 密码配置</h3>
        <form>
            <label for="wifi_ssid">WiFi 名称</label>
            <input type="text" id="wifi_ssid" name="wifi_ssid" placeholder="ssid">
            <label for="wifi_password">密码</label>
            <input type="password" id="wifi_password" name="wifi_password" placeholder="password">
            <button id="send_wifi" type="button" onclick="send_wifi_data()">提交</button>
            <button id="clear_input" type="button" onclick="clear_button()">清除</button>
        </form>
    </div>
    <script>
        function send_wifi_data() {
            var wifi_ssid = document.getElementById("wifi_ssid").value;
            var wifi_password = document.getElementById("wifi_password").value;
            var xhttp = new XMLHttpRequest();
            xhttp.open("POST", "/wifi_data", true);
            xhttp.onreadystatechange = function() {
                if (xhttp.readyState == 4) {
                    if (xhttp.status == 200) {
                        console.log(xhttp.responseText);
                        alert(xhttp.responseText);
                    } else if (xhttp.status == 0) {
                        alert("Server closed the connection abruptly!");
                    } else {
                        alert(xhttp.status + " Error!\n" + xhttp.responseText);
                    }
                }
            };
            var data = {
                "wifi_ssid": wifi_ssid,
                "wifi_passwd": wifi_password
            }
            xhttp.send(JSON.stringify(data));
        }
    
        function clear_button() {
            document.getElementById("wifi_ssid").value = '';
            document.getElementById("wifi_password").value = '';
        }
    </script>
</body>
</html>
ESP
License:  CC BY 4.0
Share

Further Reading

OLDER

ESP32(七) NVS

NEWER

Recently Updated

  • ESP32(八) 简单的webserver
  • ESP32(七) NVS
  • ESP32(四) STA & AP
  • 多级菜单
  • ESP32(五) ESP32 OTA

Trending Tags

WCH Linux Elec freeRTOS STM ESP Flutter Others SwiftUI

Contents

©2025 松果工作室. Some rights reserved.

Using the Halo theme Chirpy