SOC-ESP32S3部分:25-HTTP请求

news2025/6/5 23:29:50

飞书文档https://x509p6c8to.feishu.cn/wiki/KL4RwxUQdipzCSkpB2lcBd03nvK

HTTP(Hyper Text Transfer Protocol) 超文本传输协议,是一种建立在 TCP 上的无状态连接,整个基本的工作流程是客户端发送一个 HTTP 请求,说明客户端想要访问的资源和请求的动作,服务端收到请求之后,服务端开始处理请求,并根据请求做出相应的动作访问服务器资源,最后通过发送 HTTP 响应把结果返回给客户端。

HTTP请求方法:

一般有 GET、POST、PUT、DELETE,含义分别是获取、修改、上传、删除.

其中 GET 方式仅仅为获取服务器资源,方式较为简单,因此在请求方式为 GET 的 HTTP 请求数据中,请求正文部分可以省略,直接将想要获取的资源添加到 URL 中。

请求头:包括一些访问的域名、用户代理、Cookie等信息。

请求正文:就是HTTP请求的数据。

如下发,就是一个简单的HTTP请求示例,在执行HTTP请求前,设备必须先进行配网,配网使用的是我们上节课讲解的代码。

HTTP部分核心逻辑如下,设备配网成功后,设置全局变量is_connect_wifi为true,然后执行http_get_request发送一次GET请求,请求发送给http://www.example.com/服务器。

主要流程如下:

  1. 配置http请求参数和回调处理函数
  2. 初始化http客户端
  3. 执行http请求,监听回调
  4. 清理HTTP客户端资源,释放句柄。

配置http请求参数和回调处理函数

esp_http_client_config_t
描述: 配置ESP32 HTTP客户端的参数。
字段:
.method: HTTP请求方法,例如 HTTP_METHOD_GET 表示GET请求。
.url: 请求的目标URL。
.event_handler: 自定义事件处理函数,用于处理HTTP请求过程中的事件。
.user_data: 用户自定义数据,可以传递给事件处理函数或存储响应数据。

 _http_event_handler
描述: 自定义的HTTP事件处理函数。
用途: 处理HTTP请求过程中发生的事件(如连接建立、数据接收等)。
注意: 代码中未提供具体实现,但通常需要根据事件类型执行相应的逻辑。

使用示例:
    char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
    esp_http_client_config_t config = {
        .method = HTTP_METHOD_GET,           //get请求
        .url = "http://www.example.com/",     //请求url
        .event_handler = _http_event_handler,
        .user_data = local_response_buffer,        // Pass address of local buffer to get response
    };
   
_http_event_handler为http请求后的回调函数:
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    switch(evt->event_id) {
        case HTTP_EVENT_ERROR:    //错误事件
            ESP_LOGI(TAG, "HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:    //连接成功事件
            ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:    //发送头事件
            ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:    //接收头事件
            ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER");
            break;
        case HTTP_EVENT_ON_DATA:    //接收数据事件
            ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            break;
        case HTTP_EVENT_ON_FINISH:    //会话完成事件
            ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
            break;
        case HTTP_EVENT_DISCONNECTED:    //断开事件
            ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
            break;
        case HTTP_EVENT_REDIRECT:
            break;
    }
    return ESP_OK;
}

初始化http客户端

esp_http_client_init
esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *config);
功能: 初始化HTTP客户端并返回句柄。
参数:
config: 指向esp_http_client_config_t结构体的指针,包含HTTP客户端的配置信息。
返回值: 返回HTTP客户端的句柄。

执行http请求

esp_err_t esp_http_client_perform(esp_http_client_handle_t client);
功能: 执行HTTP请求并等待响应。
参数:
client: HTTP客户端句柄。
返回值:
ESP_OK: 请求成功。
其他错误码: 请求失败。

清理HTTP客户端资源,释放句柄。

void esp_http_client_cleanup(esp_http_client_handle_t client);
功能: 清理HTTP客户端资源,释放句柄。
参数:
client: HTTP客户端句柄。

最终代码如下:

