ESP-C3入门12. HTTPS请求、堆内存使用及JSON处理

news2024/11/18 7:48:19

ESP-C3入门12. HTTPS请求、堆内存使用及JSON处理

  • 一、创建HTTPS请求
    • 1. 基本流程
    • 2. ESP32 使用https证书的方式
      • (1) 内置证书
      • (2) ESP32管理证书
    • 3. 开发环境配置
      • (1) 引用 esp-tls 库
      • (2) 启用 `CONFIG_MBEDTLS_CERTIFICATE_BUNDLE`
  • 二、堆内存的使用
    • 1. 堆内存和栈内存
    • 2. 堆内存的使用
      • (1) 内存分配
      • (2) 内存释放
  • 三、使用cJSON库
    • 1. 加载cJSON库
    • 2. 最基本的用法
  • 四、https请求示例
    • 1. 项目结构
    • 2. 自定义组件的CMakeLists.txt设置
    • 3. http_request.h
    • 4. http_request.c
      • 5. main.c

一、创建HTTPS请求

1. 基本流程

本文主要内容接上节《创建最基本http请求》的文章。
ESP32 IDF创建http请求的基本流程:

  1. 使用esp_http_client_config_t创建http客户端;
  2. esp_http_client_init 初始化http客户端;
  3. esp_http_client_set_method 设置http请求方式;
  4. 设置http请求头esp_http_client_set_header
  5. 设置 http 请求体 esp_http_client_set_post_field
  6. 执行http请求 esp_http_client_perform
  7. 处理http响应;
  8. 释放http客户端esp_http_client_cleanup

在https请求中,有一些进行一些额外的步骤,包括 证书的验证和捆绑。
首先要获取远程服务器的证书;
如果ESP32 IDF无法验证证书,则需要使用esp_http_client_set_cert_info函数将服务器证书的SHA-1指纹添加到ESP32 IDF的证书信任列表中。
如果ESP32 IDF无法连接到远程服务器,则可能需要设置代理服务器。

2. ESP32 使用https证书的方式

(1) 内置证书

证书已经内置在ESP32的固件中,无需单独管理证书,可以直接使用。这种方式比较简单,适用于使用不频繁的HTTPS请求。

(2) ESP32管理证书

使用esp32管理证书的方式,需要用户自己管理证书。首先需要在ESP32上安装证书,然后在代码中指定证书的路径和密码。这种方式需要用户自己管理证书的更新和安装,但是可以在需要时更新证书,从而提高了安全性,适用于使用频繁的HTTPS请求场景。

总结:使用内置证书的方式更加简单,适用于简单的HTTPS请求场景;
使用esp32管理证书的方式更加灵活,适用于复杂的HTTPS请求场景。

3. 开发环境配置

(1) 引用 esp-tls 库

CMakeLists.txt里添加:

idf_component_register(SRCS "main.c" "network/wifi.c" "network/tcp_server.c" "network/tcp_client.c" "network/http_request.c"
                    INCLUDE_DIRS "network/include"
                    REQUIRES "tcpip_adapter" "nvs_flash" "esp_http_client" "esp-tls"
        )

(2) 启用 CONFIG_MBEDTLS_CERTIFICATE_BUNDLE

在这里插入图片描述
在menuconfig中设置启用ESP-IDF TLS 库中的受信任证书捆绑包(一般已经默认选中)。
在这里插入图片描述
在使用 TLS 时,需要验证对等方证书以确保其是可信的。可信任的证书可以是根证书或中间证书,或者可以通过一组根证书建立信任关系。证书的验证需要证书链和 CA 证书的列表。如果 ESP-IDF TLS 库没有找到可用的证书链或 CA 证书,将不会建立安全连接。

受信任的证书捆绑包是一组根证书,其中包括常见 CA 证书,以及其他根证书和中间证书。启用此选项可以将 ESP-IDF TLS 库的可信 CA 列表扩展到受信任的证书捆绑包中的证书,从而增加连接对等方的成功率。

