1、什么是 ESP32 Web Server?
ESP32 Web Server是在ESP32微控制器上運(yùn)行的一個(gè)嵌入式網(wǎng)頁服務(wù)器。
它能夠處理HTTP請(qǐng)求并作出響應(yīng),使用戶可以通過網(wǎng)絡(luò)瀏覽器與設(shè)備進(jìn)行通訊和交互。
這種能力使得開發(fā)者可以輕松為硬件設(shè)備構(gòu)建用戶友好的接口,實(shí)時(shí)監(jiān)控和控制設(shè)備。
溫馨提醒:公眾號(hào)后臺(tái)私信ESP32 Web Server可獲取完整工程;
2、為什么要在ESP32上運(yùn)行Web Server
在 ESP32 上運(yùn)行 Web Server 有以下幾個(gè)優(yōu)勢:
前置條件:ESP32 天生自帶Wi-Fi,支持TCP/IP協(xié)議棧,是運(yùn)行Web Server的前置條件;
用戶友好:無需復(fù)雜的客戶端軟件,任何設(shè)備的瀏覽器都可以訪問和控制。
實(shí)時(shí)監(jiān)控與控制:通過簡單的網(wǎng)頁界面即可實(shí)時(shí)監(jiān)控設(shè)備狀態(tài),并執(zhí)行控制。
跨平臺(tái)兼容:網(wǎng)頁界面可以在任何支持瀏覽器的設(shè)備上訪問,無需考慮不同操作系統(tǒng)的兼容性。
易于配置:用戶可以通過網(wǎng)頁便捷設(shè)置設(shè)備參數(shù)。
一個(gè)實(shí)際的例子,我之前開發(fā)過《一個(gè)雙無線串口數(shù)據(jù)記錄工具》,為了對(duì)設(shè)備進(jìn)行配置,專門用QT開發(fā)了一個(gè)上位機(jī),但是由于只出了windows版本,導(dǎo)致其他系統(tǒng)如Linux/MacOS的用戶用不了,所以Web Server將是一個(gè)好的解決思路;

上圖基于QT實(shí)現(xiàn)的上位機(jī)工具;
3、Web Server 原理

Web Server 原理邏輯圖
本質(zhì)上Web Server就是利用tcp進(jìn)行http協(xié)議通信,其中ESP32作為Server而瀏覽器作為Client。
那么在HTTP中,最常見的就是GET和POST,這兩種方法,其中GET用于從Server端獲取資源,POST用于把客戶端的信息上報(bào)給Server;
除了常見的GET、POST之外還有PUT、DELETE,具體的作用可以參考HTTP協(xié)議,這里就不展開了;
我們平時(shí)訪問Web Server的時(shí)候我們一般會(huì)獲取到兩種資源;
一種是靜態(tài)資源,比如我們通過網(wǎng)頁獲取到一張圖片,不管我們什么時(shí)候訪問一般都不會(huì)變化;
另一種是動(dòng)態(tài)資源,比如我們?cè)L問股票網(wǎng)頁,股市的數(shù)據(jù)是實(shí)時(shí)變化的;
所以當(dāng)我們用瀏覽器訪問網(wǎng)站時(shí),如果訪問的是靜態(tài)資源,服務(wù)器則把我們預(yù)制的文件(HTML)下發(fā)到客戶端的。而這些文件(HTML)則是開發(fā)人員提前放到服務(wù)器上的。
當(dāng)訪問的是動(dòng)態(tài)資源時(shí),服務(wù)端會(huì)進(jìn)行運(yùn)算之后再把資源返回給客戶端;
客戶端也就是瀏覽器接收到這些資源,按照我們?cè)O(shè)計(jì)的頁面邏輯把接收到的數(shù)據(jù)展示出來就是我們看到的頁面了;
所以當(dāng)我們把ESP32作為Web Server時(shí)也需要把預(yù)制的網(wǎng)頁保存到ESP32的存儲(chǔ)單元上,可以是Flash或者外接SD卡等方式。
然后等客戶端(瀏覽器)發(fā)起了GET請(qǐng)求之后,Server端遵循HTTP協(xié)議,加裝指定的html文件之后響應(yīng)發(fā)送給客戶端,客戶端就加載顯示到瀏覽器上;
4、ESP32 上如何實(shí)現(xiàn)Web Server
我們這里以通過瀏覽器訪問esp32上的服務(wù)地址,然后瀏覽器上可以顯示一行字作為靜態(tài)資源,以及esp32的運(yùn)行時(shí)長-作為動(dòng)態(tài)資源,作為我們的實(shí)驗(yàn)例子;
根據(jù)上述的原理,我們需要以下3個(gè)功能
實(shí)現(xiàn)一個(gè)http server;
預(yù)先存放html文件到esp32上的SD卡(這里我們把SD卡掛載到esp32);
處理瀏覽器發(fā)送過來的GET方法,判斷如果是獲取靜態(tài)資源則然后返回對(duì)應(yīng)的html文件,如果是動(dòng)態(tài)獲取動(dòng)態(tài)資源則返回esp32的運(yùn)行時(shí)長;
基于以上提到的三個(gè)功能,我們接著看看ESP32上的實(shí)現(xiàn);
4.1 ESP32 HTTP Server的實(shí)現(xiàn)
在樂鑫的ESP-IDF SDK中,已經(jīng)有官方提供的esp_http_server組件了,所以我們并不需要手動(dòng)來造輪子;
組件位置:esp-idf-v5.2/components/esp_http_server主要的幾個(gè)函數(shù)如如下
創(chuàng)建一個(gè)http sever:esp_err_t httpd_start(httpd_handle_t *handle, const httpd_config_t *config);
注冊(cè)不同方法的處理函數(shù):esp_err_t httpd_register_uri_handler(httpd_handle_t handle,const httpd_uri_t *uri_handler);
在本實(shí)驗(yàn)中,我注冊(cè)了兩個(gè)處理函數(shù);
- index:用于返回頁面的靜態(tài)資源;
- uptime擁有返回設(shè)備的運(yùn)行時(shí)長,具體代碼如下:
staticconsthttpd_uri_tindex_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_tuptime = {
.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!"
};
statichttpd_handle_tstart_webserver(void)
{
httpd_handle_tserver =NULL;
httpd_config_tconfig = 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);
returnserver;
}
ESP_LOGI(TAG,"Error starting server!");
returnNULL;
}
4.2 預(yù)先存放html文件到esp32上的SD卡
這個(gè)步驟主要分為2步
驅(qū)動(dòng)SD卡,同理,ESP-IDF也給我們提供了組件vfs,看下例程很快就知道怎么用了,這里不展開了,如果確實(shí)不會(huì),可以直接看本工程源碼;
編寫html文件,這里我簡單寫了一個(gè)界面,效果如下圖

