【ESP32】ESP-IDF开发 | WiFi开发 | HTTP服务器

news/2025/2/13 17:34:31/

1. 简介

1.1 HTTP

        HTTP(Hyper Text Transfer Protocol),全称超文本传输协议,用于从网络服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还能确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本、图形等)。HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器(C/S)模型。HTTP是一个无状态的协议,基于TCP协议传输数据,默认使用端口80

1.1.1 请求

        HTTP把请求分成多种类型,其中最常用的是GET请求和POST请求

1. GET请求

        GET请求一般用于信息的获取,如访问网站使用的就是GET请求。GET请求仅仅只是获取资源信息,就像数据库查询一样,不会修改、增加数据,不会影响资源的状态。如果我们想在请求资源的同时附带数据,那么这些数据会被显式地放在请求URL上面

2. POST请求

        POST请求则表示可能会修改服务器上的资源,GET请求能做的,POST请求也能做。但最大的区别是请求参数的存放位置,POST请求会把参数隐式地放在请求报文中,所以对于敏感参数如账号密码等,会是更推荐的。

1.2 HTML

        HTML(HyperText Markup Language),全称超文本标记语言,是一种用于创建网页的标准标记语言。使用 HTML ,可以建立自己的 WEB 站点,HTML 运行在浏览器上,由浏览器来解析。

        最基础的HTML由以下组成:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My HTML</title>
</head>
<body>
<h1>This is a title</h1>
<p>This is a paragraph</p>
</body>
</html>
  • <!DOCTYPE html>:声明这时一个HTML文档;
  • <html></html>:HTML内容;
  • <head></head>:头部内容;
  • <body></body>:页面内容。

2. 例程

        这个例程会在ESP32上面搭建一个简单的HTTP服务器,供局域网中的设备访问,包含基本的GET和POST请求演示。

2.1 函数API 

2.1.1 启动HTTP服务器

esp_err_t httpd_start(httpd_handle_t *handle, const httpd_config_t *config);
  • handle:HTTP服务器句柄;
  • config:配置参数。 
typedef struct httpd_config {unsigned    task_priority;size_t      stack_size;BaseType_t  core_id;uint32_t    task_caps;uint16_t    server_port;uint16_t    ctrl_port;uint16_t    max_open_sockets;uint16_t    max_uri_handlers;uint16_t    max_resp_headers;uint16_t    backlog_conn;bool        lru_purge_enable;uint16_t    recv_wait_timeout;uint16_t    send_wait_timeout;void * global_user_ctx;httpd_free_ctx_fn_t global_user_ctx_free_fn;void * global_transport_ctx;httpd_free_ctx_fn_t global_transport_ctx_free_fn;bool enable_so_linger;int linger_timeout;bool keep_alive_enable;int keep_alive_idle;int keep_alive_interval;int keep_alive_count;httpd_open_func_t open_fn;httpd_close_func_t close_fn;httpd_uri_match_func_t uri_match_fn;
} httpd_config_t;

        配置参数比较多,ESP-IDF也提供了HTTPD_DEFAULT_CONFIG宏来初始化默认配置。如果要自定义,可以关注几个比较常用的:

  • stack_size:HTTP服务器任务的栈空间;
  • server_port:服务器端口,默认是80;
  • max_open_sockets:最大可开启socket,即可以连接的客户端数量;
  • max_uri_handlers:最大URI句柄数量;
  • recv_wait_timeout:接收超时时间;
  • send_wait_timeout:发送超时时间;
  • keep_alive_enable:网页保活使能;
  • keep_alive_idle:保活空闲时间;
  • keep_alive_interval:保活间隔时间;
  • keep_alive_count:保活包失败重传次数。

2.1.2 注册URI处理

esp_err_t httpd_register_uri_handler(httpd_handle_t handle, const httpd_uri_t *uri_handler);
  • handle:HTTP句柄;
  • uri_handler:URI处理结构体。
typedef struct httpd_uri {const char       *uri;httpd_method_t    method;esp_err_t (*handler)(httpd_req_t *r);void *user_ctx;
} httpd_uri_t;
  • uri:URI;
  • method:请求类型,格式如HTTP_XXX;
  • handler:处理函数;
  • user_ctx:用户上下文。

2.1.3 获取请求头字段内容长度

size_t httpd_req_get_hdr_value_len(httpd_req_t *r, const char *field);
  • r:HTTP请求句柄;
  • field:字段名。

2.1.4 获取请求头字段内容

esp_err_t httpd_req_get_hdr_value_str(httpd_req_t *r, const char *field, char *val, size_t val_size);
  • r:HTTP请求句柄;
  • field:字段名;
  • val:输出数组;
  • val_size:数组长度。

2.1.5 获取URL参数长度

size_t httpd_req_get_url_query_len(httpd_req_t *r)
  • r:HTTP句柄。

2.1.6 获取URL参数

esp_err_t httpd_req_get_url_query_str(httpd_req_t *r, char *buf, size_t buf_len)
  • r:HTTP句柄;
  • buf:输出数组;
  • buf_len:数组长度。

2.1.7 发送响应

