一、ESP32 IDF創建WEB SERVER的流程
1. 配置web服務器
在ESP-IDF中,Web服務器使用httpd組件實現。我們需要先創建httpd_config_t結構體,指定服務器的端口、最大并發連接數、URI匹配處理器等選項。然后,我們通過調用httpd_start函數來啟動Web服務器。
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_handle_t server = NULL;
// 設置服務器端口為80
config.server_port = 80;
// 創建HTTP服務器句柄
if (httpd_start(&server, &config) != ESP_OK) {
printf("Error starting server!\\n");
return;
}
在這個示例中,我們首先使用HTTPD_DEFAULT_CONFIG宏創建默認的Web服務器配置。接著,我們將服務器端口設置為80,創建HTTP服務器句柄,并啟動Web服務器。
2. 注冊 URI處理器
在Web服務器啟動后,我們需要為不同的URI注冊處理器函數。當Web服務器接收到請求時,會根據請求的URI選擇相應的處理器函數進行處理。在ESP-IDF中,我們可以使用
httpd_register_uri_handler函數注冊URI處理器。該函數的原型如下:
esp_err_t httpd_register_uri_handler(httpd_handle_t hd, const httpd_uri_t *uri)
其中,hd參數為HTTP服務器句柄;uri參數為包含URI路徑、HTTP方法、處理函數等信息的結構體指針。例如:
static esp_err_t hello_get_handler(httpd_req_t *req)
{
char resp_str[64];
httpd_req_get_url_query_str(req, resp_str, sizeof(resp_str));
printf("query string: %s\\n", resp_str);
httpd_resp_send(req, "Hello, world!", HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
httpd_uri_t hello = {
.uri = "/hello",
.method = HTTP_GET,
.handler = hello_get_handler,
.user_ctx = NULL
};
if (httpd_register_uri_handler(server, &hello) != ESP_OK) {
printf("Error registering URI handler!\\n");
return;
}
3. 實現 URI處理器函數
在注冊URI處理器后,我們需要實現對應的處理器函數。URI處理器函數的原型為:
typedef esp_err_t (*httpd_uri_func_t)(httpd_req_t *req);
其中,req參數為指向HTTP請求信息的結構體指針,包含了請求的各種參數和數據。
4. 處理HTTP請求
在URI處理器函數中,我們可以通過HTTP請求信息結構體指針httpd_req_t獲取HTTP請求的各種參數和數據。以下是一些常用的HTTP請求處理函數:
-
httpd_req_get_hdr_value_str:獲取HTTP請求頭中指定字段的值(字符串格式)
-
httpd_req_get_url_query_str:獲取HTTP請求URL中的查詢參數(字符串格式)
-
httpd_query_key_value:解析HTTP請求URL中的查詢參數,獲取指定參數名的值(字符串格式)
-
httpd_req_recv:從HTTP請求接收數據
-
httpd_req_send:發送HTTP響應數據
-
httpd_resp_set_type:設置HTTP響應內容的MIME類型
-
httpd_resp_send_chunk:分塊發送HTTP響應數據。
例如,以下是一個URI處理器函數的示例,用于處理/echo路徑的POST請求:
static esp_err_t echo_post_handler(httpd_req_t *req)
{
char buf[1024];
int ret, remaining = req->content_len;
// 從HTTP請求中接收數據
while (remaining > 0) {
ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)));
if (ret <= 0) {
if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
// 處理超時
httpd_resp_send_408(req);
}
return ESP_FAIL;
}
// 處理接收到的數據
// ...
remaining -= ret;
}
// 發送HTTP響應
httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
httpd_resp_send(req, "Received data: ", -1);
httpd_resp_send_chunk(req, buf, req->content_len);
httpd_resp_send_chunk(req, NULL, 0);
return ESP_OK;
}
5. 處理web socket連接
除了支持HTTP請求外,ESP-IDF的Web服務器還支持WebSocket連接。WebSocket是一種基于TCP的協議,可以提供雙向通信功能。在ESP-IDF中,我們可以使用httpd_ws_frame_t結構體表示WebSocket幀,使用httpd_ws_send_frame_async函數異步發送WebSocket幀。
要處理WebSocket連接,我們需要為WebSocket URI注冊專門的處理器函數,并在該函數中處理WebSocket連接的各種事件。
6. 注冊 URI 處理函數
創建好HTTP服務器后,需要注冊URI處理函數,以便處理客戶端發送的請求。URI處理函數需要實現在HTTP請求中指定的URI。
在ESP-IDF中,可以使用
httpd_register_uri_handler()函數注冊URI處理函數。該函數需要傳入一個httpd_uri_t結構體作為參數,該結構體包含了URI路徑和處理函數的信息。
例如,下面的代碼注冊了一個處理根目錄的URI處理函數:
httpd_uri_t uri = {
.uri = "/",
.method = HTTP_GET,
.handler = hello_get_handler,
.user_ctx = NULL
};
httpd_register_uri_handler(server, &uri);
上面的代碼注冊了一個HTTP GET方法,URI路徑為“/”的處理函數hello_get_handler。當客戶端請求根目錄時,HTTP服務器將調用hello_get_handler函數處理請求。
7. 啟動HTTP服務器
在所有的URI處理函數都被注冊后,可以調用httpd_start()函數啟動HTTP服務器。
httpd_start(&server);
8. 發送響應
在URI處理函數中,可以使用httpd_resp_send()函數將響應發送回客戶端。該函數需要傳入一個httpd_req_t結構體作為參數,該結構體表示HTTP請求和響應。
例如,在上面的hello_get_handler處理函數中,可以使用httpd_resp_send()函數將“Hello, World!”字符串作為響應發送回客戶端:
static esp_err_t hello_get_handler(httpd_req_t *req)
{
const char* resp_str = "Hello, World!";
httpd_resp_send(req, resp_str, strlen(resp_str));
return ESP_OK;
}
9. 關閉 http 服務
使用httpd_stop()函數,該函數用于停止HTTP服務并釋放所有資源。
// 創建HTTP服務
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_start(&server, &config);
// 關閉HTTP服務
httpd_stop(server);
二、本要主要使用API的說明
1. httpd_register_uri_handler
用于將HTTP請求的URI路由到處理程序。這個函數接收兩個參數:httpd_handle_t類型的HTTP服務器句柄和httpd_uri_t類型的URI配置。
2. httpd_handle_t
httpd_handle_t是HTTP服務器的一個句柄,它是通過httpd_start函數創建的。而httpd_uri_t則定義了HTTP請求的URI信息,包括URI路徑、HTTP請求方法和處理函數等。
3. httpd_query_key_value獲取變量值
httpd_query_key_value 用于從查詢字符串中獲取指定鍵的值。查詢字符串是指URL中?后面的部分,包含多個鍵值對,每個鍵值對之間使用&分隔。例如,對于以下URL:
http://192.168.1.1/path/to/handler?key1=value1&key2=value2
獲取其中的:
esp_err_t httpd_query_key_value(const char *query, const char *key, char *buf, size_t buf_len);
這是一個使用示例:
char query_str[] = "key1=value1&key2=value2";
char key[] = "key1";
char value[16];
if (httpd_query_key_value(query_str, key, value, sizeof(value)) == ESP_OK) {
printf("value=%s\\n", value);
} else {
printf("key not found\\n");
}
4. 獲取get參數示例
下面定義的 handler 演示了如何從請求參數里解析 字符串param1和整型變量param2:
esp_err_t index_handler(httpd_req_t *req)
{
char* query_str = NULL;
char param1_value[10] = {0};
int param2_value=0;
query_str = strstr(req->uri, "?");
if(query_str!=NULL){
query_str ++;
httpd_query_key_value(query_str, "param1", param1_value, sizeof(param1_value));
char param2_str[10] = {0};
httpd_query_key_value(query_str, "param2", param2_str, sizeof(param2_str));
param2_value = atoi(param2_str);
}
char resp_str[50] = {0};
snprintf(resp_str, sizeof(resp_str), "param1=%s, param2=%d", param1_value, param2_value);
httpd_resp_send(req, resp_str, strlen(resp_str));
return ESP_OK;
}
5. 獲取post參數示例
下面的示例代碼中根據httpd_req_t的content_len來分配一個緩沖區,并解析請求中的POST參數:
esp_err_t post_demo_handler(httpd_req_t *req)
{
char post_string[64];
int post_int=0;
if (req->content_len > 0)
{
// 從請求體中讀取POST參數
char *buf = malloc(req->content_len + 1);
int ret = httpd_req_recv(req, buf, req->content_len);
if (ret <= 0)
{
// 接收數據出錯
free(buf);
return ESP_FAIL;
}
buf[req->content_len] = '\\0';
// 解析POST參數
char *param_str;
param_str = strtok(buf, "&");
while (param_str != NULL)
{
char *value_str = strchr(param_str, '=');
if (value_str != NULL)
{
*value_str++ = '\\0';
if (strcmp(param_str, "post_string") == 0)
{
strncpy(post_string, value_str, sizeof(post_string));
}
else if (strcmp(param_str, "post_int") == 0)
{
post_int = atoi(value_str);
}
}
param_str = strtok(NULL, "&");
}
free(buf);
}
// 將結果打印輸出
printf("post_string=%s, post_int=%d\\n", post_string, post_int);
// 返回成功
httpd_resp_send(req, NULL, 0);
return ESP_OK;
}
httpd_uri_t post_uri = {
.uri = "/post",
.method = HTTP_POST,
.handler = post_demo_handler,
.user_ctx = NULL
};
三、基本用法完整示例
接前文的項目代碼示例,項目結構如下:
本文主要是使用其中的web_server.c文件。
1. 加載http_server模塊
在 CMakeLists.txt里寫上 :
idf_component_register(
REQUIRES "esp_http_server"
)
2. 建立 web_server.h 頭文件
#ifndef ESP32_LEARN_WEB_SERVER_H
#define ESP32_LEARN_WEB_SERVER_H
void http_server_task(void *pvParameters);
#endif //ESP32_LEARN_WEB_SERVER_H
3. web_server.c文件實現
#include "include/web_server.h"
#include
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_netif.h"
#include "esp_http_server.h"
#include "string.h"
/* Define the server port number */
#define SERVER_PORT 80
/* Define the routes for the web server */
const char *INDEX_HTML = "Hello, world";
/* Define the handler for the HTTP requests */
esp_err_t index_handler(httpd_req_t *req)
{
httpd_resp_send(req, INDEX_HTML, strlen(INDEX_HTML));
return ESP_OK;
}
/* Define the HTTP server configuration */
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = index_handler,
.user_ctx = NULL
};
/* Define the HTTP server task */
void http_server_task(void *pvParameters)
{
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = SERVER_PORT;
/* Start the HTTP server */
if (httpd_start(&server, &config) == ESP_OK) {
/* Register the routes */
httpd_register_uri_handler(server, &index_uri);
ESP_LOGI("HTTP_SERVER", "Server started");
}
/* Loop to keep the task running */
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
4. main.c 創建任務開啟web server
#include
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include
#include "network/include/wifi.h"
#include "network/include/wifi_sta.h"
#include "network/include/wifi_ap.h"
#include "network/include/web_server.h"
static const char *TAG = "main";
void app_main()
{
ESP_LOGE(TAG, "app_main");
// 初始化NVS存儲區
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// Wi-Fi初始化
ESP_LOGI(TAG, "Wi-Fi initialization");
wifi_initialize();
wifi_init_softap();
/* Start the HTTP server task */
xTaskCreate(http_server_task, "http_server_task", 4096, NULL, 5, NULL);
while (1) {
vTaskDelay(pdMS_TO_TICKS(500));
}
}
-
Web
+關注
關注
2文章
1285瀏覽量
70943 -
服務器
+關注
關注
13文章
9728瀏覽量
87439 -
IDF
+關注
關注
0文章
15瀏覽量
13137 -
函數
+關注
關注
3文章
4374瀏覽量
64385 -
ESP32
+關注
關注
21文章
1012瀏覽量
18955
發布評論請先 登錄
淺析Zephyr在ESP32上的啟動流程
ESP32 之 ESP-IDF 教學(三)——通用硬件定時器(Timer)

ESP32 之 ESP-IDF 教學(九)—— 串口通信(UART)

ESP32 之 ESP-IDF 教學(五(1))——ESP-IDF的CMake 構建系統(Build System)

ESP32 之 ESP-IDF 教學(八)—— 模數轉換器(ADC)

ESP32 之 ESP-IDF 教學WiFi篇(一)—— WiFi兩種模式

ESP32 之 ESP-IDF 教學(十)—— 電機控制器(MCPWM)

啟明去端分享| ESP32-S3如何實現tcp_client和tcp_server

啟明云端分享|ESP32-S3如何實現tcp_client和tcp_server

評論