4.3 兩個(gè)處理函數(shù)的實(shí)現(xiàn)
index_page,直接加裝我們指定的index.html文件,然后返回;
staticesp_err_tindex_get_handler(httpd_req_t*req)
{
char* buf;
size_tbuf_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";
intfileSize = 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
{
intread_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");
}
returnESP_OK;
}
uptime_handler獲取系統(tǒng) 運(yùn)行時(shí)長,返回
staticesp_err_tuptime_handler(httpd_req_t*req)
{
uint64_tuptime_microseconds = esp_timer_get_time();// 獲取運(yùn)行時(shí)長(微秒)
uint32_tuptime_seconds = uptime_microseconds /1000000;// 轉(zhuǎn)換為秒
ESP_LOGI("System Uptime","System has been running for %lu seconds", uptime_seconds);
charresponse[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);
returnESP_OK;
}
5、實(shí)驗(yàn)效果
我們通過模組的IP或者綁定的mdns域名(什么是mdns可以見我之前的文章:你的ESP32設(shè)備有域名嗎?我的有!)訪問,可以獲取到靜態(tài)資源和動(dòng)態(tài)的模組運(yùn)行時(shí)長。
效果如下圖展示(運(yùn)行秒數(shù)在遞增)

6、結(jié)論
通過樂鑫提供的esp_http_server組件,我們?cè)贓SP32上成功運(yùn)行了一個(gè)簡單的web server,可以展示一些靜態(tài)和動(dòng)態(tài)資源。
后續(xù)我將用這個(gè)技術(shù)原理來實(shí)現(xiàn)更多的功能,比如:
通過網(wǎng)頁對(duì)ESP32進(jìn)行配網(wǎng);
如何基于網(wǎng)頁進(jìn)行實(shí)時(shí)通訊;
通過網(wǎng)頁控制ESP32的外設(shè)如GPIO、串口等功能;
溫馨提醒: 公眾號(hào)后臺(tái)私信ESP32 Web Server可獲取完整工程;
最后大家覺得這個(gè)技術(shù)還可以用來做什么有趣的應(yīng)用,歡迎留言討論。
歡迎大家點(diǎn)贊、關(guān)注、轉(zhuǎn)發(fā)將是我創(chuàng)作的最大動(dòng)力~
-
Web
+關(guān)注
關(guān)注
2文章
1281瀏覽量
70802 -
服務(wù)器
+關(guān)注
關(guān)注
13文章
9686瀏覽量
87280 -
ESP32
+關(guān)注
關(guān)注
20文章
1006瀏覽量
18829
發(fā)布評(píng)論請(qǐng)先 登錄
【FireBeetle ESP32-E開發(fā)板免費(fèi)試用】+wifi功能測試&搭建web服務(wù)器
Web Server服務(wù)器后臺(tái)表單處理程序
CH563運(yùn)行WEB-SERVER 20分鐘后發(fā)熱嚴(yán)重怎么改善?
ESP32 Web服務(wù)器可以向外部Rest API發(fā)起HTTP請(qǐng)求嗎?
如何實(shí)現(xiàn)ESP32上運(yùn)行運(yùn)行HTTP服務(wù)器?
使用ESP8266作為Web服務(wù)器,如何將網(wǎng)頁上傳到SPIFFS?
帶有BME280的ESP32 Web服務(wù)器的高級(jí)氣象站

最簡單DIY基于ESP8266的物聯(lián)網(wǎng)智能小車②(webserver服務(wù)器網(wǎng)頁高級(jí)遙控版)

MELSEC iQ R OPC UA服務(wù)器模塊用戶手冊(cè)(入門篇)

ESP32 CAM WEB服務(wù)器及入門指南

使用基于ESP32的Web服務(wù)器的互聯(lián)網(wǎng)控制LED

基于ESP32的簡易web服務(wù)器設(shè)計(jì)
ESP32 IDF創(chuàng)建WEB SERVER的流程

使用ESP32 Web服務(wù)器進(jìn)行家庭自動(dòng)化設(shè)置

評(píng)論