esp_err_t httpd_resp_send(httpd_req_t *r, const char *buf, ssize_t buf_len);
esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, ssize_t buf_len);
static inline esp_err_t httpd_resp_sendstr(httpd_req_t *r, const char *str);
static inline esp_err_t httpd_resp_sendstr_chunk(httpd_req_t *r, const char *str);

         发送响应有几个函数,send结尾的就是一次性把数据发送完;send后面接str的就是发送字符串,这样就不需要传长度;chunk结尾的就是可以多次发送数据,最后一定要发一个长度为0的包,表示发送完成。

2.1.8 接收请求内容

int httpd_req_recv(httpd_req_t *r, char *buf, size_t buf_len)
  • r:HTTP句柄;
  • buf:输出数组;
  • buf_len:数组长度。

2.2 代码

#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_mac.h"
#include "nvs_flash.h"
#include "sys/socket.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "netdb.h"
#include "arpa/inet.h"
#include "esp_http_server.h"#include <string.h>#define TAG "app"static httpd_handle_t http_server;static const char index_html[] = " \
<!DOCTYPE html> \
<html> \<head> \<meta charset=\"utf-8\"> \<title>index</title> \</head> \
\<body> \<h1>Hello from ESP32</h1> \<form action=\"/hello\" method=\"post\"> \<label for=\"name\">What's your name:</label> \<input type=\"text\" id=\"name\" name=\"name\" required> \<input type=\"submit\" value=\"OK\"></button> \</form> \</body> \
</html> \
";static const char hello_html_template[] = " \
<!DOCTYPE html> \
<html> \<head> \<meta charset=\"utf-8\"> \<title>hello</title> \</head> \
\<body> \<h1>Oh, Hello %s</h1> \</body> \
\
</html> \
";static esp_err_t index_get_handler(httpd_req_t *req)
{/* 获取Host信息 */size_t buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1;if (buf_len > 1) {char *buf = malloc(buf_len);if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) {ESP_LOGI(TAG, "Get request to host: %s", buf);}free(buf);}/* 回复数据包 */httpd_resp_sendstr(req, index_html);return ESP_OK;
}static const httpd_uri_t index_uri = {.uri       = "/index",.method    = HTTP_GET,.handler   = index_get_handler,.user_ctx  = NULL
};static esp_err_t hello_post_handler(httpd_req_t *req)
{/* 获取Host信息 */size_t buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1;if (buf_len > 1) {char *buf = malloc(buf_len);if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) {ESP_LOGI(TAG, "Post request to host: %s", buf);}free(buf);}/* 获取内容长度 */int len = 0;{char *buf = malloc(128);memset(buf, 0, 128);if (httpd_req_get_hdr_value_str(req, "Content-Length", buf, 128) != ESP_OK) {ESP_LOGE(TAG, "Get content length failed");return ESP_FAIL;}len = atoi(buf) + 1;free(buf);}/* 获取表单数据 */char *buf = malloc(len);memset(buf, 0, len);if (httpd_req_recv(req, buf, len) <= 0) {ESP_LOGE(TAG, "Receive request content failed");return ESP_FAIL;}if (strstr(buf, "name=") == NULL) {ESP_LOGE(TAG, "Can't found fleid \"name\"");free(buf);return ESP_FAIL;}/* 发送数据 */char *hello_html = malloc(1024);snprintf(hello_html, 1024, hello_html_template, buf + strlen("name="));httpd_resp_sendstr(req, hello_html);free(buf);free(hello_html);return ESP_OK;
}static const httpd_uri_t hello_uri = {.uri       = "/hello",.method    = HTTP_POST,.handler   = hello_post_handler,.user_ctx  = NULL
};static void wifi_event_handler(void* arg,esp_event_base_t event_base,int32_t event_id,void* event_data)
{if (event_base == IP_EVENT) {if (event_id == IP_EVENT_STA_GOT_IP) {httpd_config_t config = HTTPD_DEFAULT_CONFIG();if (httpd_start(&http_server, &config) == ESP_OK) {httpd_register_uri_handler(http_server, &index_uri);httpd_register_uri_handler(http_server, &hello_uri);}ESP_LOGI(TAG, "HTTP server on port %d", config.server_port);}} else if (event_base == WIFI_EVENT) {if (event_id == WIFI_EVENT_STA_DISCONNECTED) {httpd_stop(http_server);ESP_LOGI(TAG, "HTTP server stopped");} else if (event_id == WIFI_EVENT_STA_START) {esp_wifi_connect();}}
}int 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());ESP_ERROR_CHECK(nvs_flash_init());}/* 初始化WiFi协议栈 */ESP_ERROR_CHECK(esp_netif_init());ESP_ERROR_CHECK(esp_event_loop_create_default());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,&wifi_event_handler,NULL,NULL));ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,IP_EVENT_STA_GOT_IP,&wifi_event_handler,NULL,NULL));wifi_config_t wifi_config = {.sta = {.ssid = "Your SSID",.password = "Your password",.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK,},};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());return 0;
}

         WiFi基站模式和AP连接在之前的文章有讲过,这里不再赘述。

        AP连接成功后会启动HTTP服务器,我这里全部使用默认的配置,注册了两个URI处理,一个是“/index”,用来演示GET请求,获取主页;一个是“/hello”,用来演示POST请求。

        第一个URI处理函数,演示一下请求头字段的获取,一般先获取字段的长度,接着请求对应大小的堆内存,再copy数据到数组中。不建议在函数内直接定义数组,因为处理函数是在HTTP任务中调用的,这样做很容易导致栈溢出。最后就是返回HTML页面文本给客户端。

