文章目录
- 1 准备
- 2 移植
- 2.1 softAP工程移植到simple工程中
- 2.2 移植注意事项
- 3 验证
- 4 添加HTML
- 4.1 浏览器显示自己编译的html
- 4.2 在使用html发数据给ESP32
- 4.3 HTML 内容
- 4.4 更新 html_test.html
1 准备
- 参考工程
Espressif\frameworks\esp-idf-v5.2.1\examples\wifi\getting_started\softAP
softAP工程演示将ESP32作为AP,即热点,使手机等终端可以连接 - 参考工程
Espressif\frameworks\esp-idf-v5.2.1\examples\protocols\http_server\simple
simple工程演示ESP32作为服务器,响应客户端的请求
2 移植
2.1 softAP工程移植到simple工程中
-
在simple工程添加自定义ap文件
-
在CMakeLists.txt 添加新增 .c 文件
-
将 softAP 工程程序复制到 user_wifi_ap.c 中
注意:1)可以删除重复的共同的初始化函数; -
简化后的
app_main
函数void app_main(void) {static httpd_handle_t server = NULL;ESP_ERROR_CHECK(nvs_flash_init());ESP_ERROR_CHECK(esp_netif_init());ESP_ERROR_CHECK(esp_event_loop_create_default());wifi_init_softap();/* Register event handlers to stop the server when Wi-Fi or Ethernet is disconnected,* and re-start it upon connection.*/ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server));ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server));/* Start the server for the first time */server = start_webserver(); }
2.2 移植注意事项
-
设置AP WiFi密码时,长度要大于等于8
#define EXAMPLE_ESP_WIFI_PASS "12345678" // 密码需要 >= 8
-
WIFI_AUTH_WPA3_PSK和WIFI_AUTH_WPA2_PSK模式
如果使用WIFI_AUTH_WPA3_PSK模式,可能部分电脑和手机不能连接该WiFi热点
wifi_config_t wifi_config = {.ap = {... #ifdef CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT.authmode = WIFI_AUTH_WPA3_PSK,.sae_pwe_h2e = WPA3_SAE_PWE_BOTH, #else /* CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT */.authmode = WIFI_AUTH_WPA2_PSK, #endif...},};
关闭WIFI_AUTH_WPA3_PSK使能
3 验证
- 编译、烧录、打开串口,
idf.py -p COM11 flash monitor
; - 使用手机连接WiFi;
- 使用手机浏览器输入
192.168.4.1/hello
- 手机浏览器反馈数据与程序一致
4 添加HTML
注意下文所有与html有关的功能都要对应 html_test.html
详细在文末
4.1 浏览器显示自己编译的html
-
添加一份html文件并压缩为 .gz格式 到 main文件夹下
.gz 格式可以使用 7-Zip压缩软件 -
打开 CMakeLists.txt文件添加 html_test.html.gz
-
在对应的 .c 文件添加如下程序
extern const unsigned char html_test_html_gz_start[] asm("_binary_html_test_html_gz_start"); extern const unsigned char html_test_html_gz_end[] asm("_binary_html_test_html_gz_end");static esp_err_t index_handler(httpd_req_t *req) {size_t CNB_803_web_html_gz_len = html_test_html_gz_end - html_test_html_gz_start;ESP_LOGI(TAG, "web refresh");httpd_resp_set_type(req, "text/html");httpd_resp_set_hdr(req, "Content-Encoding", "gzip");httpd_resp_set_hdr(req, "X-Content-Type-Options", "nosniff");return httpd_resp_send(req, (const char *)html_test_html_gz_start, CNB_803_web_html_gz_len); }httpd_uri_t index_uri = {.uri = "/",.method = HTTP_GET,.handler = index_handler,.user_ctx = NULL};static httpd_handle_t start_webserver(void){if (httpd_start(&server, &config) == ESP_OK) {// Set URI handlershttpd_register_uri_handler(server, &index_uri); // 添加该函数}}
-
编译烧录打开串口,电脑连接无线热点,并在浏览器输入
192.168.4.1
,效果如下
4.2 在使用html发数据给ESP32
-
添加程序
static esp_err_t html_cmd_handler(httpd_req_t *req) {char *buf = NULL;char _cmd_value[16];static char json_response[256];char *p = json_response;if (html_data_parse(req, &buf) != ESP_OK || httpd_query_key_value(buf, "key_value", _cmd_value, sizeof(_cmd_value)) != ESP_OK) // 这里key_value改变时需要网页端同步修改{free(buf);httpd_resp_send_404(req);return ESP_FAIL;}free(buf);uint8_t cmd_value = atoi(_cmd_value);ESP_LOGI(TAG, "receive from html cmd: %d ", cmd_value);if (cmd_value == 14){*p++ = '{';p += sprintf(p, "\"frame_sensitivity\":%u,", html_data.sensitivity);*p++ = '}';*p++ = 0;}else{html_data.cmd = cmd_value;*p++ = '{';p += sprintf(p, "\"cnb_board\":\"%s\",", PROJECT_NAME);p += sprintf(p, "\"cnb_ver\":\"%s\"", PROJECT_VERSION);*p++ = '}';*p++ = 0;}httpd_resp_set_type(req, "application/json");// Access-Control-Allow-Origin配置成*什么意思?意思是允许任意网站跨域访问该服务端口,在这种情况下,任意一个前端程序都可以随意集成该端口内容,实现数据获取。httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");httpd_resp_set_hdr(req, "X-Content-Type-Options", "nosniff");return httpd_resp_send(req, json_response, strlen(json_response)); }httpd_uri_t html_cmd_uri = {.uri = "/key_value",.method = HTTP_GET,.handler = html_cmd_handler,.user_ctx = NULL};static httpd_handle_t start_webserver(void){if (httpd_start(&server, &config) == ESP_OK) {// Set URI handlershttpd_register_uri_handler(server, &html_cmd_uri ); // 添加该函数}}
-
编译烧录打开串口,效果如下
4.3 HTML 内容
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="shortcut icon" href="#" /><title>html-test</title><style>/* style 属于CSS部分 */body {font-family: Arial, Helvetica, sans-serif;background: #413f3f;color: #EFEFEF;font-size: 16px}/* 包裹“按键”、“下拉选项”的背景 */ .ctr_field_bkg {background-color: #e0cfcf;height: 100px;width: 350px;position: absolute;left: 10px;top: 50px;}/* 单个按键的属性 */.button_key_1 {left: 50px;top: 10px;}/* 按键的父属性 */.button {display: block;padding: 0 5px;border: 0;position: relative;height: 30px;width: 90px;left: 100px;top: 10px;cursor: pointer;/* cursor:用来设置鼠标光标/指针在指定元素上的形状; */color: #fff;background: #5a8194;border-radius: 5px;font-size: 16px;}/* 鼠标移动到按键时,按键会变为该颜色*/.button:hover {background-color: #f88020;color: white;}/* 下拉选项 */.drop_down_option {/* display: flex; */cursor: pointer;/* 用来设置鼠标光标/指针在指定元素上的形状; */flex-wrap: nowrap;line-height: 22px;position: relative;margin: 5px 0;border-radius: 15px;left: 100px;top: 20px;font-size: 16px;color: #fff;background: #a0b931;display: inline-block;}/* wifi修改按键入口 */.wifi_entry_button {display: block;padding: 0 5px;border: 0;position: absolute;height: 30px;width: 350px;left: 10px;top: 160px;cursor: pointer;/* cursor:用来设置鼠标光标/指针在指定元素上的形状; */color: #fff;background: #96b1b9;border-radius: 5px;font-size: 16px;}/* wifi 弹窗的整体属性 */.wifi_input_windows {width: 350px;height: 250px;border: 3px solid #f8f8f8;visibility: hidden;position: absolute;z-index: 999;opacity: 1;overflow: hidden;background-color: white;text-align: center;margin-top: 10%;margin-left: 1%;border-radius: 10px;}.open {visibility: visible;opacity: 1;}.wifi_input_windows_box {margin-top: 15px;width: 100%;height: 40px;color: #0e0d0c;}/* WiFi 弹窗中 “wifi账号与密码修改” 字体 */.wifi_input_windows_box .login_logo {text-align: left;font-size: 20px;font-weight: 300;padding-left: 80px;float: left;}/* WiFi 弹窗中右上角的X */.wifi_input_windows_box .close {width: 20px;height: 20px;color: #d83434;text-align: center;line-height: 20px;border: 1px solid #5d5d5d;border-radius: 50%;float: right;padding-top: 0px;margin-right: 10px;font-size: 12px;}/* 鼠标移动到对应位置时,按键会变为该颜色*/.wifi_input_windows_box .close:hover {cursor: pointer;background-color: #aa1111;color: white;}hr {background-color: #F8F8F8;}.wifi_input_windows_box .submit_1 {border: 2px solid #f88020;height: 40px;width: 80px;background-color: white;}.wifi_input_windows_box .submit_1:hover {background-color: #f88020;color: white;}.msg {display: inline-block;font-size: 16px;/* margin-top: 2%; */margin-left: 5%;color: #d83434;}</style>
</head><body><section class="main"><div id="logo"><label for="nav-toggle-cb" id="nav-toggle">☰ html test</label></div><canvas id="frame_check" width="340" height="200" class="detection_box">您的浏览器不支持 HTML5 canvas 标签,感应框无法显示。</canvas><!-- <div class="ctr_field_bkg"> --><div class="ctr_field_bkg"><button class="button button_key_1" id="key_id_1" value="2">按键</button><select title="下拉选项" class="drop_down_option" id="down_option"><option value="9">选项1</option><option value="10">选项2</option><option value="11">选项3</option></select></div><div><button class="wifi_entry_button" id="btn_1">点击修改wifi账号与密码</button><div class="wifi_input_windows"><form><div class="wifi_input_windows_box"><div class="login_logo">wifi账号与密码修改</div><div class="close">X</div></div><hr><div class="wifi_input_windows_box">wifi 账号输入:<input type="text" name="ssid" id="id_ssid" value=""><span class="msg"id="id_span_ssid"></span></div><div class="wifi_input_windows_box">wifi 密码输入:<input type="text" name="password" id="id_password" value=""><span class="msg" id="id_span_password"></span></div><div class="wifi_input_windows_box"><input class="submit_1" type="submit" name="submit" id="id_submit" value="提 交"></div></form></div></div></section><script>var baseHost = document.location.origin;var btn_1 = document.getElementById("btn_1");var close = document.getElementsByClassName("close");var wifi_input_windows = document.getElementsByClassName("wifi_input_windows");function fetchUrl(url, cb) {fetch(url).then(response => response.json()).then(function (data) {cb(200, data);}).catch(function (err) {cb(-1, err);});}function transmit_cmd_value(key_value, cb) {fetchUrl(`${baseHost}/key_value?key_value=${key_value}`, function (code, txt) {cb(code, txt);// cb(200, txt); //测试用});}function transmit_wifi_value(wifi_ssid, wifi_password, cb) {fetchUrl(`${baseHost}/wifi?wifi_ssid=${wifi_ssid}&wifi_password=${wifi_password}`, function (code, txt) {cb(code, txt);// cb(200, txt); //测试用});}btn_1.addEventListener('click', function () {wifi_input_windows[0].className = "wifi_input_windows open";})close[0].addEventListener('click', function () {var span_password = document.getElementById("id_span_password");var span_ssid = document.getElementById("id_span_ssid");span_password.innerHTML =''span_ssid.innerHTML =''wifi_input_windows[0].className = "wifi_input_windows";})const ssid = document.querySelector('[name=ssid]')ssid.addEventListener('change', verifyName)function verifyName() {const span = ssid.nextElementSiblingconst reg = /^[a-zA-Z0-9-_]{2,16}$/if (!reg.test(ssid.value)) {span.innerHTML = '2到16个字符且只能包含数字、字母、下划线'return false}span.innerHTML = ''return true}const password = document.querySelector('[name=password]')password.addEventListener('change', verifyPassword)function verifyPassword() {const span = password.nextElementSiblingconst reg = /^[a-zA-Z0-9-_]{8,16}$/if (!reg.test(password.value)) {span.innerHTML = '8到16个字符且只能包含数字、字母、下划线'return false}span.innerHTML = ''return true}const form = document.querySelector('form')form.addEventListener('submit', function (e) {if ((!verifyName()) || (!verifyPassword())) {e.preventDefault()}else {transmit_wifi_value(ssid.value, password.value, function (code, txt) {if (code != 200) {alert('Error[' + code + ']: ' + txt);} else {// console.log(txt.cnb_board);}});alert('请重新连接新的wifi账号与密码。')}})// 监听来自MCU的信号document.addEventListener('DOMContentLoaded', function (event) {function update_frame() {transmit_cmd_value(14, function (code, txt) {if (code != 200) {alert('Error[' + code + ']: ' + txt);} else {console.log(txt);a_area_ctx.clearRect(0, 0, document.getElementById('frame_check').width, document.getElementById('frame_check').height);document.getElementById("down_option").selectedIndex = txt.frame_sensitivity; }});}//setInterval(update_frame, 1000); // 网页更新周期const sensitivity_option = document.getElementById('down_option')sensitivity_option.onchange = () => {transmit_cmd_value(sensitivity_option.value, function (code, txt) {if (code != 200) {alert('Error[' + code + ']: ' + txt);} else {console.log(txt.cnb_board);}});}const ButtonKey_1 = document.getElementById('key_id_1');ButtonKey_1.onclick = () => {let key_value = parseInt(document.getElementById('key_id_1').value);transmit_cmd_value(key_value, function (code, txt) {if (code != 200) {alert('Error[' + code + ']: ' + txt);} else {console.log(txt.cnb_board);}});}})</script>
</body></html>
4.4 更新 html_test.html
- 直接编辑html_test.html,使用浏览器打开就可以直接看效果
- 更新,使用7-Zip压缩软件重新压缩
- 编译烧录即可