#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_eap_client.h"
#include "esp_netif.h"
#include "esp_smartconfig.h"
#include "esp_mac.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "esp_http_client.h"

static const char *TAG = "http_client";

static EventGroupHandle_t s_wifi_event_group;

static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;
static void smartconfig_example_task(void *parm);
static bool is_connect_wifi = false;

#define MAX_HTTP_OUTPUT_BUFFER 2048

esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    switch(evt->event_id) {
        case HTTP_EVENT_ERROR:    //错误事件
            ESP_LOGI(TAG, "HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:    //连接成功事件
            ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:    //发送头事件
            ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:    //接收头事件
            ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER");
            printf("%.*s", evt->data_len, (char*)evt->data);
            break;
        case HTTP_EVENT_ON_DATA:    //接收数据事件
            ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            if (!esp_http_client_is_chunked_response(evt->client)) {
                printf("%.*s", evt->data_len, (char*)evt->data);
            }
            break;
        case HTTP_EVENT_ON_FINISH:    //会话完成事件
            ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
            break;
        case HTTP_EVENT_DISCONNECTED:    //断开事件
            ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
            break;
        case HTTP_EVENT_REDIRECT:
            break;
    }
    return ESP_OK;
}

static void http_get_request(){
    char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
    esp_http_client_config_t config = {
        .method = HTTP_METHOD_GET,           //get请求
        .url = "http://www.example.com/",     //请求url
        .event_handler = _http_event_handler,
        .user_data = local_response_buffer,        // Pass address of local buffer to get response
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);
    // GET
    esp_err_t err = esp_http_client_perform(client);
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "HTTP GET OK");
    } else {
        ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
    }
    ESP_LOGI(TAG, "%s\n", local_response_buffer);   
    esp_http_client_cleanup(client);
}

static void http_get_task(void *pvParameters)
{
    while (1)
    {
        if (is_connect_wifi)
        {
            http_get_request();
        }
        vTaskDelay(8000 / portTICK_PERIOD_MS);
    }
}

static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        // WiFi 站点模式启动后,创建 SmartConfig 任务
        xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        is_connect_wifi = false;
        // WiFi 断开连接时,重新连接并清除连接标志位
        esp_wifi_connect();
        xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        // 获取到 IP 地址后,设置连接标志位
        xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
        is_connect_wifi = true;
    }
    else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE)
    {
        // SmartConfig 扫描完成事件
        ESP_LOGI(TAG, "Scan done");
    }
    else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL)
    {
        // SmartConfig 找到信道事件
        ESP_LOGI(TAG, "Found channel");
    }
    else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD)
    {
        // SmartConfig 获取到 SSID 和密码事件
        ESP_LOGI(TAG, "Got SSID and password");
        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        wifi_config_t wifi_config;
        uint8_t ssid[33] = {0};
        uint8_t password[65] = {0};
        uint8_t rvd_data[33] = {0};

        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));

        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        ESP_LOGI(TAG, "SSID:%s", ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", password);
        if (evt->type == SC_TYPE_ESPTOUCH_V2)
        {
            // 如果使用的是 ESPTouch V2,获取额外的数据
            ESP_ERROR_CHECK(esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)));
            ESP_LOGI(TAG, "RVD_DATA:");
            for (int i = 0; i < 33; i++)
            {
                printf("%02x ", rvd_data[i]);
            }
            printf("\n");
        }
        // 断开当前 WiFi 连接,设置新的 WiFi 配置并重新连接
        ESP_ERROR_CHECK(esp_wifi_disconnect());
        ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
        esp_wifi_connect();
    }
    else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE)
    {
        // SmartConfig 发送 ACK 完成事件,设置 SmartConfig 完成标志位
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}

