(ESP32学习记录)在ESP32C3上实现HTTP Web服务器

借鉴ESP32官方例程实现了简单的HTTP Web服务器,代码如下:

/*HTTP服务器端*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_netif.h"
#include "nvs_flash.h"
#include "wifi_provisioning/manager.h"
#include "wifi_provisioning/scheme_softap.h"
#include "socket.h"
#include "esp_http_server.h"
/*宏定义*/
#define ERASE_NVS 0
// 清除储存的wifi密码->0: 沿用上次的WIFI信息进行连接; 1: 清除WIFI信息,使用SOFTAP一键配网进行新链接
/*函数声明*/
void WIFI_CALLBACK_FUN(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data); // 事件队列回调函数
void wifi_connect(void);                                                                                          // WIFI连接处理函数
esp_err_t HTTPD_URI_HANDLER_GET_FUN(httpd_req_t *r); //HTTP web server 事件URI处理函数, 处理来自于客户端的GET请求
esp_err_t HTTPD_URI_HANDLER_POST_FUN(httpd_req_t *r); //HTTP web server 事件URI处理函数, 处理来自于客户端的POST请求
/*全局变量*/

/***************************************************************************************************************/
/*MAIN函数*/
void app_main(void)
{
    wifi_connect(); // 连接wifi
    /*1. 配置Web服务器*/
    httpd_handle_t handle;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.server_port = 80;
    config.lru_purge_enable = true; //清除“最近最少使用”连接
    httpd_start(&handle, &config);
    /*2. 注册URI处理事件*///注册函数必须放在httpd_start函数成功执行之后
    httpd_uri_t uri_handler_get_from_client_dt =
    {
        .uri = "/ok",
        .method = HTTP_GET, //客户端发送来的GET请求
        .handler = HTTPD_URI_HANDLER_GET_FUN
    };
    httpd_register_uri_handler(handle, &uri_handler_get_from_client_dt);    //客户端GET请求事件注册

    httpd_uri_t uri_handler_post_from_client_dt =
    {
        .uri = "/ok",
        .method = HTTP_POST,
        .handler = HTTPD_URI_HANDLER_POST_FUN
    };
    httpd_register_uri_handler(handle, &uri_handler_post_from_client_dt);  //客户端POST请求事件注册
    /*while循环,空任务*/
    while(1)
    {
        vTaskDelay(10);
    }
    /*end. 关闭http服务*/
    httpd_stop(handle);
}
/*HTTPD注册URI的处理函数*/
esp_err_t HTTPD_URI_HANDLER_GET_FUN(httpd_req_t *r)
{
    char *snd_dat = "Hello http client!\0";
    esp_err_t ret = httpd_resp_send(r, snd_dat, strlen(snd_dat));
    if(ret == ESP_OK)
    {
        printf("已经向客户端发送数据: %s\n", snd_dat);
        printf("URL=%s\n", r->uri);
        printf("LEN=%d\n", r->content_len);
    }
    return ret;
}
esp_err_t HTTPD_URI_HANDLER_POST_FUN(httpd_req_t *r)
{
    char recv_buf[100] = {0};
    int err_cnt = 0;
    int recv_dt_len = 0;
    printf("收到POST请求,开始处理!\n");
    printf("数据长度为: %d\n", r->content_len);
    printf("recv_buf size is %d\n", sizeof(recv_buf));
    if((r->content_len) > sizeof(recv_buf))    //如果接收的数据长度大于缓冲去大小
    {
        recv_dt_len = sizeof(recv_buf);
        while(recv_dt_len > 0)
        {
            int ret = httpd_req_recv(r, recv_buf, sizeof(recv_buf));
            if(ret > 0)
            {
                printf("收到的数据为:%s\n", recv_buf);
                recv_dt_len = recv_dt_len - ret;
                err_cnt = 0;
            }
            else if(ret == HTTPD_SOCK_ERR_TIMEOUT)   //连接超时,需要重试
            {
                err_cnt ++;
                vTaskDelay(100);
                if(err_cnt == 20)   //重试次数20
                {
                    printf("TIMEOUT连接超时!\n");
                    httpd_resp_send_404(r); //向客户端发送404错误
                    break;
                }
            }
            else if(ret < 0)
            {
                printf("错误!\n");
                httpd_resp_send_408(r); //向客户端发送错误
                break;
            }
            else    //ret=0->连接断开或接收缓冲区(HTTP缓冲区)为空
            {
                break;
            }
        }
    }
    else        //接收数据长度小于缓冲区大小
    {
        recv_dt_len = r->content_len;
        int ret = httpd_req_recv(r, recv_buf, recv_dt_len);
        if(ret > 0)
            printf("收到的数据为:%s\n", recv_buf);
        else if(ret < 0)
        {
            if(ret == HTTPD_SOCK_ERR_TIMEOUT)
            {
                while(1)
                {
                    err_cnt ++;
                    vTaskDelay(100);
                    ret = httpd_req_recv(r, recv_buf, recv_dt_len);
                    if(ret > 0)
                    {
                        printf("收到的数据为:%s\n", recv_buf);
                        break;
                    }
                    else        //其他小于0的情况
                        break;
                    if(err_cnt == 20)
                    {
                        printf("TIMEOUT连接超时!\n");
                        httpd_resp_send_404(r); //向客户端发送404错误
                        break;
                    }
                }
            }
        }
    }
    printf("POST请求处理完毕,退出函数!\n");
    return ESP_OK;
    //HTTPD_SOCK_ERR_INVALID
}
/**********************************************************************************************/
/*回调函数*/
void WIFI_CALLBACK_FUN(void *event_handler_arg, esp_event_base_t eb, int32_t ei, void *event_data)
{
    if (eb == WIFI_EVENT)
    {
        if (ei == WIFI_EVENT_STA_START)
        {
            esp_wifi_connect(); // 开始连接WIFI
            printf("正在连接WIFI!\n");
        }
        if (ei == WIFI_EVENT_STA_DISCONNECTED)
        {
            printf("自动连接失败,正在启动SOFTAP配网服务!\n");
            const char *pop = "abcd1234";
            const char *wifi_name = "SOFTAP_PROV_MGR_PROB";
            const char *wifi_key = "12345678";
            wifi_prov_mgr_start_provisioning(WIFI_PROV_SECURITY_1, pop, wifi_name, wifi_key);
            wifi_prov_mgr_wait();
            wifi_prov_mgr_deinit();
            printf("Provisioning服务结束,连接WIFI成功!\n");
        }
    }
    if (eb == IP_EVENT && ei == IP_EVENT_STA_GOT_IP)
    {
        printf("WIFI连接成功!\n");
        ip_event_got_ip_t *sta_ip = (ip_event_got_ip_t *)event_data;
        printf("本机IP地址为: " IPSTR "\n", IP2STR(&sta_ip->ip_info.ip));
    }
}

