ESP32 Web Server是在ESP32微控制器上运行的一个嵌入式网页服务器。
它能够处理HTTP请求并作出响应,使用户可以通过网络浏览器与设备进行通讯和交互。
这种能力使得开发者可以轻松为硬件设备构建用户友好的接口,实时监控和控制设备。
温馨提醒: 公众号后台私信 ESP32 Web Server 可获取完整工程;
在 ESP32 上运行 Web Server 有以下几个优势:
前置条件:ESP32 天生自带Wi-Fi,支持TCP/IP协议栈,是运行Web Server的前置条件;
用户友好:无需复杂的客户端软件,任何设备的浏览器都可以访问和控制。
实时监控与控制:通过简单的网页界面即可实时监控设备状态,并执行控制。
跨平台兼容:网页界面可以在任何支持浏览器的设备上访问,无需考虑不同操作系统的兼容性。
易于配置:用户可以通过网页便捷设置设备参数。
一个实际的例子,我之前开发过《一个双无线串口数据记录工具》,为了对设备进行配置,专门用QT开发了一个上位机,但是由于只出了windows版本,导致其他系统如Linux/MacOS的用户用不了,所以Web Server将是一个好的解决思路;

上图基于QT实现的上位机工具;

Web Server 原理逻辑图
本质上 Web Server 就是利用tcp进行http协议通信,其中ESP32作为Server而浏览器作为Client。
那么在HTTP中,最常见的就是GET和POST,这两种方法,其中GET用于从Server端获取资源,POST用于把客户端的信息上报给Server;
除了常见的GET、POST之外还有PUT、DELETE,具体的作用可以参考HTTP协议,这里就不展开了;
我们平时访问Web Server的时候我们一般会获取到两种资源;
一种是静态资源,比如我们通过网页获取到一张图片,不管我们什么时候访问一般都不会变化;
另一种是动态资源,比如我们访问股票网页,股市的数据是实时变化的;
所以当我们用浏览器访问网站时,如果访问的是静态资源,服务器则把我们预制的文件(HTML)下发到客户端的。而这些文件(HTML)则是开发人员提前放到服务器上的。
当访问的是动态资源时,服务端会进行运算之后再把资源返回给客户端;
客户端也就是浏览器接收到这些资源,按照我们设计的页面逻辑把接收到的数据展示出来就是我们看到的页面了;
所以当我们把ESP32作为Web Server时也需要把预制的网页保存到ESP32的存储单元上,可以是Flash或者外接SD卡等方式。
然后等客户端(浏览器)发起了GET请求之后,Server端遵循HTTP协议,加装指定的html文件之后响应发送给客户端,客户端就加载显示到浏览器上;
我们这里以通过浏览器访问esp32上的服务地址,然后浏览器上可以显示一行字作为静态资源,以及esp32的运行时长-作为动态资源,作为我们的实验例子;
根据上述的原理,我们需要以下3个功能
实现一个http server;
预先存放html文件到esp32上的SD卡(这里我们把SD卡挂载到esp32);
处理浏览器发送过来的GET方法,判断如果是获取静态资源则然后返回对应的html文件,如果是动态获取动态资源则返回esp32的运行时长;
基于以上提到的三个功能,我们接着看看ESP32上的实现;
在乐鑫的ESP-IDF SDK中,已经有官方提供的esp_http_server组件了,所以我们并不需要手动来造轮子;
组件位置:esp-idf-v5.2/components/esp_http_server主要的几个函数如如下
创建一个http sever:esp_err_t httpd_start(httpd_handle_t *handle, const httpd_config_t *config);
注册不同方法的处理函数: esp_err_t httpd_register_uri_handler(httpd_handle_t handle,const httpd_uri_t *uri_handler);
在本实验中,我注册了两个处理函数;
static consthttpd_uri_t index_page = {
.uri = "/",
.method = HTTP_GET,
.handler = index_get_handler,
/* Let's pass response string in user
* context to demonstrate it's usage */
.user_ctx = "Hello World!"
};
staticconsthttpd_uri_t uptime = {
.uri = "/uptime",
.method = HTTP_GET,
.handler = uptime_handler,
/* Let's pass response string in user
* context to demonstrate it's usage */
.user_ctx = "Hello World!"
};
static httpd_handle_t start_webserver(void)
{
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
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
ESP_LOGI(TAG, "Registering URI handlers");
httpd_register_uri_handler(server, &index);
httpd_register_uri_handler(server, &uptime);
return server;
}
ESP_LOGI(TAG, "Error starting server!");
returnNULL;
}
这个步骤主要分为2步
驱动SD卡,同理,ESP-IDF也给我们提供了组件vfs,看下例程很快就知道怎么用了,这里不展开了,如果确实不会,可以直接看本工程源码;
编写html文件,这里我简单写了一个界面,效果如下图