二、堆内存的使用

1. 堆内存和栈内存

一般情况下声明的变量是栈内存,是一种后进后出的数据结构,用于存储局部变量、函数参数和返回地址等。它的内存空间由编译器在程序运行时自动分配和释放,空间大小通常是有限制的。

堆内存是由程序员在运行时手动分配和释放的内存,它的内存空间通常比栈内存更大,也更灵活,可以根据需要动态调整内存大小。 堆内存通常用来存储动态分配的数据结构,如链表、树、图等。 但使用中要避免内存泄露。

在嵌入式系统中,由于内存资源有限,使用堆内存可以更好地进行内存管理。

2. 堆内存的使用

(1) 内存分配

在ESP32开发中,堆空间是动态分配的,它的大小是由可用RAM的大小限制的。ESP-IDF中提供了几个API用于堆内存管理,其中之一是heap_caps_malloc函数。heap_caps_malloc是ESP32的堆空间动态内存分配API,允许用户从堆空间中分配内存。

函数原型:

void *heap_caps_malloc(size_t size, uint32_t caps);

其中:

  • size参数是要分配的内存块的大小
  • caps参数是分配内存的策略,这是一个32位的标志位,用于设置内存的要求和限制。

标志位值列表:

  • MALLOC_CAP_8BIT: 分配8位宽的内存;
  • MALLOC_CAP_32BIT: 分配32位宽的内存;
  • MALLOC_CAP_64BIT: 分配64位宽的内存;
  • MALLOC_CAP_SPIRAM: 分配SPI RAM内存;
  • MALLOC_CAP_DMA: 分配DMA内存;
  • MALLOC_CAP_EXEC: 分配可执行内存;
  • MALLOC_CAP_DEFAULT: 分配默认类型的内存;
  • MALLOC_CAP_INVALID: 分配无效类型的内存。

(2) 内存释放

heap_caps_free 进行内存释放。

本文中的响应数据使用变量 char* local_response_buffer ,使用下面的代码把变量放在了堆上:

    local_response_buffer = heap_caps_malloc(MAX_HTTP_OUTPUT_BUFFER, MALLOC_CAP_8BIT);
    if (local_response_buffer == NULL) {
        ESP_LOGE(TAG, "Failed to allocate memory for HTTP output buffer");
        abort();
    }

上述代码分配了一个大小为MAX_HTTP_OUTPUT_BUFFER的8位宽的内存块。

三、使用cJSON库

1. 加载cJSON库

在 CMakeLists.txt中添加 json:

idf_component_register(SRCS "main.c" "network/wifi.c" "network/tcp_server.c" "network/tcp_client.c" "network/http_request.c"
                    INCLUDE_DIRS "network/include"
                    REQUIRES "tcpip_adapter" "nvs_flash" "esp_http_client" "esp-tls" "json"
        )

2. 最基本的用法

void parse_json(const char *json_string) {
    // 解析 JSON 字符串
    cJSON *root = cJSON_Parse(json_string);
    if (root == NULL) {
        printf("Failed to parse JSON string\n");
        return;
    }

    // 获取 "name" 属性的值
    cJSON *name = cJSON_GetObjectItemCaseSensitive(root, "body");
    if (cJSON_IsString(name) && (name->valuestring != NULL)) {
        printf("body: %s\n", name->valuestring);
    } else {
        printf("Failed to get name property\n");
    }

    cJSON_Delete(root);
}

更多用法可参考:点击这里

四、https请求示例

1. 项目结构

在这里插入图片描述

2. 自定义组件的CMakeLists.txt设置

idf_component_register(SRCS "main.c" "network/wifi.c" "network/tcp_server.c" "network/tcp_client.c" "network/http_request.c"
                    INCLUDE_DIRS "network/include"
                    REQUIRES "tcpip_adapter" "nvs_flash" "esp_http_client" "esp-tls" "json"
        )

