下面给出一个示例工程,展示如何在 ESP32-S3 上通过原生 ESP-IDF 与「豆包API」进行简单交互(例如:HTTP GET / POST),并解析返回的数据。该示例涵盖:Wi-Fi 连接、HTTP 请求、JSON 解析 等核心流程,以便你在此基础上改写、对接实际的「豆包API」。
说明: 由于并不清楚“豆包API”的实际接口形式,这里以一个“示例 URL”和“示例响应 JSON”为例进行演示,供你参考如何编写代码。
ESP32-S3 豆包大模型组合
一、工程结构
假设我们的工程结构如下:
├── CMakeLists.txt
├── main
│ ├── CMakeLists.txt
│ └── main.c
└── sdkconfig
其中 main.c
存放主要逻辑,CMakeLists.txt
根据 ESP-IDF 标准模板编写。
二、主要功能流程
- 初始化 NVS、Wi-Fi 并连接到指定路由器。
- 发起 HTTP 请求 到 “豆包API” 示例地址
https://api.doubao.com/v1/info
。- 可以是 GET 或 POST,根据你的 API 实际需求来调整。
- 解析响应数据(JSON),打印或使用响应内容。
- 定时或按需再次请求,“与原生 IDF 交互”即直接使用官方 API(
esp_http_client_xxx
、cJSON
解析等)。
三、示例代码
1. 顶层 CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
set(PROJECT_NAME "esp_s3_doubao_demo")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(${PROJECT_NAME})
2. main/CMakeLists.txt
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")
3. main.c
下面是一份精简示例,请结合你的项目需求进行修改(如替换 WIFI_SSID
、WIFI_PASS
、“豆包API”的 URL、处理返回数据等)。
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_http_client.h"
#include "cJSON.h"
/*----------------------------
* 修改这里:Wi-Fi SSID & PASS
*---------------------------*/
#define WIFI_SSID "your_wifi_ssid"
#define WIFI_PASS "your_wifi_password"
/*----------------------------
* Tag for logging
*---------------------------*/
static const char *TAG = "DouBao_API";
/*
* Wi-Fi 事件回调
* 当发生STA连接成功、断开等事件时,会调用此函数
*/
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) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "Wi-Fi disconnected, retrying...");
esp_wifi_connect();
} else 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(TAG, "Got IP: %s",
ip4addr_ntoa(&event->ip_info.ip));
}
}
/*
* 初始化并连接 Wi-Fi(Station 模式)
*/
static void wifi_init(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
/* 注册事件回调 */
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
NULL));
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_PSK
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "Wi-Fi init done, connecting...");
}
/*
* 解析服务器返回的 JSON 示例
* 假设返回类似:
* {
* "code": 0,
* "msg": "ok",
* "data": {
* "user": "test_user",
* "score": 88
* }
* }
*/
static void parse_json_response(const char *json_str)
{
cJSON *root = cJSON_Parse(json_str);
if (!root) {
ESP_LOGE(TAG, "Failed to parse JSON");
return;
}
cJSON *code = cJSON_GetObjectItem(root, "code");
cJSON *msg = cJSON_GetObjectItem(root, "msg");
cJSON *data = cJSON_GetObjectItem(root, "data");
if (cJSON_IsNumber(code) && cJSON_IsString(msg) && data) {
ESP_LOGI(TAG, "code = %d, msg = %s", code->valueint, msg->valuestring);
cJSON *user = cJSON_GetObjectItem(data, "user");
cJSON *score= cJSON_GetObjectItem(data, "score");
if (cJSON_IsString(user) && cJSON_IsNumber(score)) {
ESP_LOGI(TAG, "user: %s, score: %d",
user->valuestring, score->valueint);
}
} else {
ESP_LOGW(TAG, "JSON format not expected");
}
cJSON_Delete(root);
}
/*
* HTTP GET 示例:访问「豆包API」
*/
static void doubao_http_get_task(void *pv)
{
// 配置 HTTP Client
esp_http_client_config_t config = {
.url = "https://api.doubao.com/v1/info", // 示例URL
.method = HTTP_METHOD_GET,
.timeout_ms = 5000,
// 若需要HTTPS并使用自签证书,需要设置 .cert_pem 或 skip_cert_common_name_check 等
};
esp_http_client_handle_t client = esp_http_client_init(&config);
while (1) {
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
int status_code = esp_http_client_get_status_code(client);
int content_length = esp_http_client_get_content_length(client);
ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d",
status_code, content_length);
// 读取服务器返回内容
char *buffer = malloc(content_length + 1);
if (buffer) {
esp_http_client_read(client, buffer, content_length);
buffer[content_length] = '\0'; // 结尾
ESP_LOGI(TAG, "Response: %s", buffer);
// 解析 JSON
parse_json_response(buffer);
free(buffer);
}
} else {
ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
}
// 间隔一定时间再请求
vTaskDelay(pdMS_TO_TICKS(5000));
}
// 不会到这里,但如果要结束,需要 clean up
esp_http_client_cleanup(client);
vTaskDelete(NULL);
}
/*
* HTTP POST 示例:向「豆包API」发送数据
*/
static void doubao_http_post_task(void *pv)
{
// 构造要发送的 JSON
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "device_id", "esp_s3_test");
cJSON_AddNumberToObject(root, "temperature", 26.5);
char *post_data = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
esp_http_client_config_t config = {
.url = "https://api.doubao.com/v1/report",
.method = HTTP_METHOD_POST,
.timeout_ms = 5000,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
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) {
int status_code = esp_http_client_get_status_code(client);
int content_length = esp_http_client_get_content_length(client);
ESP_LOGI(TAG, "HTTP POST Status = %d, content_length = %d",
status_code, content_length);
// 如果需要读取响应
if (content_length > 0) {
char *buffer = malloc(content_length + 1);
if (buffer) {
esp_http_client_read(client, buffer, content_length);
buffer[content_length] = '\0';
ESP_LOGI(TAG, "Response: %s", buffer);
// 根据需要解析
free(buffer);
}
}
} else {
ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(err));
}
free(post_data);
esp_http_client_cleanup(client);
vTaskDelete(NULL);
}
/*
* app_main() -- 主函数
*/
void app_main(void)
{
// 1. 初始化 NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
nvs_flash_erase();
nvs_flash_init();
}
// 2. 初始化 Wi-Fi
wifi_init();
// 3. 等待 Wi-Fi 连接成功后,再启动任务
// 可以粗略延时,也可以在 IP_EVENT_STA_GOT_IP 里创建任务,这里简单示例延时
vTaskDelay(pdMS_TO_TICKS(5000));
// 4. 创建一个任务执行 GET
xTaskCreate(doubao_http_get_task, "DouBaoGetTask", 4096, NULL, 5, NULL);
// 5. 创建一个任务执行 POST (视情况,需要时再创建)
// 这里示例一次性发起 POST 并退出
xTaskCreate(doubao_http_post_task, "DouBaoPostTask", 4096, NULL, 5, NULL);
}
关键点说明
-
Wi-Fi 初始化
- 使用了 ESP-IDF 的官方方式:
esp_wifi_init
→esp_wifi_set_mode(WIFI_MODE_STA)
→esp_wifi_set_config
→esp_wifi_start
→esp_wifi_connect
。 - 通过注册事件回调 (
event_handler
) 监听连接状态,获取 IP 后就能上网。
- 使用了 ESP-IDF 的官方方式:
-
HTTP 请求
- 使用 esp_http_client 模块。
- GET: 先
esp_http_client_perform()
,然后读取状态码、读取响应内容并解析。 - POST: 设置 header、post_field;
perform
后获取返回数据。
-
JSON 解析
- 用 cJSON 做解析、构造 JSON 数据。IDF 自带 cJSON 库,需在
menuconfig -> Component config -> JSON
中启用(若默认没启用)。 - 注意内存分配和释放(
malloc
,free
,cJSON_PrintUnformatted
,cJSON_Delete
等)。
- 用 cJSON 做解析、构造 JSON 数据。IDF 自带 cJSON 库,需在
-
安全 (HTTPS)
- 若豆包API 使用 HTTPS 且证书非权威机构签署,需要在
esp_http_client_config_t
里配置.cert_pem
或者设置.skip_cert_common_name_check = true
来跳过校验(不推荐)。 - 若是正式环境,建议做好服务器证书验证。
- 若豆包API 使用 HTTPS 且证书非权威机构签署,需要在
-
任务栈大小
- 此处示例给了 4096;根据实际请求大小、JSON 解析复杂度等,可能需要调大或调小。
四、menuconfig
相关设置
-
启用 Wi-Fi
idf.py menuconfig -> Component config -> Wi-Fi
- 确保 STA 模式可用、选对国家代码等。
-
启用 HTTPS / SSL(若调用的豆包API是 HTTPS)
menuconfig -> Component config -> mbedTLS
- 可以调整
MBEDTLS_SSL_MAX_CONTENT_LEN
(默认4096) 等参数,避免握手时内存不足。
-
启用 cJSON
menuconfig -> Component config -> JSON -> cJSON library
- 若不需要 cJSON,可以改用其它 JSON 库或更精简的处理方式。
-
其他网络协议栈
menuconfig -> Component config -> LWIP
- 调整 TCP/IP 缓冲区、最大 socket 数量等,视并发需求配置。
五、验证方法
-
编译并烧写
- 在项目目录下执行:
idf.py set-target esp32s3 idf.py menuconfig # 配置 Wi-Fi、日志级别等 idf.py build idf.py flash monitor
- 在项目目录下执行:
-
观察日志
- 启动后,ESP32-S3 会连接到指定路由器;
- 日志中若出现
Got IP: ...
则表示连接成功; - 然后会看到 GET 和 POST 任务的执行结果、返回内容等。
-
与“豆包API”真实环境对接
- 替换为你实际的 API 地址 (域名/IP)、路由、URL path、POST body 等即可。
六、总结
- 上述示例演示了如何在 ESP32-S3 上使用原生 ESP-IDF 的 API(Wi-Fi、HTTP Client、cJSON)与一个假想的「豆包API」进行网络交互。
- 你可以根据 API 文档(如果“豆包API”有更详细接口说明),修改请求方式(GET/POST/PUT 等)、修改 JSON 格式解析逻辑,以及加入更多业务处理(如存储 NVS、本地处理数据等)。
- 若需要 HTTPS 安全,请确保正确配置服务器证书或使用 IDF 提供的 CA 证书校验机制。
这样,你就能完成一个基于 ESP32-S3 原生 IDF 与 “豆包API” 进行完整交互的示例工程。祝你开发顺利!