当前位置:网站首页>ESP32实验-自建web服务器配网02
ESP32实验-自建web服务器配网02
2022-06-27 00:13:00 【NULL_1969】
前言
上一篇提到了配网的简单方式,采用的json格式传递wifi账户和密码。这种方式优势是可以在esp32端直接用cjson库解析出json数据。但是不好的地方在于,html网页会复杂一点,需要将输入框中的数据转换成为json格式再发送。发送方式为post请求。那么有没有办法直接解析post默认格式数据呢。这一点本文将进行探讨。
另外一点,上一篇wifi从ap模式切换到station模式采用的是延时。这种方式也是不是很合理,这里进行了优化。
配网整个流程
这里对配网的整个流程进行梳理。
上电->wifi初始化为ap模式->开启http服务器->用户连上esp32wifi->浏览器输入esp网关地址默认是192.168.1.4->在页面上输入要连接的wifi名称和密码->点击页面上的发送按钮->浏览器通过post请求将wifi名称和密码发送到esp32->esp32解析出wifi名称和密码->退出wifiap模式,关闭http服务器->将wifi名称和密码作为参数,将wifi初始化为station模式。 配网完成。
嵌入html网页方法
网页文件的内容本质上是很长的字符串。那最简单的方法就是定义一个字符串数组。将数组内容填充为网页内容。
const char index_string[] =
"<!DOCTYPE html> \ <head> \ <meta charset=\"utf-8\"> \ <title>wifi config</title> \ </head>";
比如采用这样定义,这种方式一般用于给网页的反馈信息,比如404信息等。但是对于复杂一点的网页,这种方式显然就不太方便。要是能直接将用html工具设计生成的.htm格式的文件直接编译那不是更好了。这就是另外一种比较推荐的方式。

这次是直接借用了一个半开源的esp32桌面小电视的配网网页。如图上图。直接编译需要两个步骤。
1、修改CMakeList.txt文件
idf_component_register(SRCS ${main_src}
INCLUDE_DIRS "."
EMBED_FILES "upload_script.html" "wifi.html"
)
添加EMBED_FILES
2、调用编译出来的文件。
这个wifi.html编译出出来的文本文件怎么使用呢。wifi.html编译出来一般名称是默认的_binary_名称_类型_start。这个指针代编译出来文件的起始地址。_binary_名称_类型_end,代表结束地址。wifi.html的引用方式如下。
extern const unsigned char script_start[] asm("_binary_wifi_html_start");
extern const unsigned char script_end[] asm("_binary_wifi_html_end");
const size_t script_size = (script_end - script_start);
通过上述方式,便可以得到wifi.html这个大的数组。
http服务器配置
上面有了引用wifi.html的方法,但怎么能让其在浏览器中显示呢?这就要用到http服务器。
在浏览器中输入地址,浏览器会默认使用GET方法向http服务器请求数据。http服务器收到GET命令后,将要在浏览器中显示的数据发送给浏览器,这样浏览器就能显示出网页了。
在配网中,至少需要定义两个页面,一个是根页面,一个是点击网页上配网的按钮会触发进入的页面。根页面就是访问192.168.4.1进入的网页。具体定义如下:
定义页面结构体:
httpd_uri_t index_page = {
.uri = "/", //192.168.1.4
.method = HTTP_GET,
.handler = index_get_handler,
/* Let's pass response string in user * context to demonstrate it's usage */
.user_ctx = NULL,
};
其中uri为根目录,就是192.168.4.1这个页面。method定义该页面的触发方法,为GET方法。handler定义进入该页面后需要运行的函数。当浏览器访问了192.168.4.1的时候index_get_handler函数就会运行。
定义index_get_handler
static esp_err_t index_get_handler(httpd_req_t *req)
{
extern const unsigned char upload_script_start[] asm("_binary_wifi_html_start");
extern const unsigned char upload_script_end[] asm("_binary_wifi_html_end");
const size_t upload_script_size = (upload_script_end - upload_script_start);
/* Add file upload form and script which on execution sends a POST request to /upload */
httpd_resp_set_type(req,HTTPD_TYPE_TEXT);
httpd_resp_send(req, (const char *)upload_script_start, upload_script_size);
return ESP_OK;
}
这个函数就是将前文说到的wifi.html这个文件的内容返回给浏览器进行显示。
实际页面显示效果如下:
现在再说下当点击了保存并连接需要做的事情。点击这个按钮后,浏览器会调用get方法,将数据发送给http服务器。所以点击保存并连接后需要显示的页面定义及处理方法如下:
static const httpd_uri_t echo = {
.uri = "/",
.method = HTTP_POST,
.handler = echo_post_handler,
.user_ctx = NULL
};
static esp_err_t echo_post_handler(httpd_req_t *req)
{
char buf[100];
// char ssid[10];
// char pswd[10];
int ret, remaining = req->content_len;
while (remaining > 0) {
/* Read the data for the request */
if ((ret = httpd_req_recv(req, buf,
MIN(remaining, sizeof(buf)))) <= 0) {
if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
/* Retry receiving if timeout occurred */
continue;
}
return ESP_FAIL;
}
/* Send back the same data */
httpd_resp_send_chunk(req, buf, ret);
remaining -= ret;
esp_err_t e = httpd_query_key_value(buf,"ssid",wifi_name,sizeof(wifi_name));
if(e == ESP_OK) {
printf("ssid = %s\r\n",wifi_name);
}
else {
printf("error = %d\r\n",e);
}
e = httpd_query_key_value(buf,"password",wifi_password,sizeof(wifi_password));
if(e == ESP_OK) {
printf("pswd = %s\r\n",wifi_password);
}
else {
printf("error = %d\r\n",e);
}
/* Log data received */
ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
ESP_LOGI(TAG, "%.*s", ret, buf);
ESP_LOGI(TAG, "====================================");
}
// End response
httpd_resp_send_chunk(req, NULL, 0);
if(strcmp(wifi_name ,"\0")!=0 && strcmp(wifi_password,"\0")!=0)
{
xSemaphoreGive(ap_sem);
ESP_LOGI(TAG, "set wifi name and password successfully! goto station mode");
}
return ESP_OK;
}
当点击了保存并连接按钮后,echo_post_handler函数就会运行。这个函数的主要作用是,将受到的数据返回给浏览器进行显示,并且将post字符串中的wifi名称和密码分别解析出来,赋值给
char wifi_name[30]={0};
char wifi_password[30]={0};
这两个字符串数组。最大的名称和密码长度为29字节。
当解析出来的wifi_name和wifi_password值都不为空,则释放信号量,给wifi_station的任务,进入station模式。
当输入名称abc密码123456时,服务端收到的字符串为
ssid=abc&password=123456&citycode=
这是一种post方法通过格式的字符串。esp32提供了相应的解析方法。最早就是不知道这个解析方法,所以才会用到json格式发送wifi名称和密码。解析的函数如下:
esp_err_t e = httpd_query_key_value(buf,“ssid”,wifi_name,sizeof(wifi_name));
第一个参数为要解析的post字符串。
第二参数是传入需要解析的“键值对”中的键。
第三个参数为解析到的数据存储的数组。
最后一个参数为存储数组的长度
当解析成功后。e 的值为ESP_OK,通过这个来判断是否解析成功。
启动http服务器
启动服务,将上文的两个页面注册到服务器中。这个比较简单。
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, &echo);
httpd_register_uri_handler(server, &index_page);
#if CONFIG_EXAMPLE_BASIC_AUTH
httpd_register_basic_auth(server);
#endif
return server;
}
ESP_LOGI(TAG, "Error starting server!");
return NULL;
}
wifistation
当收到正确的wifi名称和密码后,esp32要将wifi转成station模式,并进行联网。这一部分是如何实现的?这里用到了一个单独的任务。
进入station模式前,需要将http服务器关闭,并且wifi重新初始化。**注意下面的这些函数都不能少。否则station模式会工作不正常。**这也是实验了好久才得出的。
void wifi_station_task(void * pvParameters)
{
uint32_t result =0;
while(1)
{
result = xSemaphoreTake(ap_sem,portMAX_DELAY);
if(result == pdPASS)
{
esp_wifi_stop();
esp_event_handler_unregister(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler
);
esp_netif_destroy_default_wifi(ap_netif);
esp_event_loop_delete_default();
esp_wifi_deinit();
esp_netif_deinit();
httpd_stop(server);
printf("hello \r\n");
ESP_LOGI(TAG,"led on");
wifi_init_sta(wifi_name,wifi_password);
}
printf("hello1 \r\n");
// vTaskDelay(pdMS_TO_TICKS(1000));
}
}
代码链接
具体代码链接如下,开发环境为官方的esp-idf。https://download.csdn.net/download/sinat_36568888/85750520
边栏推荐
猜你喜欢