如果ESP32在接受请求时报413错误,在SDK的配置文件(sdkconfig)中,修改CONFIG_HTTPD_MAX_REQ_HDR_LEN配置,增大请求头的长度。

         这个URI处理会返回一个HTML页面,如果你用的是浏览器请求的话,就会有自动解析并显示画面。

https://i-blog.csdnimg.cn/direct/9ae8918db48244a6a4bd67b603bca906.png" />

        这个HTML包含一个标题和一些表单控件,我们可以在文本框这里填写自己的名字,点击“OK”按钮,会向ESP32提交表单数据,其实就是向“/hello”这个URI发起POST请求。

        对于POST请求,在HTTP的请求头中会有一个“Content-Length”字段来描述数据包的大小。我们首先获取这个字段内容,然后去请求相应的内存空间,最后copy数据包数据到数组中;如果数据包非常大的话也可以多次获取。

        对于表单数据,一般都是以键值对的形式组成的,中间用等于号连接。接收到POST请求后需要返回对应的数据,这里就是HTML文件,我们把表单获取到的数据附到HTML文档中,显示的效果如下。

https://i-blog.csdnimg.cn/direct/0877997dcc444e01bad275bfa8a8e21f.png" /> 


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

相关文章

腾讯云HAI部署DeepSeek结合Ollama API搭建智能对话系统

前言 本文将详细介绍如何在腾讯云HAI平台上部署DeepSeek模型&#xff0c;并配置使用Ollama API服务以实现对外部请求的支持。通过对前期准备、部署流程、API服务配置及使用的详细阐述&#xff0c;希望能为读者提供一个全面且实用的指南&#xff0c;助力AI应用的高效开发和部署…

如何从0开始将vscode源码编译、运行、打包桌面APP

** 网上关于此的内容很少&#xff0c;今天第二次的完整运行了&#xff0c;按照下文的顺序走不会出什么问题。最重要的就是环境的安装&#xff0c;否则极其容易报错&#xff0c;请参考我的依赖版本以及文末附上的vscode官方指南 ** 第一步&#xff1a;克隆 VSCode 源码 首先…

使用Python爬虫获取1688 App原数据API接口

一、引言 在电商领域&#xff0c;数据是企业决策、市场分析和产品优化的关键要素。1688作为国内领先的B2B电商平台&#xff0c;汇聚了海量的商品信息和交易数据。通过获取1688 App的原数据API接口&#xff0c;企业可以精准把握市场动态&#xff0c;了解竞争对手的策略&#xf…

smart代理VSwebshare哪家http代理商的IP代理综合质量由于911代理?

在选择HTTP代理商时&#xff0c;综合考虑其IP代理的质量至关重要&#xff0c;本文将比较Smart代理与Webshare两家HTTP代理商在多个方面优于911代理&#xff0c;并解释为何需要进行这种代理商之间的对比。 如何考核一家HTTP代理商的IP代理综合质量&#xff1f; 为了评估一家HTT…

ASP.NET Core SignalR的分布式部署

假设聊天室程序被部署在两台服务器上&#xff0c;客户端1、2连接到了服务器A上的ChatRoomHub&#xff0c;客户端3、4连接到服务器B上的ChatRoomHub&#xff0c;那么客户端1发送群聊消息时&#xff0c;只有客户端1、2能够收到&#xff0c;客户端3、4收不到&#xff1b;在客户端3…

解决MybatisPlus updateById更新数据时将没传的数据也更新成了null

首先&#xff0c;MybatisPlus在调用自带的更新接口updateById时&#xff0c;如果没加任何配置&#xff0c;默认是不会将前端没传的数据也更新成null的。即MyBatisPlus不会更新传入实体中为null的字段&#xff0c;只会更新设置了不为null的值。 如果发现没传的也更新成null了的话…

C++ 实践扩展(Qt Creator 联动 Visual Studio 2022)

​ 这里我们将在 VS 上实现 QT 编程&#xff0c;实现如下&#xff1a; 一、Vs 2022 配置&#xff08;若已安装&#xff0c;可直接跳过&#xff09; 点击链接&#xff1a;​​​​​Visual Studio 2022 我们先去 Vs 官网下载&#xff0c;如下&#xff1a; 等待程序安装完成之…

[论文笔记] Deepseek-R1R1-zero技术报告阅读

启发: 1、SFT&RL的训练数据使用CoT输出的格式,先思考再回答,大大提升模型的数学与推理能力。 2、RL训练使用群体相对策略优化(GRPO),奖励模型是规则驱动,准确性奖励和格式化奖励。 1. 总体概述 背景与目标 报告聚焦于利用强化学习(RL)提升大型语言模型(LLMs)…