static void smartconfig_example_task(void *parm)
{
    EventBits_t uxBits;
    wifi_config_t myconfig = {0};
    ESP_LOGI(TAG, "creat smartconfig_example_task");
    // 获取wifi配置信息
    esp_wifi_get_config(ESP_IF_WIFI_STA, &myconfig);
    if (strlen((char *)myconfig.sta.ssid) > 0)
    {
        // 如果配置过,就直接连接wifi
        ESP_LOGI(TAG, "alrealy set, SSID is :%s,start connect", myconfig.sta.ssid);
        esp_wifi_connect();
    }
    else
    {
        // 如果没有配置过,就进行配网操作
        ESP_LOGI(TAG, "have no set, start to config");
        ESP_ERROR_CHECK(esp_smartconfig_set_type(SC_TYPE_ESPTOUCH_AIRKISS)); // 支持APP ESPTOUCH和微信AIRKISS
        smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
        ESP_ERROR_CHECK(esp_smartconfig_start(&cfg));
    }
    while (1)
    {
        // 等待连接标志位或 SmartConfig 完成标志位
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
        if (uxBits & CONNECTED_BIT)
        {
            // 连接到 AP 后的日志
            ESP_LOGI(TAG, "WiFi Connected to ap");
            // 联网成功后,可以关闭线程
            vTaskDelete(NULL);
        }
        if (uxBits & ESPTOUCH_DONE_BIT)
        {
            // SmartConfig 完成后的日志
            ESP_LOGI(TAG, "smartconfig over");
            // 停止 SmartConfig
            esp_smartconfig_stop();
            // 删除 SmartConfig 任务
            vTaskDelete(NULL);
        }
    }
}

void app_main(void)
{
    // 初始化 NVS 闪存
    ESP_ERROR_CHECK( nvs_flash_init());
    // 初始化网络接口
    ESP_ERROR_CHECK(esp_netif_init());
    // 创建事件组
    s_wifi_event_group = xEventGroupCreate();
    // 创建默认事件循环
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    // 创建默认的 WiFi 站点模式网络接口
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    // 初始化 WiFi 配置
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // 注册事件处理函数
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));

    // 设置 WiFi 模式为站点模式并启动 WiFi
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_start());

    xTaskCreate(&http_get_task, "http_get_task", 9192, NULL, 5, NULL);
}

对于POST请求

只需要把esp_http_client_config_t的method设置为HTTP_METHOD_POST,另外安装服务器的参数,填写esp_http_client_set_header和esp_http_client_set_post_field即可。例如,我们需要发送JSON数据包{"field1":"value1"}给服务器http://httpbin.org/post,具体POST请求实现如下:

static void http_post(void)
{
    esp_http_client_config_t config = {
        .method = HTTP_METHOD_POST,        
        .url = "http://httpbin.org/post",    
        .event_handler = _http_event_handler,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);
    // POST
    const char *post_data = "{\"field1\":\"value1\"}";
    esp_http_client_set_header(client, "Content-Type", "application/json");
    esp_http_client_set_post_field(client, post_data, strlen(post_data));
    esp_err_t err = esp_http_client_perform(client);
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "HTTP POST OK");
    } else {
        ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(err));
    }
    esp_http_client_cleanup(client);
}

对于HTTPS请求

只需额外配置esp_http_client_config_t 的url为https连接,另外添加证书文件即可cert_pem,例如

    esp_http_client_config_t config = {
        .method = HTTP_METHOD_GET,
        .url = "https://www.howsmyssl.com/",
        .event_handler = _http_event_handler,
        .cert_pem = howsmyssl_com_root_cert_pem_start,
    };

esp_http_client_config_t更多参数说明如下:可按需使用