【Mysql】时间字段默认设置为当前时间

Skills needing attention in selection and purchase of slip ring

Concepts de base de données Oracle

The fourth bullet of redis interview eight part essay (end)

解决unable to create a folder to save the sketch: mkdir sketch

解决STC8G1K08程序不能运行的问题和端口配置

JSON解析,ESP32轻松获取时间气温和天气

05 | standard design (Part 2): how to standardize the different styles of commit information, which are difficult to read?

基于SSMP的宠物医院管理系统
![统计无向图中无法互相到达点对数[经典建邻接表+DFS统计 -> 并查集优化][并查集手册/写的详细]](/img/cc/a0be58eddc72c22a9a6ee5c61eb81a.png)
统计无向图中无法互相到达点对数[经典建邻接表+DFS统计 -> 并查集优化][并查集手册/写的详细]
随机推荐
Record a bug caused by a line break
Oracle 數據庫基本知識概念
网络中的网络(套娃)
Live review | Ziya &ccf TF: Discussion on software supply chain risk management technology under cloud native scenario
全網最全的混合精度訓練原理
Technical dry goods | what is a big model? Oversized model? Foundation Model?
Skills needing attention in selection and purchase of slip ring
How to use Pinia (I) introduce Pinia into the project
指南针开户安全的吗?
安利!如何提优质的ISSUE?学霸是这样写的!
At present, which securities company is the best and safest to open an account for stock speculation?
光谱共焦如何测量玻璃基板厚度
Moher College - SQL injection vulnerability test (error reporting and blind note)
Great health industry annual must attend event, 2022 Shandong International Great Health Industry Expo
Mindspire, a domestic framework, cooperates with Shanshui nature conservation center to find and protect the treasure life in the "China water tower"
Concepts de base de données Oracle
1+1<2 ?! Interpretation of hesic papers
find_ Detailed use guide of CIRC
中信证券佣金 网上开户炒股安全吗
Big guys talk about the experience sharing of the operation of the cutting-edge mindspore open source community. Come up with a small notebook!