/*连接WIFI*/
void wifi_connect(void)
{
    nvs_flash_init();
#if ERASE_NVS != 0
    nvs_flash_erase();
    nvs_flash_init();
#endif
    esp_event_loop_create_default(); // 创建默认事件队列
    esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, WIFI_CALLBACK_FUN, NULL, NULL);
    esp_netif_init();                    // 初始化网卡
    esp_netif_create_default_wifi_ap();  // 创建默认网卡ap
    esp_netif_create_default_wifi_sta(); // 创建默认网卡sta
    wifi_init_config_t esp_wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&esp_wifi_init_config); // 初始化WIFI功能
    /*softAP配网*/
    wifi_prov_mgr_config_t wifi_prov_mgr_config_t =
        {
            .scheme = wifi_prov_scheme_softap,
            .app_event_handler = WIFI_PROV_EVENT_HANDLER_NONE};
    wifi_prov_mgr_init(wifi_prov_mgr_config_t);
    bool wifi_prov_mgr_diag = false;
    wifi_prov_mgr_is_provisioned(&wifi_prov_mgr_diag);
    if (wifi_prov_mgr_diag == false)
    {
        const char *pop = "abcd1234";
        const char *wifi_name = "SOFTAP_PROV_MGR_PROB";
        const char *wifi_key = "12345678";
        wifi_prov_mgr_start_provisioning(WIFI_PROV_SECURITY_1, pop, wifi_name, wifi_key);
        wifi_prov_mgr_wait();
        wifi_prov_mgr_deinit();
        printf("Provisioning服务结束,连接WIFI成功!\n");
    }
    else
    {
        esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_STA_START, WIFI_CALLBACK_FUN, NULL, NULL);        // 注册WIFI开启事件
        esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, WIFI_CALLBACK_FUN, NULL, NULL);    // 注册WIFI连接事件
        esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, WIFI_CALLBACK_FUN, NULL, NULL); // 注册WIFI连接失败事件
        esp_wifi_start();
    }
    esp_wifi_set_ps(WIFI_PS_NONE); // 关闭WIFI的省电模式
}

使用浏览器向ESP32 http web server发送POST请求

var xhr = new XMLHttpRequest();
xhr.open("POST", "http://192.168.137.123/ok", true);
xhr.send("hello web server!");