# 这将把当前组件的路径添加到编译器的头文件搜索路径中,否则提示找不到string.h等头文件
target_include_directories(${COMPONENT_LIB} PRIVATE ${CMAKE_CURRENT_LIST_DIR})

target_include_directories(${COMPONENT_LIB} PUBLIC "network/include"

        )

3. http_request.h

#ifndef HTTP_REQUEST_H
#define HTTP_REQUEST_H
#include "esp_http_client.h"

esp_err_t http_event_handler(esp_http_client_event_t *evt);
void request(const char* url);
#endif

4. http_request.c

#include <esp_err.h>
#include <esp_log.h>
#include "network/include/http_request.h"
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
#include "esp_crt_bundle.h"
#endif
static const char *TAG = "HTTP_REQUEST";
#define MAX_HTTP_OUTPUT_BUFFER 4096
#include "cJSON.h"

char* local_response_buffer ;

// HTTP 请求的处理函数
esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
    // 缓存http响应的buffer
    static char *output_buffer;
    // 已经读取的字节数
    static int output_len;
    local_response_buffer = (char *) evt->user_data;

    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)) {
                // 如果配置了user_data buffer,则把响应复制到该buffer中
                if (local_response_buffer) {
                    memcpy(local_response_buffer + output_len, evt->data, evt->data_len);
                } else {
                    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) {
                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 parse_json(const char *json_string) {
    // 解析 JSON 字符串
    cJSON *root = cJSON_Parse(json_string);
    if (root == NULL) {
        printf("Failed to parse JSON string\n");
        return;
    }

    // 获取 "name" 属性的值
    cJSON *name = cJSON_GetObjectItemCaseSensitive(root, "body");
    if (cJSON_IsString(name) && (name->valuestring != NULL)) {
        printf("body: %s\n", name->valuestring);
    } else {
        printf("Failed to get name property\n");
    }

    cJSON_Delete(root);
}

void request(const char *url) {
    // 响应结果放在这里
    local_response_buffer = heap_caps_malloc(MAX_HTTP_OUTPUT_BUFFER, MALLOC_CAP_8BIT);
    if (local_response_buffer == NULL) {
        ESP_LOGE(TAG, "Failed to allocate memory for HTTP output buffer");
        abort();
    }

    // 创建一个 HTTP 客户端配置
    esp_http_client_config_t config = {
            .url = url,
            .event_handler = http_event_handler,
            .user_data = local_response_buffer,
            .crt_bundle_attach = esp_crt_bundle_attach,
    };

    // 创建一个 HTTP 客户端并执行 GET 请求
    esp_http_client_handle_t client = esp_http_client_init(&config);
    esp_err_t err = esp_http_client_perform(client);

    // 检查请求是否成功
    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);//数据长度
        // 解析json
        parse_json(local_response_buffer);
    } else {
        printf("HTTP GET request failed: %s\n", esp_err_to_name(err));
    }
    printf("Response: %.*s\n", strlen(local_response_buffer), local_response_buffer);

    if (local_response_buffer != NULL) {
        heap_caps_free(local_response_buffer);
        local_response_buffer = NULL;
    }
    //断开并释放资源
    esp_http_client_cleanup(client);
}

5. main.c

#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include <nvs_flash.h>
#include "network/include/wifi.h"
#include "network/include/http_request.h"

static const char *TAG = "wifi connection";

#define HTTP_URL "https://jsonplaceholder.typicode.com/posts/1"

