当前位置: 首页 > news >正文

ESP32运行轻量级 Web 服务器入门(基于ESP-IDF)

主要参考资料:
乐鑫官方ESP-IDF文档 HTTP服务器: https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32/api-reference/protocols/esp_http_server.html#
NVS入门(基于ESP-IDF): https://blog.csdn.net/qq_40773212/article/details/135595361

目录

  • 简介
  • HTTP Server参考代码
  • 用户在Web网页上输入Wi-Fi账号密码
    • NVS检查账号密码
    • 运行HTTP Server
    • 运行DNS服务器

简介

HTTP Server 组件提供了在 ESP32 上运行轻量级 Web 服务器的功能。

HTTP Server参考代码

/* URI 处理函数,在客户端发起 GET /uri 请求时被调用 */
esp_err_t get_handler(httpd_req_t *req)
{/* 发送回简单的响应数据包 */const char resp[] = "URI GET Response";httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);return ESP_OK;
}/* URI 处理函数,在客户端发起 POST/uri 请求时被调用 */
esp_err_t post_handler(httpd_req_t *req)
{/* 定义 HTTP POST 请求数据的目标缓存区* httpd_req_recv() 只接收 char* 数据,但也可以是* 任意二进制数据(需要类型转换)* 对于字符串数据,null 终止符会被省略,* content_len 会给出字符串的长度 */char content[100];/* 如果内容长度大于缓冲区则截断 */size_t recv_size = MIN(req->content_len, sizeof(content));int ret = httpd_req_recv(req, content, recv_size);if (ret <= 0) {  /* 返回 0 表示连接已关闭 *//* 检查是否超时 */if (ret == HTTPD_SOCK_ERR_TIMEOUT) {/* 如果是超时,可以调用 httpd_req_recv() 重试* 简单起见,这里我们直接* 响应 HTTP 408(请求超时)错误给客户端 */httpd_resp_send_408(req);}/* 如果发生了错误,返回 ESP_FAIL 可以确保* 底层套接字被关闭 */return ESP_FAIL;}/* 发送简单的响应数据包 */const char resp[] = "URI POST Response";httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);return ESP_OK;
}/* GET /uri 的 URI 处理结构 */
httpd_uri_t uri_get = {.uri      = "/uri",.method   = HTTP_GET,.handler  = get_handler,.user_ctx = NULL
};/* POST/uri 的 URI 处理结构 */
httpd_uri_t uri_post = {.uri      = "/uri",.method   = HTTP_POST,.handler  = post_handler,.user_ctx = NULL
};/* 启动 Web 服务器的函数 */
httpd_handle_t start_webserver(void)
{/* 生成默认的配置参数 */httpd_config_t config = HTTPD_DEFAULT_CONFIG();/* 置空 esp_http_server 的实例句柄 */httpd_handle_t server = NULL;/* 启动 httpd server */if (httpd_start(&server, &config) == ESP_OK) {/* 注册 URI 处理程序 */httpd_register_uri_handler(server, &uri_get);httpd_register_uri_handler(server, &uri_post);}/* 如果服务器启动失败,返回的句柄是 NULL */return server;
}/* 停止 Web 服务器的函数 */
void stop_webserver(httpd_handle_t server)
{if (server) {/* 停止 httpd server */httpd_stop(server);}
}

用户在Web网页上输入Wi-Fi账号密码

NVS检查账号密码

  //ui显示操作界面ui_init();//查看NVS有无存储账号nvs_handle_t my_nvs_handle;err = nvs_open("WI-FI", NVS_READWRITE, &my_nvs_handle);if (err != ESP_OK) {printf("Handle error\n");// Handle error} else {// Check if key existserr = nvs_get_str(my_nvs_handle, "SSID", SSID_value, &SSID_size);err = nvs_get_str(my_nvs_handle, "PWD", PWD_value, &PWD_size);if (err == ESP_OK) {//有账号密码,就直接连接并且显示连接界面ESP_LOGI(TAG, "Value of 'SSID' is: %s", SSID_value);ESP_LOGI(TAG, "Value of 'PWD' is: %s", PWD_value);lv_disp_load_scr(ui_WiFiconnect);} else if (err == ESP_ERR_NVS_NOT_FOUND) {// The key does not existESP_LOGI(TAG, "Key does not exist in NVS.");lv_disp_load_scr(ui_WiFireset);webConfigWifiLoop();} // Close the NVS handle when donenvs_close(my_nvs_handle);}

运行HTTP Server

void webConfigWifiLoop(void) 
{webConfigWifiInit();/* Allocate memory for server data */server_data = calloc(1, sizeof(struct file_server_data));if (!server_data) {printf("\r\nFailed to allocate memory for server data\r\n");return;}httpd_config_t config = HTTPD_DEFAULT_CONFIG();config.max_open_sockets = 1;config.backlog_conn = 1;config.lru_purge_enable = true;config.max_uri_handlers = 15;config.stack_size = 8192; // config.server_port = 89;config.uri_match_fn = httpd_uri_match_wildcard;printf("Starting Web server on port: %d\r\n", config.server_port);if (httpd_start(&webConfigWifi_httpd, &config) == ESP_OK){static httpd_uri_t root = {.uri = "/",.method = HTTP_GET,.handler = main_page_handler,.user_ctx  = NULL};root.user_ctx = server_data;httpd_register_uri_handler(webConfigWifi_httpd, &root);static httpd_uri_t wifi_data = {.uri       = "/wifi_data",.method    = HTTP_POST,.handler   = wifi_config_handler,.user_ctx  = NULL};wifi_data.user_ctx = server_data;httpd_register_uri_handler(webConfigWifi_httpd, &wifi_data);httpd_register_err_handler(webConfigWifi_httpd, HTTPD_404_NOT_FOUND, http_404_error_handler);}start_dns_server();while(1) {vTaskDelay(1000 / portTICK_PERIOD_MS);}
}

