ESP-C3入门14. 实现基本的web server

news/2024/12/29 7:42:12/

ESP-C3入门14. 实现基本的web server

  • 一、ESP32 IDF创建WEB SERVER的流程
    • 1. 配置web服务器
    • 2. 注册 URI处理器
    • 3. 实现 URI处理器函数
    • 4. 处理HTTP请求
    • 5. 处理web socket连接
    • 6. 注册 URI 处理函数
    • 7. 启动HTTP服务器
    • 8. 发送响应
    • 9. 关闭 http 服务
  • 二、本要主要使用API的说明
    • 1. httpd_register_uri_handler
    • 2. httpd_handle_t
    • 3. httpd_query_key_value获取变量值
    • 4. 获取get参数示例
    • 5. 获取post参数示例
  • 三、基本用法完整示例
    • 1. 加载http_server模块
    • 2. 建立 web_server.h 头文件
    • 3. web_server.c文件实现
    • 4. main.c 创建任务开启web server

在这里插入图片描述

一、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 <http_parser.h>
#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 = "<html><body><h1>Hello, world</h1></body></html>";/* 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 <string.h>#include "freertos/FreeRTOS.h"
#include "freertos/task.h"#include <nvs_flash.h>
#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));}
}

http://www.ppmy.cn/news/29300.html

相关文章

《代码整洁之道》笔记

1章&#xff1a;专业人士要有专业人士素养&#xff0c;要有责任心&#xff0c;编写代码尽可能完善没有bug&#xff0c;有bug也要勇于承担。坚持学习&#xff0c;坚持练习&#xff0c;保证自己的专业技能。谦虚&#xff0c;相互学习&#xff0c;与顾客达成一致2章&#xff1a;说…

【算法笔记】前缀和与差分

前缀和与差分 算法是解决问题的方法与步骤。 在看一个算法是否优秀时&#xff0c;我们一般都要考虑一个算法的时间复杂度和空间复杂度。 现在随着空间越来越大&#xff0c;时间复杂度成为了一个算法的重要指标&#xff0c;那么如何估计一个算法的时间复杂度呢&#xff1f; …

【JavaSE】对象的比较

哈喽&#xff0c;大家好&#xff01;我是保护小周ღ&#xff0c;本期为大家带来的是Java中自定义类型&#xff08;对象&#xff09;的三种比较方式&#xff0c;equals 方法, Comparable 泛型接口, Comparator 泛型接口 。在日常编程中&#xff0c;我们常常会需要比较的问题&…

发现新大陆——原来软件开发根本不需要会编码(看我10分钟应用上线)

目录 一、前言 二、官网基础功能及搭建 三、体验过程 01、连接数据源 02、设计表单 03、流程设计 04、图表呈现 05、组织架构设置 五、效率评价 六、小结 一、前言 众所周知&#xff0c;每家公司在发展过程中都需要构建大量的内部系统&#xff0c; 如运营使用的用户…

前端学习第二阶段-第4章 移动web开发

4-1 媒体查询 01-移动WEB开发rem适配布局导读 02-rem单位 03-媒体查询语法简介 04-媒体查询案例背景变色 05-媒体查询rem实现元素动态大小变化 06-媒体查询引入资源 4-2 less介绍和使用 07-CSS的弊端 08-less简介以及安装 09-less变量 10-less编译easy less插件 11-less嵌套 12…

python+pytest接口自动化(6)-请求参数格式的确定

我们在做接口测试之前&#xff0c;先需要根据接口文档或抓包接口数据&#xff0c;搞清楚被测接口的详细内容&#xff0c;其中就包含请求参数的编码格式&#xff0c;从而使用对应的参数格式发送请求。例如某个接口规定的请求主体的编码方式为 application/json&#xff0c;那么在…

消息队列 面试题 整理

消息队列 为什么要使用消息队列&#xff1f; 异步解耦&#xff1a;关注的是通知而非处理。 流量削峰&#xff1a;将短时间内高并发的请求持久化&#xff0c;然后逐步处理&#xff0c;削平高峰期的请求。 日志收集&#xff1a; 事务最终一致性 系统间的消息通信方式&#xff…

全球十大正规贵金属交易价格走势app软件排名(2023口碑榜)

最近&#xff0c;随着人们对贵金属产品的需求增加&#xff0c;贵金属价格走势变得更加复杂。为了更好地掌握贵金属市场的变化&#xff0c;很多投资者都在寻找一款可以追踪贵金属价格走势的app软件。 针对这一需求&#xff0c;现在已经有多款贵金属价格走势软件涌现在市场上。…