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

ESP32-IDF http请求崩溃问题分析与解决

文章目录

  • esp32s3 http请求崩溃
  • 问题代码讨论
  • 修正后不崩溃的代码
  • esp32相关文章

ESP32S3板子, 一运行http请求百度网站的例子, 就会panic死机, 记录下出现及解决过程.

esp32s3 http请求崩溃

一执行http请求的perform就会崩溃,
打印如图
在这里插入图片描述
ESP32-IDF 的http请求代码是根据官方demo来改的,
第一步先连接wifi,
连接上后执行http get请求百度网站.
理论上写法是没问题的,但是运行到板子上发现很容易崩溃.

问题代码讨论

会在可能有问题的地方注释,有4个问题点,具体看代码 ,
最主要问题是运行内存有限, 容易发生栈或堆溢出或越界导致崩溃.

#include <stdio.h>
#include <pthread.h>#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_tls.h"
#include "esp_crt_bundle.h"
#include "esp_http_client.h"static const char *TAG = "HTTP_REQUEST";// 这里的buffer要适当, 如果改太大了, 比如10240就可能导致死机, 要根据实际运行结果做调整
#define MAX_HTTP_OUTPUT_BUFFER 2048
#define HTTP_URL "http://www.baidu.com"// HTTP 请求的处理函数
esp_err_t http_event_handler(esp_http_client_event_t *evt)
{// 缓存http响应的bufferstatic char *output_buffer;// 已经读取的字节数static int output_len;switch(evt->event_id) {case HTTP_EVENT_ERROR:ESP_LOGD(TAG, "HTTP_EVENT_ERROR");break;case HTTP_EVENT_ON_CONNECTED:ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");break;case HTTP_EVENT_HEADER_SENT:ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");break;case HTTP_EVENT_ON_HEADER:ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);break;case HTTP_EVENT_ON_DATA:ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);if (!esp_http_client_is_chunked_response(evt->client)) {if (evt->user_data) {// 问题1: 这里没有做防溢出限制, 当http返回的数据长度趤过预留的buffer大小MAX_HTTP_OUTPUT_BUFFER时就会溢出崩溃memcpy(evt->user_data + output_len, evt->data, evt->data_len);} else {if (output_buffer == NULL) {// 问题2: 这里直接用malloc申请http返回的数据长度的堆空间, 实测在esp32s3板子上跑会崩溃output_buffer = (char *) malloc(esp_http_client_get_content_length(evt->client));output_len = 0;if (output_buffer == NULL) {ESP_LOGE(TAG, "Failed to allocate memory for output buffer");return ESP_FAIL;}}memcpy(output_buffer + output_len, evt->data, evt->data_len);}output_len += evt->data_len;}break;case HTTP_EVENT_ON_FINISH:ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");if (output_buffer != NULL) {// Response is accumulated in output_buffer. Uncomment the below line to print the accumulated response// ESP_LOG_BUFFER_HEX(TAG, output_buffer, output_len);free(output_buffer);output_buffer = NULL;}output_len = 0;break;case HTTP_EVENT_DISCONNECTED:ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");if (output_buffer != NULL) {free(output_buffer);output_buffer = NULL;}output_len = 0;break;}return ESP_OK;
}void request(const char *url) {printf("request  -----------1\n");// 响应结果放在这里char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};// 创建一个 HTTP 客户端配置esp_http_client_config_t config = {.method = HTTP_METHOD_GET,.url = url,.event_handler = http_event_handler,.user_data = local_response_buffer,.disable_auto_redirect = true,};// 创建一个 HTTP 客户端并执行 GET 请求esp_http_client_handle_t client = esp_http_client_init(&config);printf("request  -----------2\n");esp_err_t err = esp_http_client_perform(client); // 请求百度网页时,一执行这行系统就会崩溃printf("request  -----------3\n");// 检查请求是否成功if (err == ESP_OK) {int len =  esp_http_client_get_content_length(client);ESP_LOGI(TAG, "Status = %d, content_length = %d",esp_http_client_get_status_code(client),//状态码len);//数据长度} else {printf("HTTP GET request failed: %s\n", esp_err_to_name(err));}printf("Response: %.*s\n", strlen(local_response_buffer), local_response_buffer);//断开并释放资源esp_http_client_cleanup(client);printf("request  -----------4\n");
}void http_test_task(void *arg)
{sleep(15);request(HTTP_URL);vTaskDelete(NULL);
}/*** @brief WiFi 的事件循环Handler* @param arg* @param event_base* @param event_id* @param event_data*/
void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{printf("wifi_event_handler base:%s, id:%d\n", event_base, event_id);if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START){printf("esp_wifi_connect\n");esp_wifi_connect();}else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED){printf("esp_wifi_connect\n");esp_wifi_connect();}if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP){ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;ESP_LOGI("ESP32", "IP地址:: " IPSTR, IP2STR(&event->ip_info.ip));//request(HTTP_URL); 问题点3: 获取到ip地址后,不要直接在这里执行http请求, 否则会直接崩溃}
}void app_main(void)
{esp_err_t ret = nvs_flash_init(); // 初始化默认NVS分区if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND){ESP_ERROR_CHECK(nvs_flash_erase()); // 擦除默认NVS分区ret = nvs_flash_init();             // 初始化默认NVS分区}ESP_ERROR_CHECK(ret);ESP_ERROR_CHECK(esp_netif_init());                // 初始化底层TCP/IP堆栈ESP_ERROR_CHECK(esp_event_loop_create_default()); // 创建默认事件循环esp_netif_create_default_wifi_sta(); // 创建默认的WIFI STA。wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();esp_wifi_init(&cfg); // 初始化WiFi为WiFi驱动程序wifi_sta_config_t cfg_sta = {.ssid = "black",.password = "black1234",.threshold.authmode = WIFI_AUTH_WPA2_PSK,   //加密方式.pmf_cfg = {.capable = true,.required = false},};esp_wifi_set_config(WIFI_IF_STA, (wifi_config_t *)&cfg_sta); // 设置ESP32 STA或AP的配置esp_wifi_set_mode(WIFI_MODE_STA); // 设置WiFi操作模式// 将事件处理程序的实例注册到默认循环中           任何事件esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL, NULL);// 将事件处理程序的实例注册到默认循环中            工作站从连接的AP获得IP事件esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL, NULL);esp_wifi_start(); // 根据当前配置启动wifixTaskCreate(&http_test_task, "http_test_task", 8192, NULL, 5, NULL); //问题点4: 这里参数需要设置足够大的栈大小, 否则会导致崩溃
}