index_page,直接加装我们指定的index.html文件,然后返回;
static esp_err_t index_get_handler(httpd_req_t *req)
{
char* buf;
size_t buf_len;
/* Get header value string length and allocate memory for length + 1,
* extra byte for null termination */
buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1;
if (buf_len > 1) {
buf = malloc(buf_len);
/* Copy null terminated value string into buffer */
if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) {
ESP_LOGI(TAG, "Found header => Host: %s", buf);
}
free(buf);
}
/* Read URL query string length and allocate memory for length + 1,
* extra byte for null termination */
buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len > 1) {
buf = malloc(buf_len);
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
ESP_LOGI(TAG, "Found URL query => %s", buf);
}
free(buf);
}
/* Set some custom headers */
/* Send response with custom headers and body set as the
* string passed in user context*/
constchar* resp_str = (constchar*) req->user_ctx;
char *file_name = "/sdcard/html/index.html";
int fileSize = myLogFileSize(file_name);
FILE* fileFD = fopen( file_name, "r+" );
if( fileFD == NULL )
{
constchar* resp_str = (constchar*) req->user_ctx;
ESP_LOGI(TAG, "File not found => %s", file_name);
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
}
else
{
int read_len = 0;
char* fileBuf = NULL;
fseek( fileFD, 0, SEEK_END );
fileSize = ftell( fileFD );
fseek( fileFD, 0, SEEK_SET );
fileBuf = (char*)malloc( fileSize + 1 );
memset( fileBuf, 0, fileSize + 1 );
read_len = fread( fileBuf, 1, fileSize, fileFD );
fclose( fileFD );
resp_str = fileBuf;
ESP_LOGI(TAG, "Found file => %s", resp_str);
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
free( fileBuf );
}
/* After sending the HTTP response the old HTTP request
* headers are lost. Check if HTTP request headers can be read now. */
if (httpd_req_get_hdr_value_len(req, "Host") == 0) {
ESP_LOGI(TAG, "Request headers lost");
}
return ESP_OK;
}
uptime_handler获取系统 运行时长,返回
static esp_err_t uptime_handler(httpd_req_t *req)
{
uint64_t uptime_microseconds = esp_timer_get_time(); // 获取运行时长(微秒)
uint32_t uptime_seconds = uptime_microseconds / 1000000; // 转换为秒
ESP_LOGI("System Uptime", "System has been running for %lu seconds", uptime_seconds);
char response[64];
snprintf(response, sizeof(response), " %lu seconds", uptime_seconds);
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
// httpd_resp_set_hdr(req, "Content-Type", "text/plain");
httpd_resp_send(req, response, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
我们通过模组的IP或者绑定的mdns域名(什么是mdns可以见我之前的文章:你的ESP32设备有域名吗?我的有!)访问,可以获取到静态资源和动态的模组运行时长。
效果如下图展示(运行秒数在递增)

通过乐鑫提供的esp_http_server组件,我们在ESP32上成功运行了一个简单的web server,可以展示一些静态和动态资源。
后续我将用这个技术原理来实现更多的功能,比如:
通过网页对ESP32进行配网;
如何基于网页进行实时通讯;
通过网页控制ESP32的外设如GPIO、串口等功能;
温馨提醒: 公众号后台私信 ESP32 Web Server 可获取完整工程;
最后大家觉得这个技术还可以用来做什么有趣的应用,欢迎留言讨论。
欢迎大家点赞、关注、转发将是我创作的最大动力~
全部0条评论
快来发表一下你的评论吧 !