typedef struct {
    const char                  *url;
    /*!< HTTP URL,URL 信息是最重要的,如果设置了此参数,它将覆盖下面的其他字段(如果有的话) */
    const char                  *host;
    /*!< 以字符串形式表示的域名或 IP 地址 */
    int                         port;
    /*!< 要连接的端口,默认值取决于 esp_http_client_transport_t(80 用于 HTTP,443 用于 HTTPS) */
    const char                  *username;
    /*!< 用于 HTTP 认证 */
    const char                  *password;
    /*!< 用于 HTTP 认证 */
    esp_http_client_auth_type_t auth_type;
    /*!< HTTP 认证类型,请参阅 `esp_http_client_auth_type_t` */
    const char                  *path;
    /*!< HTTP 路径,如果未设置,默认值为 `/` */
    const char                  *query;
    /*!< HTTP 查询字符串 */
    const char                  *cert_pem;
    /*!< SSL 服务器证书,以 PEM 格式表示的字符串,如果客户端需要验证服务器 */
    size_t                      cert_len;
    /*!< 指向 cert_pem 的缓冲区长度。如果是 null 结尾的 PEM 字符串,可为 0 */
    const char                  *client_cert_pem;
    /*!< SSL 客户端证书,以 PEM 格式表示的字符串,如果服务器需要验证客户端 */
    size_t                      client_cert_len;
    /*!< 指向 client_cert_pem 的缓冲区长度。如果是 null 结尾的 PEM 字符串,可为 0 */
    const char                  *client_key_pem;
    /*!< SSL 客户端密钥,以 PEM 格式表示的字符串,如果服务器需要验证客户端 */
    size_t                      client_key_len;
    /*!< 指向 client_key_pem 的缓冲区长度。如果是 null 结尾的 PEM 字符串,可为 0 */
    const char                  *client_key_password;
    /*!< 客户端密钥解密密码字符串 */
    size_t                      client_key_password_len;
    /*!< 指向 client_key_password 的密码字符串长度 */
    esp_http_client_proto_ver_t tls_version;
    /*!< 连接的 TLS 协议版本,例如,TLS 1.2、TLS 1.3(默认 - 无偏好) */
#ifdef CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
    bool                        use_ecdsa_peripheral;
    /*!< 使用 ECDSA 外设来使用私钥 */
    uint8_t                     ecdsa_key_efuse_blk;
    /*!< ECDSA 密钥存储的 efuse 块 */
#endif
    const char                  *user_agent;
    /*!< 随 HTTP 请求发送的用户代理字符串 */
    esp_http_client_method_t    method;
    /*!< HTTP 请求方法 */
    int                         timeout_ms;
    /*!< 网络超时时间,单位为毫秒 */
    bool                        disable_auto_redirect;
    /*!< 禁用 HTTP 自动重定向 */
    int                         max_redirection_count;
    /*!< 收到 HTTP 重定向状态码时的最大重定向次数,如果为 0,则使用默认值 */
    int                         max_authorization_retries;
    /*!< 收到 HTTP 未授权状态码时的最大连接重试次数,如果为 0,则使用默认值。如果为 -1,则禁用授权重试 */
    http_event_handle_cb        event_handler;
    /*!< HTTP 事件处理函数 */
    esp_http_client_transport_t transport_type;
    /*!< HTTP 传输类型,请参阅 `esp_http_client_transport_t` */
    int                         buffer_size;
    /*!< HTTP 接收缓冲区大小 */
    int                         buffer_size_tx;
    /*!< HTTP 发送缓冲区大小 */
    void                        *user_data;
    /*!< HTTP 用户数据上下文 */
    bool                        is_async;
    /*!< 设置异步模式,目前仅支持 HTTPS */
    bool                        use_global_ca_store;
    /*!< 对所有设置了此布尔值的连接使用全局 CA 证书存储 */
    bool                        skip_cert_common_name_check;
    /*!< 跳过对服务器证书 CN 字段的任何验证 */
    const char                  *common_name;
    /*!< 指向包含服务器证书通用名称的字符串指针。
         如果不为 NULL,服务器证书的 CN 必须与此名称匹配;
         如果为 NULL,服务器证书的 CN 必须与主机名匹配。 */
    esp_err_t (*crt_bundle_attach)(void *conf);
    /*!< 指向 esp_crt_bundle_attach 的函数指针。启用使用证书包进行服务器验证,必须在菜单配置中启用 */
    bool                        keep_alive_enable;
    /*!< 启用 keep-alive 超时机制 */
    int                         keep_alive_idle;
    /*!< keep-alive 空闲时间。默认值为 5(秒) */
    int                         keep_alive_interval;
    /*!< keep-alive 间隔时间。默认值为 5(秒) */
    int                         keep_alive_count;
    /*!< keep-alive 数据包重试发送次数。默认值为 3 次 */
    struct ifreq                *if_name;
    /*!< 数据要通过的接口名称。如果未设置,则使用默认接口 */
#if CONFIG_ESP_TLS_USE_SECURE_ELEMENT
    bool use_secure_element;
    /*!< 启用此选项以使用安全元件 */
#endif
#if CONFIG_ESP_TLS_USE_DS_PERIPHERAL
    void *ds_data;
    /*!< 数字签名外设上下文指针,更多详细信息请参阅 ESP - TLS 文档 */
#endif
#if CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
    bool save_client_session;
#endif
#if CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT
    struct esp_transport_item_t *transport;
#endif
} esp_http_client_config_t;