运行DNS服务器

在这部分,DNS服务器被设置为监听DNS查询,并针对所有类型A(A记录,用于IPv4地址解析)的查询回复软AP(Soft Access Point)的IP地址。

/*Sets up a socket and listen for DNS queries,replies to all type A queries with the IP of the softAP
*/
void dns_server_task(void *pvParameters)
{char rx_buffer[128];char addr_str[128];int addr_family;int ip_protocol;while (1) {struct sockaddr_in dest_addr;dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);dest_addr.sin_family = AF_INET;dest_addr.sin_port = htons(DNS_PORT);addr_family = AF_INET;ip_protocol = IPPROTO_IP;inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);if (sock < 0) {ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);break;}ESP_LOGI(TAG, "Socket created");int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));if (err < 0) {ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);}ESP_LOGI(TAG, "Socket bound, port %d", DNS_PORT);while (1) {ESP_LOGI(TAG, "Waiting for data");struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6socklen_t socklen = sizeof(source_addr);int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);// Error occurred during receivingif (len < 0) {ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);close(sock);break;}// Data receivedelse {// Get the sender's ip address as stringif (source_addr.sin6_family == PF_INET) {inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);} else if (source_addr.sin6_family == PF_INET6) {inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);}// Null-terminate whatever we received and treat like a string...rx_buffer[len] = 0;char reply[DNS_MAX_LEN];int reply_len = parse_dns_request(rx_buffer, len, reply, DNS_MAX_LEN);ESP_LOGI(TAG, "Received %d bytes from %s | DNS reply with len: %d", len, addr_str, reply_len);if (reply_len <= 0) {ESP_LOGE(TAG, "Failed to prepare a DNS reply");} else {int err = sendto(sock, reply, reply_len, 0, (struct sockaddr *)&source_addr, sizeof(source_addr));if (err < 0) {ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);break;}}}}if (sock != -1) {ESP_LOGE(TAG, "Shutting down socket");shutdown(sock, 0);close(sock);}}vTaskDelete(NULL);
}void start_dns_server(void)
{xTaskCreate(dns_server_task, "dns_server", 4096, NULL, 5, NULL);
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【天怡AI-注册安全分析报告-无验证方式导致安全隐患】
  • 从一个文本文件中挑选出符合条件的内容行
  • 大数据:快速入门Scala+Flink
  • 【word密码】word怎么限制格式,但可以修改文字?
  • 编写webpack插件自动上传sourceMap
  • Linux 基本指令(二)
  • 《linux系统》基础操作
  • 电力电网电线变电站输电线绝缘子无人机类数据集/农业植物病虫害类数据集/光伏板/工程煤矿矿场类数据集/道路类数据集
  • Android使用Flow封装一个FlowBus工具类
  • Linux-vim使用
  • Android Camera 预览角度和拍照保存图片角度相关
  • DNF Decouple and Feedback Network for Seeing in the Dark
  • 网络安全:构建数字世界的坚固防线
  • 【设计模式】万字详解:深入掌握五大基础行为模式
  • QT For Android开发-打开PPT文件
  • [iOS]Core Data浅析一 -- 启用Core Data
  • 78. Subsets
  • Android优雅地处理按钮重复点击
  • Angular数据绑定机制
  • Computed property XXX was assigned to but it has no setter
  • CSS实用技巧干货
  • Go 语言编译器的 //go: 详解
  • gulp 教程
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • javascript 哈希表
  • Promise面试题2实现异步串行执行
  • React 快速上手 - 07 前端路由 react-router
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • Shell编程
  • Vue2.x学习三:事件处理生命周期钩子
  • windows下使用nginx调试简介
  • 第13期 DApp 榜单 :来,吃我这波安利
  • 类orAPI - 收藏集 - 掘金
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • 设计模式 开闭原则
  • 深度学习入门:10门免费线上课程推荐
  • 使用docker-compose进行多节点部署
  • 小程序01:wepy框架整合iview webapp UI
  • 做一名精致的JavaScripter 01:JavaScript简介
  • Hibernate主键生成策略及选择
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • !$boo在php中什么意思,php前戏
  • # wps必须要登录激活才能使用吗?
  • #include到底该写在哪
  • ( 用例图)定义了系统的功能需求,它是从系统的外部看系统功能,并不描述系统内部对功能的具体实现
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (Java)【深基9.例1】选举学生会
  • (Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测
  • (二)hibernate配置管理
  • (十六)串口UART
  • (一)、软硬件全开源智能手表,与手机互联,标配多表盘,功能丰富(ZSWatch-Zephyr)
  • (一)Kafka 安全之使用 SASL 进行身份验证 —— JAAS 配置、SASL 配置
  • (一)UDP基本编程步骤
  • .h头文件 .lib动态链接库文件 .dll 动态链接库