void app_main()
{
    ESP_LOGE(TAG, "app_main");
    // 初始化NVS存储区
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    // Wi-Fi初始化
    ESP_LOGI(TAG, "Wi-Fi initialization");
    wifi_initialize();

    // Wi-Fi Station初始化
    wifi_station_initialize();

    vTaskDelay(pdMS_TO_TICKS(1000*10));
    ESP_LOGI(TAG, "request 1");
    request(HTTP_URL);

    vTaskDelay(pdMS_TO_TICKS(2000));
    ESP_LOGI(TAG, "request again");
    request(HTTP_URL);

    while (1) {
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/370510.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux基础命令-which查找命令文件位置

文章目录 which 命令功能 语法格式 基本参数 参考实例 1&#xff09;查找chmod命令的文件位置 2&#xff09;查找chmod命令的所有路径 3&#xff09;一次性查找多个命令路径 4&#xff09;组合其他命令一起使用 5&#xff09;显示命令的版本信息 命令总结 which 命…

11 udp 发送数据的流程梳理

前言 呵呵 之前曾经看到过 湖光大佬 的 tcp 的流程梳理 呵呵 很高深 有很多不明白的地方, 不光是涉及到 linux 网络处理本身的东西, 还涉及到了 tcp协议 的一些具体的实现, 是非常的复杂 这里之前 在 0voice/linux_kernel_wiki 上面看到了网络协议栈部分的梳理 呵呵 自己也…

计算机网络(七):DNS协议和原理,DNS为什么用UDP,网页解析的全过程

文章目录一、什么是DNS二、DNS的作用三、DNS作用四、DNS为什么用UDP五、如果打开一个网站很慢&#xff0c;要如何排查六、网页解析的全过程一、什么是DNS DNS是域名系统的英文缩写&#xff0c;是一种组织成域层次结构的计算机和网络服务命名系统&#xff0c;用于TCP/IP网络。 …

数据库行业的 “叛逆者”:大数据已“死”,MotherDuck 当立

“大数据”已死——现今我们最重要的事情不是担心数据大小&#xff0c;而是专注于我们将如何使用它来做出更好的决策。数据库行业发展至今&#xff0c;在数据层面有很多的加速和变革&#xff0c;尤其是过去几年的云数仓爆炸式增长&#xff0c;带来了行业的很多变化。毫无疑问&a…

Fiddler报文分析-断点应用、模拟网络限速-HTTPS的 拦截

目录 一、报文分析 Statistics 请求性能数据 检查器&#xff08;Inspectors&#xff09; 自定义响应&#xff08;AutoResponder&#xff09; Composer Composer的功能就是用来创建HTTP Request然后发送请求。 允许自定义请求发送到服务器&#xff0c;即可以手动创建一个新…

Acwing---1242. 修改数组——并查集的简单应用

修改数组1.题目2.基本思想3.代码实现1.题目 给定一个长度为 N 的数组 A[ A1,A2,⋅⋅⋅AN ] &#xff0c;数组中有可能有重复出现的整数。 现在小明要按以下方法将其修改为没有重复整数的数组。 小明会依次修改 A2,A3,⋅⋅⋅,AN。 当修改 Ai 时&#xff0c;小明会检查 Ai 是…

在VScode里面添加Python解释器

VScode编辑器在安装好Python插件之后会自动选择环境变量中排序最高的那一个解释器作为默认解释器&#xff0c;而想要额外添加新的Python解释器就需要自己设置。 Python和VScode编辑器安装在Windows系统中 Python扩展插件安装在VScode编辑器 第一步&#xff0c;打开VScode编辑…

leaflet 绘制渐变折线(094)

第094个 点击查看专栏目录 本示例的目的是介绍如何在vue+leaflet中绘制渐变折线。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共89行)安装插件相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:htt…

分布式算法 - Raft算法

Paxos是出了名的难懂&#xff0c;而Raft正是为了探索一种更易于理解的一致性算法而产生的。它的首要设计目的就是易于理解&#xff0c;所以在选主的冲突处理等方式上它都选择了非常简单明了的解决方案。推荐阅读提示强烈推荐通过如下资料学习raft。 raft.github.io这里面有一个…

基于Springboot+vue+协同过滤+前后端分离+智能图书推荐系统(用户,多商户,管理员)

技术栈: SpringBoot,SSM, MYSQL, Vue,Layui,JQUERY,HTML,CSS, JAVASCRIPT,前后台分离,日历控件,协同过滤(余弦函数)-基于用户过滤首页访问 http://localhost:8080/vue_gxhfztjxt管理员admin 123456商家 seller1 123456买家 user1 123456代码下载链接&#xff1a;https://pan.ba…

【转载】STM32 Timer定时器开机立即进入中断问题(HAL库)

【转载】STM32 Timer定时器开机立即进入中断问题&#xff08;HAL库&#xff09;问题1问题2F407定时器更新中断问题&#xff08;TIM_IT_Update中断&#xff09;STM32 定时器有时一开启就进中断的话题[STM32CubeF4] HAL中的定时器中断处理函数&#xff0c;存在重复进入的隐患问题…

(二)变形梯度和相对变形梯度

本文主要内容如下&#xff1a;1. 变形梯度2. 变形梯度的逆3. 相对变形梯度4. 两点张量1. 变形梯度 a. 运动变形前&#xff0c;参考构型中某代表性物质点 A 邻域内的线元&#xff1a; dX⃗dXAG⃗Adxic⃗id\vec{X}dX^A\vec{G}_Adx^i\vec{c}_idXdXAGA​dxici​ b. 运动变形后&…

Laravel框架01:composer和Laravel简介

Laravel框架01&#xff1a;composer和Laravel简介一、Composer介绍二、创建Laravel项目三、Laravel目录结构四、Laravel启动方式一、Composer介绍 composer 是PHP中用来管理依赖关系的工具。类似于Javascript的NPM。composer官网&#xff1a;https://getcomposer.org/安装结束…

【bug】Transformer输出张量的值全部相同?!

【bug】Transformer输出张量的值全部相同&#xff1f;&#xff01;现象原因解决现象 输入经过TransformerEncoderLayer之后&#xff0c;基本所有输出都相同了。 核心代码如下&#xff0c; from torch.nn import TransformerEncoderLayer self.trans TransformerEncoderLayer…

SPARC体系下硬浮点编译故障分析

问题说明 之前extension版的app工程都是用的软浮点编译的&#xff0c;在增加姿控算法库后&#xff0c;统一改用硬浮点运行&#xff0c;发现之前一个浮点数解析不对了&#xff0c;排查发现和工程编译选项有关&#xff0c;为软浮点时正常&#xff0c;硬浮点时异常。该问题脱离业…

【华为OD机试模拟题】用 C++ 实现 - VLAN 资源池(2023.Q1)

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

Stream操作流 练习

基础数据&#xff1a;Data AllArgsConstructor NoArgsConstructor public class User {private String name;private int age;private String sex;private String city;private Integer money; static List<User> users new ArrayList<>();public static void m…

【计算机三级网络技术】 第一篇 网络系统结构与系统设计的基本原则

网络系统结构与系统设计的基本原则 文章目录网络系统结构与系统设计的基本原则一、计算机网络的基本结构二、计算机网络分类及其互联方式1.局域网2.城域网3.广域网4.计算机网络的互联方式三、局域网技术四、城域网技术1.城域网的概念2.宽带城域网建设产生的影响3.推动城域网快速…

HTML - 扫盲

文章目录1. 前言2. HTML2.1 下载 vscode3 HTML 常见标签3.1 注释标签3.2 标题标签3.3 段落标签3.4 换行标签3.5 格式化标签1. 加粗2. 倾斜3. 下划线3.6 图片标签3.7 超链接标签3.8 表格标签3.9 列表标签4. 表单标签4.1 from 标签4.2 input 标签4.3 select 标签4.4 textarea标签…

webgl渲染优化——深度缓冲区、多边形缓冲机制

文章目录前言深度缓冲区多边形缓冲机制总结前言 webgl在渲染三维场景时&#xff0c;按照Z坐标的值决定前后关系&#xff0c;但是在默认状态下它并未开启深度检测&#xff0c;而是将后绘制的物体放在前面&#xff1b;当两个物体Z坐标相差无几时&#xff0c;会产生深度冲突&…