HTTPS请求需要做服务器证书校验,所以需要添加证书,证书是由服务器提供的,这里用到两个服务器进行测试,所以需要用到两个证书,拷贝idf证书到工程main下方

esp-idf/examples/protocols/esp_http_client/main/howsmyssl_com_root_cert.pem

esp-idf/examples/protocols/esp_http_client/main/postman_root_cert.pem

拷贝证书到工程路径后,还需要添加到CMakeLists.txt中,保证烧录到设备中

demo/main/CMakeLists.txt

idf_component_register(
                    SRCS "main.c"
                    INCLUDE_DIRS "."
                    EMBED_TXTFILES howsmyssl_com_root_cert.pem postman_root_cert.pem
                    )

最后请求代码如下:

//howsmyssl服务器证书位置
extern const char howsmyssl_com_root_cert_pem_start[] asm("_binary_howsmyssl_com_root_cert_pem_start");
extern const char howsmyssl_com_root_cert_pem_end[]   asm("_binary_howsmyssl_com_root_cert_pem_end");
//postman服务器证书位置
extern const char postman_root_cert_pem_start[] asm("_binary_postman_root_cert_pem_start");
extern const char postman_root_cert_pem_end[]   asm("_binary_postman_root_cert_pem_end");


static void https_get_request(void)
{
    esp_http_client_config_t config = {
        .method = HTTP_METHOD_GET,
        .url = "https://www.howsmyssl.com/",
        .event_handler = _http_event_handler,
        .cert_pem = howsmyssl_com_root_cert_pem_start,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);
    esp_err_t err = esp_http_client_perform(client);

    if (err == ESP_OK) {
        ESP_LOGI(TAG, "HTTPS Status = %d, content_length = %"PRId64,
                esp_http_client_get_status_code(client),
                esp_http_client_get_content_length(client));
    } else {
        ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
    }
    esp_http_client_cleanup(client);
}

static void https_post(){
    esp_http_client_config_t config = {
        .method = HTTP_METHOD_POST,
        .url = "https://www.postman-echo.com/post",
        .event_handler = _http_event_handler,
        .cert_pem = postman_root_cert_pem_start,
        .is_async = true,
        .timeout_ms = 5000,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);
    esp_err_t err;
    const char *post_data = "this is xiaozhi post data";
    esp_http_client_set_post_field(client, post_data, strlen(post_data));
    while (1) {
        err = esp_http_client_perform(client);
        if (err != ESP_ERR_HTTP_EAGAIN) {
            break;
        }
    }
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "HTTPS Status = %d, content_length = %"PRId64,
                esp_http_client_get_status_code(client),
                esp_http_client_get_content_length(client));
    } else {
        ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
    }
    esp_http_client_cleanup(client);
}

这个实验需要先完成WiFi配网哦,具体看WiFi章节24-WiFi配网

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

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

相关文章

字符编码全解析:ASCII、GBK、Unicode、UTF-8与ANSI

UTF - 8(全球字符能被唯一标识)、GBK、Unicode、ANSI 区别与关联 qwen模型分词器文件 1. ASCII(基础铺垫,理解编码起源) 作用:最早期为处理英文文本设计,是字符编码的基础,后演变成其他编码兼容的一部分 。范围:共 128 个字符(0 - 127),包含英文大小写字母、数字…