修正后不崩溃的代码

百度网页返回的http结果太多了,会导致空间不够,
针对问题代码, 做出调整, 对栈空间大小做限制, 当http返回内容过长时,直接丢弃

#include <stdio.h>
#include <pthread.h>#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_tls.h"
#include "esp_crt_bundle.h"
#include "esp_http_client.h"static const char *TAG = "HTTP_REQUEST";
#define MAX_HTTP_OUTPUT_BUFFER 2048
#define HTTP_URL "http://www.baidu.com"// HTTP 请求的处理函数
esp_err_t http_event_handler(esp_http_client_event_t *evt)
{// 缓存http响应的bufferstatic char *output_buffer;// 已经读取的字节数static int output_len;switch(evt->event_id) {case HTTP_EVENT_ERROR:ESP_LOGD(TAG, "HTTP_EVENT_ERROR");break;case HTTP_EVENT_ON_CONNECTED:ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");break;case HTTP_EVENT_HEADER_SENT:ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");break;case HTTP_EVENT_ON_HEADER:ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);break;case HTTP_EVENT_ON_DATA:ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);if (!esp_http_client_is_chunked_response(evt->client)) {if (evt->user_data) {// 这里对buffer长度进行判断, 如果http返回长度过长, 为防止溢出就丢弃,否则进行追加拷贝int left = MAX_HTTP_OUTPUT_BUFFER - output_len -1;if (left > evt->data_len)memcpy(evt->user_data + output_len, evt->data, evt->data_len);else if (left > 0)memcpy(evt->user_data + output_len, evt->data, left);elseESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, buffer full");} else {// 如果实测user_data设置为空时,走这里申请堆内存会崩溃则可以注释掉这段,否则可以使用if (output_buffer == NULL) {output_buffer = (char *) malloc(esp_http_client_get_content_length(evt->client));output_len = 0;if (output_buffer == NULL) {ESP_LOGE(TAG, "Failed to allocate memory for output buffer");return ESP_FAIL;}}memcpy(output_buffer + output_len, evt->data, evt->data_len);}output_len += evt->data_len;}break;case HTTP_EVENT_ON_FINISH:ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");if (output_buffer != NULL) {// Response is accumulated in output_buffer. Uncomment the below line to print the accumulated response// ESP_LOG_BUFFER_HEX(TAG, output_buffer, output_len);free(output_buffer);output_buffer = NULL;}output_len = 0;break;case HTTP_EVENT_DISCONNECTED:ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");if (output_buffer != NULL) {free(output_buffer);output_buffer = NULL;}output_len = 0;break;}return ESP_OK;
}void request(const char *url) {printf("request  -----------1\n");// 响应结果放在这里char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};// 创建一个 HTTP 客户端配置esp_http_client_config_t config = {.method = HTTP_METHOD_GET,.url = url,.event_handler = http_event_handler,.user_data = local_response_buffer,.disable_auto_redirect = true,};// 创建一个 HTTP 客户端并执行 GET 请求esp_http_client_handle_t client = esp_http_client_init(&config);printf("request  -----------2\n");esp_err_t err = esp_http_client_perform(client); // 请求百度网页时,一执行这行系统就会崩溃printf("request  -----------3\n");// 检查请求是否成功if (err == ESP_OK) {int len =  esp_http_client_get_content_length(client);ESP_LOGI(TAG, "Status = %d, content_length = %d",esp_http_client_get_status_code(client),//状态码len);//数据长度} else {printf("HTTP GET request failed: %s\n", esp_err_to_name(err));}printf("Response: %.*s\n", strlen(local_response_buffer), local_response_buffer);//断开并释放资源esp_http_client_cleanup(client);printf("request  -----------4\n");
}void http_test_task(void *arg)
{sleep(15);request(HTTP_URL);vTaskDelete(NULL);
}/*** @brief WiFi 的事件循环Handler* @param arg* @param event_base* @param event_id* @param event_data*/
void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{printf("wifi_event_handler base:%s, id:%d\n", event_base, event_id);if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START){printf("esp_wifi_connect\n");esp_wifi_connect();}else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED){printf("esp_wifi_connect\n");esp_wifi_connect();}if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP){ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;ESP_LOGI("ESP32", "IP地址:: " IPSTR, IP2STR(&event->ip_info.ip));//request(HTTP_URL); 问题点2: 获取到ip地址后,不要直接在这里执行http请求, 否则会直接崩溃 , 这里注释掉不用}
}void app_main(void)
{esp_err_t ret = nvs_flash_init(); // 初始化默认NVS分区if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND){ESP_ERROR_CHECK(nvs_flash_erase()); // 擦除默认NVS分区ret = nvs_flash_init();             // 初始化默认NVS分区}ESP_ERROR_CHECK(ret);ESP_ERROR_CHECK(esp_netif_init());                // 初始化底层TCP/IP堆栈ESP_ERROR_CHECK(esp_event_loop_create_default()); // 创建默认事件循环esp_netif_create_default_wifi_sta(); // 创建默认的WIFI STA。wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();esp_wifi_init(&cfg); // 初始化WiFi为WiFi驱动程序wifi_sta_config_t cfg_sta = {.ssid = "black",.password = "black1234",.threshold.authmode = WIFI_AUTH_WPA2_PSK,   //加密方式.pmf_cfg = {.capable = true,.required = false},};esp_wifi_set_config(WIFI_IF_STA, (wifi_config_t *)&cfg_sta); // 设置ESP32 STA或AP的配置esp_wifi_set_mode(WIFI_MODE_STA); // 设置WiFi操作模式// 将事件处理程序的实例注册到默认循环中           任何事件esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL, NULL);// 将事件处理程序的实例注册到默认循环中            工作站从连接的AP获得IP事件esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL, NULL);esp_wifi_start(); // 根据当前配置启动wifixTaskCreate(&http_test_task, "http_test_task", 8192, NULL, 5, NULL); //问题点3: 这里参数需要设置足够大的栈大小, 否则会导致崩溃, 可以根据实测来改, 最好MAX_HTTP_OUTPUT_BUFFER大,否则可能会崩溃.
}