<think>我们被要求配置ESP32-C2通过Web服务器连接Wi-Fi。这通常意味着我们要在ESP32-C2上运行一个Web服务器,当设备启动时,它进入配置模式(作为AP),用户可以通过浏览器访问该Web服务器,输入本地Wi-Fi网络的SSID和密码,然后设备将这些凭证保存并尝试连接到该网络。 参考引用[1]和[2]提到ESP32-C2支持Wi-Fi和BLE,我们可以利用其Wi-Fi功能。引用[5]提供了一个使用esp-idf的Wi-Fi初始化的示例代码,但我们需要的是更高级的配置:通过Web服务器接收Wi-Fi凭证。 步骤概述: 1. 初始化ESP32-C2的Wi-Fi为AP模式,创建一个配置用的热点。 2. 启动一个Web服务器,提供简单的配置页面。 3. 用户通过浏览器访问该页面,输入目标Wi-Fi的SSID和密码。 4. Web服务器接收表单数据,保存到非易失性存储(NVS)。 5. 设备重启或切换为STA模式,使用保存的凭证连接目标Wi-Fi。 6. 如果连接成功,则关闭AP模式(或保持双模式,根据需求),并告知用户配置成功。 我们将使用ESP-IDF框架进行开发。由于引用[5]中提到了使用esp-idf-v5.1.1,我们将基于此版本编写代码。 详细步骤: 步骤1:设置项目环境 确保已安装ESP-IDF v5.1.1,并设置好环境。 步骤2:包含必要的头文件 在main.c中包含以下头文件: ```c #include "esp_wifi.h" #include "esp_event.h" #include "esp_log.h" #include "esp_http_server.h" #include "nvs_flash.h" #include "esp_system.h" #include "esp_netif.h" ``` 步骤3:初始化NVS(非易失性存储) 用于保存Wi-Fi凭证。 ```c void initialize_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); } ``` 步骤4:创建默认事件循环和网络接口 参考引用[5]的示例。 步骤5:配置AP模式 设置一个热点,用户可以通过手机或电脑连接此热点,然后访问Web服务器。 步骤6:启动Web服务器 使用ESP-IDF的HTTP服务器组件,创建一个简单的Web服务器,提供配置页面。 步骤7:处理HTTP请求 我们需要两个基本的HTTP请求处理: - GET /:返回一个HTML表单,用于输入SSID和密码。 - POST /configure:接收表单提交的SSID和密码,保存到NVS,然后尝试连接。 步骤8:保存凭证并连接目标Wi-Fi 从POST请求中获取SSID和密码,保存到NVS,然后重启设备(或直接切换模式尝试连接)。 步骤9:连接目标Wi-Fi 使用保存的凭证连接目标Wi-Fi。 步骤10:处理连接结果 连接成功则切换到STA模式(关闭AP模式可选),失败则返回配置页面(或保持AP模式以便重新配置)。 考虑到代码量较大,这里只给出关键部分的代码示例。 代码示例: 首先,定义一些全局变量和常量: ```c static const char *TAG = "wifi_config"; // 用于存储Wi-Fi配置的结构体 typedef struct { char ssid[32]; char password[64]; } wifi_config_t; ``` 初始化AP模式: ```c void start_ap_mode() { esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); wifi_config_t wifi_ap_config = { .ap = { .ssid = "ESP32-C2_Config", // AP的SSID .password = "12345678", // 密码,至少8位 .ssid_len = strlen("ESP32-C2_Config"), .max_connection = 4, .authmode = WIFI_AUTH_WPA_WPA2_PSK }, }; if (strlen("12345678") == 0) { wifi_ap_config.ap.authmode = WIFI_AUTH_OPEN; } ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_ap_config)); ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(TAG, "AP mode started. SSID: %s, Password: %s", wifi_ap_config.ap.ssid, wifi_ap_config.ap.password); } ``` 创建HTTP服务器: ```c httpd_handle_t start_webserver(void) { httpd_config_t config = HTTPD_DEFAULT_CONFIG(); httpd_handle_t server = NULL; if (httpd_start(&server, &config) == ESP_OK) { // 注册URI处理函数 httpd_register_uri_handler(server, &uri_get); httpd_register_uri_handler(server, &uri_post); return server; } return NULL; } ``` 定义GET和POST的URI处理结构体: ```c // GET处理函数:返回配置页面 static const httpd_uri_t uri_get = { .uri = "/", .method = HTTP_GET, .handler = get_handler, .user_ctx = NULL }; // POST处理函数:接收配置数据 static const httpd_uri_t uri_post = { .uri = "/configure", .method = HTTP_POST, .handler = post_handler, .user_ctx = NULL }; ``` GET处理函数返回一个简单的HTML表单: ```c static esp_err_t get_handler(httpd_req_t *req) { const char* html_form = "<!DOCTYPE html><html><body>" "<h2>Wi-Fi Configuration</h2>" "<form action=\"/configure\" method=\"post\">" "SSID: <input type=\"text\" name=\"ssid\"><br>" "Password: <input type=\"password\" name=\"password\"><br>" "<input type=\"submit\" value=\"Connect\">" "</form></body></html>"; httpd_resp_set_type(req, "text/html"); httpd_resp_send(req, html_form, strlen(html_form)); return ESP_OK; } ``` POST处理函数接收表单数据,保存到NVS,并尝试连接: ```c static esp_err_t post_handler(httpd_req_t *req) { 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) { return ESP_FAIL; } content[recv_size] = &#39;\0&#39;; // 解析表单数据(简单起见,这里假设只有两个字段) char ssid[32] = {0}; char password[64] = {0}; // 实际应用中应使用更健壮的解析方式(如urldecode和字符串解析) // 这里仅作示例,假设表单数据格式为: ssid=...&password=... char *ptr = strstr(content, "ssid="); if (ptr) { ptr += 5; // 跳过"ssid=" char *end = strchr(ptr, &#39;&&#39;); if (end) { *end = &#39;\0&#39;; url_decode(ptr, ssid, sizeof(ssid)); } else { // 如果没有&,则取到字符串末尾 url_decode(ptr, ssid, sizeof(ssid)); } } ptr = strstr(content, "password="); if (ptr) { ptr += 9; // 跳过"password=" url_decode(ptr, password, sizeof(password)); } // 保存到NVS save_wifi_config(ssid, password); // 返回响应,提示正在连接 const char* response = "<html><body><h1>Configuration saved. Connecting to Wi-Fi...</h1></body></html>"; httpd_resp_send(req, response, strlen(response)); // 延迟一段时间后重启(或者直接切换模式连接) vTaskDelay(2000 / portTICK_PERIOD_MS); esp_restart(); return ESP_OK; } ``` 注意:这里需要实现`url_decode`函数和`save_wifi_config`函数。 保存Wi-Fi配置到NVS的示例: ```c void save_wifi_config(const char* ssid, const char* password) { nvs_handle_t nvs; ESP_ERROR_CHECK(nvs_open("storage", NVS_READWRITE, &nvs)); ESP_ERROR_CHECK(nvs_set_str(nvs, "ssid", ssid)); ESP_ERROR_CHECK(nvs_set_str(nvs, "password", password)); ESP_ERROR_CHECK(nvs_commit(nvs)); nvs_close(nvs); } ``` 在`app_main`函数中,我们需要: 1. 初始化NVS 2. 尝试从NVS读取保存的Wi-Fi配置,如果存在则连接目标Wi-Fi(STA模式) 3. 如果连接失败(或没有保存的配置),则进入AP模式并启动Web服务器 连接目标Wi-Fi的函数可以参考引用[5]中的`example_connect()`,但这里我们需要自己实现。 注意:为了简化,我们可能需要在连接失败时重新进入配置模式。在实际应用中,可能需要一个超时机制,比如尝试连接多次失败后重新开启AP模式。 由于代码较长,以上仅为核心步骤和代码片段。实际开发中还需要考虑错误处理、内存释放、事件处理等。 最后,编译并烧录到ESP32-C2设备。 相关问题: 1. 如何从NVS中读取保存的Wi-Fi配置? 2. 如何实现URL解码(url_decode)? 3. 如何实现连接目标Wi-Fi的函数? 4. 如何让设备在连接目标Wi-Fi失败后自动回到配置模式? 5. 如何优化Web界面,使其更美观易用? 6. 如何增加安全性(例如,配置热点的密码强度,防止未授权访问)? 根据用户要求,生成相关问题如下:</think>### ESP32-C2 通过 Web 服务器配置 Wi-Fi 连接指南 以下是基于 ESP-IDF 框架的详细配置步骤(以 V5.1.1 为例): --- #### **步骤 1:初始化基础组件** ```c #include "esp_wifi.h" #include "esp_http_server.h" #include "nvs_flash.h" #include "esp_event.h" void app_main() { // 初始化 NVS 存储(保存 Wi-Fi 凭证) 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); // 初始化网络和事件循环 ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); } ``` --- #### **步骤 2:启动 AP 模式(配置热点)** ```c void start_ap_mode() { esp_netif_create_default_wifi_ap(); // 创建 AP 接口 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // 设置 AP 配置 wifi_config_t ap_config = { .ap = { .ssid = "ESP32-C2_Config", // 热点名称 .password = "12345678", // 至少 8 位密码 .max_connection = 4, .authmode = WIFI_AUTH_WPA_WPA2_PSK } }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &ap_config)); ESP_ERROR_CHECK(esp_wifi_start()); } ``` --- #### **步骤 3:创建 Web 服务器** ```c // 处理根目录请求(返回配置页面) static esp_err_t root_handler(httpd_req_t *req) { const char* html_form = "<form action=&#39;/save&#39; method=&#39;post&#39;>" "SSID: <input type=&#39;text&#39; name=&#39;ssid&#39;><br>" "Password: <input type=&#39;password&#39; name=&#39;password&#39;><br>" "<input type=&#39;submit&#39; value=&#39;Connect&#39;>" "</form>"; httpd_resp_set_type(req, "text/html"); httpd_resp_send(req, html_form, HTTPD_RESP_USE_STRLEN); return ESP_OK; } // 处理表单提交 static esp_err_t save_handler(httpd_req_t *req) { char content[100]; int recv_len = httpd_req_recv(req, content, sizeof(content)); // 解析 SSID 和密码 char ssid[32] = {0}; char password[64] = {0}; sscanf(content, "ssid=%[^&]&password=%s", ssid, password); // 保存到 NVS nvs_handle_t nvs; nvs_open("wifi_config", NVS_READWRITE, &nvs); nvs_set_str(nvs, "ssid", ssid); nvs_set_str(nvs, "password", password); nvs_commit(nvs); nvs_close(nvs); // 返回响应 httpd_resp_send(req, "Wi-Fi配置已保存!设备将重启连接...", HTTPD_RESP_USE_STRLEN); vTaskDelay(2000 / portTICK_PERIOD_MS); esp_restart(); // 重启以连接新网络 return ESP_OK; } // 启动 Web 服务器 void start_webserver() { httpd_config_t config = HTTPD_DEFAULT_CONFIG(); httpd_handle_t server = NULL; if (httpd_start(&server, &config) == ESP_OK) { httpd_register_uri_handler(server, &(httpd_uri_t){ .uri = "/", .method = HTTP_GET, .handler = root_handler }); httpd_register_uri_handler(server, &(httpd_uri_t){ .uri = "/save", .method = HTTP_POST, .handler = save_handler }); } } ``` --- #### **步骤 4:连接目标 Wi-Fi(重启后执行)** ```c void connect_to_wifi() { // 从 NVS 读取凭证 char ssid[32], password[64]; nvs_handle_t nvs; nvs_open("wifi_config", NVS_READONLY, &nvs); nvs_get_str(nvs, "ssid", ssid, sizeof(ssid)); nvs_get_str(nvs, "password", password, sizeof(password)); nvs_close(nvs); // 配置 STA 模式 esp_netif_create_default_wifi_sta(); wifi_config_t sta_config = { .sta = { .ssid = "", .password = "", } }; strcpy((char*)sta_config.sta.ssid, ssid); strcpy((char*)sta_config.sta.password, password); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &sta_config)); ESP_ERROR_CHECK(esp_wifi_connect()); } ``` --- #### **主函数逻辑** ```c void app_main() { initialize_nvs(); // 步骤 1 // 检查是否已有保存的凭证 nvs_handle_t nvs; if (nvs_open("wifi_config", NVS_READONLY, &nvs) == ESP_OK) { size_t ssid_len = 0; if (nvs_get_str(nvs, "ssid", NULL, &ssid_len) == ESP_OK && ssid_len > 0) { connect_to_wifi(); // 步骤 4 return; } } // 无凭证则进入配置模式 start_ap_mode(); // 步骤 2 start_webserver(); // 步骤 3 } ``` --- ### **关键说明** 1. **工作流程** - 设备首次启动时创建热点 `ESP32-C2_Config` - 用户连接热点后访问 `http://192.168.4.1` 输入 Wi-Fi 凭证 - 凭证保存后设备重启并自动连接目标网络 2. **安全建议** - 在 `save_handler` 中增加输入验证(如 SSID 长度检查) - 使用 HTTPS 需配置 SSL 证书(需额外存储空间) 3. **优化方向** - 添加连接状态反馈(如 LED 指示灯) - 实现 **双重模式(AP+STA)**,配置时不中断服务 - 通过 `esp_event_handler` 监听 `WIFI_EVENT_STA_CONNECTED` 事件[^5] > **注意**:ESP32-C2 的 RISC-V 架构需使用 ESP-IDF 的 RISC-V 工具链编译[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值