MaxCompute开发UDF和UDTF案例

文章目录 一、Java开发UDF1、创建Maven项目2、创建UDF类3、打包上传资源4、创建函数MyUDF5、SQL验证 二、Java开发UDTF1、创建Maven项目2、创建UDTF类3、打包上传更新资源4、创建函数MyUDTF5、SQL验证 三、常见问题1、发布函数报错 一、Java开发UDF 1、创建Maven项目 创建Mav…

49套夏日小清新计划总结日系卡通ppt模板

绿色小清新PPT模版&#xff0c;日系小清新PPT模版&#xff0c;粉红色PPT模版&#xff0c;蓝色PPT模版&#xff0c;草青色PPT模版&#xff0c;日系卡通PPT模版 49套夏日小清新计划总结日系卡通ppt模板&#xff1a;夏日小清新日系卡通PPT模版https://pan.quark.cn/s/9e4270d390fa…

告别硬编码!用工厂模式优雅构建可扩展的 Spring Boot 应用 [特殊字符]

嗨&#xff0c;各位技术伙伴们&#xff01;&#x1f44b; 在日常的软件开发中&#xff0c;我们经常面临需求变更的挑战。如何构建一个既能满足当前需求&#xff0c;又能轻松应对未来变化的系统呢&#xff1f;答案往往藏在那些经典的设计模式中。 今天&#xff0c;我们就来聊聊…

Express教程【006】:使用Express写接口

文章目录 8、使用Express写接口8.1 创建API路由模块8.2 编写GET接口8.3 编写POST接口 8、使用Express写接口 8.1 创建API路由模块 1️⃣新建routes/apiRouter.js路由模块&#xff1a; /*** 路由模块*/ // 1-导入express const express require(express); // 2-创建路由对象…

mongodb集群之分片集群

目录 1. 适用场景2. 集群搭建如何搭建搭建实例Linux搭建实例(待定)Windows搭建实例1.资源规划2. 配置conf文件3. 按顺序启动不同角色的mongodb实例4. 初始化config、shard集群信息5. 通过router进行分片配置 1. 适用场景 数据量大影响性能 数据量大概达到千万级或亿级的时候&…

Starrocks Full GC日志分析

GC日志样例&#xff1a; [2025-06-03T07:36:06.1770800] GC(227) Pause Full (G1 Evacuation Pause) [2025-06-03T07:36:06.1960800] GC(227) Phase 1: Mark live objects [2025-06-03T07:36:06.9480800] GC(227) Cleaned string and symbol table, strings: 47009 processed,…

飞算 JavaAI 赋能老项目重构:破旧立新的高效利器

许多企业的 Java 老项目面临着代码陈旧、架构落后、维护困难等问题。老项目重构势在必行&#xff0c;却又因庞大的代码量、复杂的业务逻辑让开发团队望而却步。 老项目重构困境重重 传统的 Java 老项目往往在长期的迭代和维护中积累了诸多问题。一方面&#xff0c;代码质量堪…

typescript的Interface和Type

类型别名和接口非常相似&#xff0c;在大多数情况下你可以在它们之间自由选择。 几乎所有的 interface 功能都可以在 type 中使用&#xff0c;关键区别在于不能重新开放类型以添加新的属性&#xff0c;而接口始终是可扩展的。 // window.ts.transpileModule(src, {}); 这是调…

java后端生成心电图-jfreechart

用jfreechart生成心电图 先上成功的图片 上代码 1.导入包 implementation org.jfree:jfreechart:1.5.4implementation org.jfree:jcommon:1.0.242.实现代码 对数据进行滤波 转换单位 package com.shinrun.infrastructure.util;import java.util.ArrayList; import java.ut…

算法/机理模型演示平台搭建(二)——算法接口部署(FastApi)

算法/机理模型演示平台搭建(二)—— 算法接口部署(FastApi) 1. 项目结构2. 构建 Docker 镜像3. 运行 Docker 容器4. 访问 API 文档5. 调用 API1. 项目结构 app app/algorithms app/models Dockerfile FROM python:3.9-slimWORKDIR /codeCOPY ./requirements.txt /code…