这样连上wifi后, http get请求百度网页就正常了, 可以打印部分内容, 建议换成简单的页面的网址, 百度返回的数据太多了.

esp32相关文章

可见我的esp32专栏
作者:帅得不敢出门 csdn原创谢绝转载

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Ubuntu22.04安装 docker和docker-compose环境
  • c# fromlayout 布局中间空隙问题
  • 无人机培训机构必备运营合格证及驾驶员训练机构合格证详解
  • 告别AI痕迹:如何让Midjourney创作更自然
  • 2024百元蓝牙耳机推荐有哪些?四款百元王炸机型2024推荐
  • 如何理解 Linux 命令 和 Shell 命令 之间的关系
  • Spring Security 原理、源码解析及进阶
  • 如何使用 Qt C++ 基于 FFmpeg 开发本地视频播放器
  • Docker 系列文章导航
  • AI大模型编写多线程并发框架(六十五):发布和应用
  • C++和OpenGL实现3D游戏编程【连载7】——文字和汉字的显示
  • 使用C++编写接口调用PyTorch模型,并生成DLL供.NET使用
  • k8s ingress-nginx
  • JS设计模式之“语言之魂” - 原型模式
  • Python任务编排和工作流管理库之prefect使用详解
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • HTTP中的ETag在移动客户端的应用
  • JavaScript中的对象个人分享
  • jquery ajax学习笔记
  • Object.assign方法不能实现深复制
  • PAT A1120
  • Theano - 导数
  • Vim 折腾记
  • 测试如何在敏捷团队中工作?
  • 创建一种深思熟虑的文化
  • 关于Flux,Vuex,Redux的思考
  • 通信类
  • 我从编程教室毕业
  • 用element的upload组件实现多图片上传和压缩
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (4)STL算法之比较
  • (每日一问)设计模式:设计模式的原则与分类——如何提升代码质量?
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (区间dp) (经典例题) 石子合并
  • (生成器)yield与(迭代器)generator
  • (实战篇)如何缓存数据
  • .net core 实现redis分片_基于 Redis 的分布式任务调度框架 earth-frost
  • .net 发送邮件
  • .net 托管代码与非托管代码
  • .net反混淆脱壳工具de4dot的使用
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • .Net面试题4
  • .Net中的集合
  • ::前边啥也没有
  • @data注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)
  • [Angular 基础] - 数据绑定(databinding)
  • [AR Foundation] 人脸检测的流程
  • [AutoSAR 存储] 汽车智能座舱的存储需求
  • [codeforces]Levko and Permutation
  • [Django学习]查询过滤器(lookup types)
  • [IE9] IE9 RC版下载链接