动态规划-647.回文子串-力扣(LeetCode)

一、题目解析 这里的子字符串是连续的&#xff0c;与之前的子序列不同&#xff0c;这里需要我们统计回文子串的数目。 二、算法原理 这里也有其他算法可以解决该问题&#xff0c;如中心扩展算法 时间复杂度O(N^2)/空间复杂度O(1)&#xff0c;马拉车算法(具有局限性) 时间复杂…

仿真每日一练 | Workbench中接触种类及选择方法简介

Workbench中给我们提供的接触类型主要包括以下几种&#x1f447; ◆ 1、摩擦 ◆ 2、无摩擦 ◆ 3、绑定 ◆ 4、不分离 ◆ 5、粗糙 ◆ 6、强制滑移 下面通过最常用的摩擦和绑定给大家展示两者的区别&#xff0c;同时文末也给大家介绍了几种接触的选择方法。首先先给大家介绍一下…

Go语言中的rune和byte类型详解

1. rune类型 1.1. 基本概念 1. rune是Go语言的内建类型&#xff0c;它是int32的别名&#xff0c;即32位有符号整数&#xff1b; 2. 用于表示一个Unicode码点&#xff0c;全拼Unicode code point&#xff1b; 3. 可以表示任何UTF-8编码的字符&#xff1b; 1.2. 特点 1. 每…

【java面试】redis篇

redis篇 一、适用场景&#xff08;一&#xff09;缓存1、缓存穿透1.1 解决方案1&#xff1a;缓存空数据&#xff0c;查询返回的数据为空&#xff0c;将空结果缓存1.2 解决方案2&#xff1a;布隆过滤器 2、缓存击穿1.1 解决方案1&#xff1a;互斥锁1.2 解决方案2&#xff1a;逻辑…

高效易用的 MAC 版 SVN 客户端:macSvn 使用体验

高效易用的 MAC 版 SVN 客户端&#xff1a;macSvn 使用体验 下载安装使用总结 最近有个项目要使用svn, 但是mac缺乏一款像 Windows 平台 TortoiseSVN 那样全面、高效且便捷的 SVN 客户端工具, 直到博主找到了该工具本文将结合实际使用体验&#xff0c;详细介绍 macSvn工具的核心…

HCIP(BGP综合实验)

一、实验拓扑 AS 划分&#xff1a; AS1&#xff1a;R1&#xff08;环回 L0:172.16.0.1/32&#xff0c;L1:192.168.1.0/24&#xff09;AS2&#xff1a;R2、R3、R4、R5、R6、R7&#xff08;内部运行 OSPF&#xff0c;AS 号为 64512 和 64513 的联盟&#xff09;AS3&#xff1a;R…

Attention Is All You Need (Transformer) 以及Transformer pytorch实现

参考https://zhuanlan.zhihu.com/p/569527564 Attention Is All You Need (Transformer) 是当今深度学习初学者必读的一篇论文。 一. Attention Is All You Need (Transformer) 论文精读 1. 知识准备 机器翻译&#xff0c;就是将某种语言的一段文字翻译成另一段文字。 由…

uniapp+vue2+uView项目学习知识点记录

持续更新中... 1、发送给朋友&#xff0c;分享到朋友圈功能开启 利用onShareAppMessage和onShareTimeline生命周期函数&#xff0c;在script中与data同级去写 // 发送给朋友 onShareAppMessage() {return {title: 清清前端, // 分享标题path: /pages/index/index, // 分享路…

精美的软件下载页面HTML源码:现代UI与动画效果的完美结合

精美的软件下载页面HTML源码&#xff1a;现代UI与动画效果的完美结合 在数字化产品推广中&#xff0c;一个设计精良的下载页面不仅能提升品牌专业度&#xff0c;还能显著提高用户转化率。本文介绍的精美软件下载页面HTML源码&#xff0c;通过现代化UI设计与丰富的动画效果&…