网络访问与文件下载¶
本文说明本章节功能的接入方式、关键源码和应用参考,帮助开发者快速完成集成。
1. 应用例程¶
场景 |
文件 |
调用接口 |
|---|---|---|
小智机器人联网对话入口 |
|
网络数据回调、 |
AI App 火山方舟 Chat Completions |
|
|
AI App 火山同声传译 WebSocket |
|
WebSocket 连接、音频数据收发回调 |
浏览器 HTTP 图片 / 视频 / JSON 请求 |
|
|
云书城列表 / 封面 / 下载 |
|
|
高德地图网络请求 |
|
高德地图网络请求封装接口 |
高德地图离线包下载 |
|
|
Wi-Fi P2P / 投屏入口 |
|
Wi-Fi P2P / 投屏入口回调 |
2. 简介¶
本文中的“网络访问”指设备通过 Wi-Fi、4G/LTE 或 BT PAN 获得 IP 网络能力后,由固件业务代码直接访问服务器。例如:
获取天气数据;
同步账号、设备配置或业务数据;
下载电子书、图片、资源包、音频文件;
通过 HTTP/HTTPS、WebSocket 或 MQTT 访问云服务;
访问大模型服务,获取文本、语音或流式响应。
业务开发时建议把网络访问拆成四层理解:
层级 |
作用 |
常见选择 |
说明 |
|---|---|---|---|
网络承载 |
让设备获得 IP 网络能力 |
Wi-Fi、4G/LTE、BT PAN |
业务代码通常只关心网络是否 ready |
网络状态 |
判断当前是否可以访问服务器 |
|
访问服务器前必须先判断 |
应用协议 |
与服务器交换数据 |
HTTP/HTTPS、WebSocket、MQTT、socket |
HTTP 下载文件,WebSocket/MQTT 适合长连接和 AI 交互 |
数据落点 |
服务器返回数据保存到哪里 |
内存、文件系统、ringbuffer、业务 parser |
小文件可放内存,大文件建议放文件系统,流式音频建议放 ringbuffer |
本文不描述手机 App 下载后再通过蓝牙发送给设备的文件传输流程。该类流程属于手机协同传输或文件传输业务,不属于设备直接访问网络。
3. Solution 提供的访问网络方式¶
3.1 Wi-Fi¶
Wi-Fi 适合设备直接连接路由器访问服务器。当前 Solution 中 Wi-Fi 主要通过 SDIO Wi-Fi 模块接入,应用侧可使用 app_wifi_open()、app_wifi_sta_open()、app_wifi_scan()、app_wifi_connect()。底层模组、menuconfig 和调试命令请参考 SDIO Wi-Fi。
3.2 4G/LTE¶
4G/LTE 适合设备独立联网,不依赖手机或路由器。当前应用示例通过 app_net_init() 和 NET_OP_LTE_BEGIN 发起 LTE 入网。模组接入、AT 命令、PPP/PPPoS 或 netdev/lwIP 适配请参考 4G(LTE)。
3.3 BT PAN¶
BT PAN 是通过蓝牙 PAN profile 获得 IP 网络能力。普通 BT 配对或 A2DP/HFP 连接不等于可以上网,必须启用 PAN profile 并完成 lwIP 网络适配。
完成 PAN 入网后,业务层看到的是一个 IP 网络通道,可以使用 HTTP/HTTPS、socket、WebSocket 等协议访问服务器。典型场景包括获取天气数据、同步账号或设备配置、下载电子书和图片资源、获取语音或音乐资源,也可以用于 HTTP OTA。OTA 只是 PAN 的一个应用场景,不代表 PAN 只能用于 OTA。
4. 配置 Solution 实现访问网络¶
配置网络访问时,先选择设备使用哪一种网络承载,再配置对应承载。Wi-Fi、4G/LTE、BT PAN 都可以让设备获得 IP 网络能力,项目中可以只选一种,也可以同时支持多种;业务代码最终都应统一等待网络 ready 后再访问服务器。
建议按下面顺序处理:
步骤 |
配置内容 |
说明 |
|---|---|---|
1 |
选择网络承载 |
按产品形态选择 Wi-Fi、4G/LTE、BT PAN 中的一种或多种 |
2 |
打开网络框架 |
路径为 |
3 |
配置具体承载 |
Wi-Fi 配置模组和驱动,4G 配置 modem 和数据通道,BT PAN 配置 PAN profile 和 lwIP 适配 |
4 |
业务代码等待网络 ready |
访问服务器前等待 |
4.1 Wi-Fi 配置入口¶
应用层 Wi-Fi 开关为 APP_WIFI_USED。以包含该配置项的产品工程为例,路径为:menuconfig (Top) → Product Applicaiton Config → Enanle application/setting/setting_wifi (APP_WIFI_USED)。该开关会自动选择 RT_USING_WIFI 和 RT_USING_LWIP;当前 grid_view 示例默认开启,其他产品工程应以其自身 Kconfig 默认值为准。APP_NETWORK_USED 依赖 APP_WIFI_USED,因此会随该承载条件变为可用;这些均是 Kconfig 配置符号,不应手工在配置头或源码中定义同名宏。
SDIO/SDHCI 控制器的路径为:menuconfig (Top) → SiFli SDK configuration → Board Config → SDIO (BSP_USING_SDIO);BSP_USING_SDIO 直接依赖 BF0_HCPU,且其父级板级驱动菜单不在 PC Simulator 工程显示,因此仅在非 PC Simulator 的 HCPU 工程显示;启用后自动选择 RT_USING_SDIO。再按硬件选择 SDMMC1 或 SDMMC2,并将对应接口模式设为 Wi-Fi 所需的 SDIO 模式。Wi-Fi 模组开关的路径为:menuconfig (Top) → SiFli SDK configuration → Board Config → Board Peripherals,在其中选择对应模组。SWT6621SL 使用 Enable swt 6621 wifi mode (BSP_WIFI_SWT6621);AIC8800MC 依次打开 WIFI mod aic8800mc enable (WIFI_USING_AIC8800MC)、Enable using aicxtek WIFI (USING_AICXTEK_WIFI),并在 hardware wifi interface type 中选择 SDIO interface sdio (WIFI_INF_TYPE_SDIO)。
具体模组配置请参考 SDIO Wi-Fi - menuconfig 配置。如果 Wi-Fi 已编译但无法扫描或连接,优先回到 SDIO Wi-Fi 检查 SDIO 时钟、pinmux、电源控制和固件路径。
应用代码中常用接口为:
app_wifi_open();
app_wifi_sta_open();
app_wifi_scan();
app_wifi_connect(ssid, password, NULL);
4.2 4G/LTE 配置入口¶
4G/LTE 的总开关路径为:menuconfig (Top) → Components Config → Using 4G modem module (USING_MODEM_SUPPORT)。该开关无直接 depends on,默认关闭;启用后自动选择 BSP_USING_MODEM、PERI_USING_ONCHIP 和 RT_USING_LWIP。APP_NETWORK_USED 不依赖 LTE/modem;如业务需要使用该网络状态框架,须另行满足 BT_FINSH_PAN、APP_WIFI_USED 或 BSP_USING_PC_SIMULATOR 中的任一条件。
剩下只按实际 modem 配置 UART/AT、SIM/APN 和数据通道。如果使用 PPP/PPPoS,再打开对应 PPP 组件。完整的 lwIP、PPP 和 PPPoS 路径请参考 4G(LTE)- 配置项。
应用代码中常用接口为:
app_net_init();
send_msg_to_net_thread(NET_OP_LTE_BEGIN, NULL, 0);
4.3 BT PAN 配置入口¶
BT PAN 配置需要先打开 BT 基础功能和 lwIP,再打开 PAN profile。配置时按以下顺序确认:
在 HCPU 工程的
menuconfig (Top) → Bluetooth config → Enable bluetooth (BLUETOOTH)启用BLUETOOTH;再在同一菜单启用Bluetooth service (BSP_BLE_SIBLES)、Enable bt peripherals device (PERI_USING_BT)与其子项Enable BT finsh (BT_FINSH)。BSP_BLE_SIBLES在 LCPU 上仅支持 55x;BT_FINSH依赖BSP_BLE_SIBLES且不支持 55x。按
menuconfig (Top) → SiFli SDK configuration → RTOS → RT Thread → RT-Thread Components → Network → LwIP: light weight TCP/IP stack (RT_USING_LWIP)启用RT_USING_LWIP。该项默认关闭,启用后自动选择RT_USING_DEVICE与RT_USING_DEVICE_IPC,并在启用 SAL 时选择SAL_USING_LWIP。在
menuconfig (Top) → SiFli SDK configuration → SiFli Built-in Components → Bluetooth → Classic BT service → Enable BT pan finsh (BT_FINSH_PAN)启用 PAN。BT_FINSH_PAN依赖BT_FINSH与RT_USING_LWIP,默认关闭,因此任一前置项未启用时不会显示。如果使用手动经典 BT profile,在
menuconfig (Top) → Bluetooth config → Enable bluetooth → Manually select profiles (BT_PROFILE_CUSTOMIZE) → Enable PAN (CFG_PAN)启用CFG_PAN。CFG_PAN依赖BT_PROFILE_CUSTOMIZE,默认关闭,并会自动选择BT_FINSH_PAN;仍须满足前述BT_FINSH与RT_USING_LWIP依赖。
PAN 配置完成后,上层业务代码不应关心底层网络承载是蓝牙、Wi-Fi 还是 4G,而应统一等待网络 ready,然后使用 HTTP/HTTPS、socket、WebSocket 等协议访问服务器。
5. 通用网络状态判断¶
业务代码不要在收到 Wi-Fi、LTE 或 BT PAN 连接事件后立即访问服务器,而应统一等待 net_is_connected()。推荐和 solution/examples/_app_demo/application/connectivity/demo_connectivity.c 中的使用方式保持一致。
#include <rtthread.h>
#if defined(APP_NETWORK_USED)
#include "app_net.h"
#endif
static int demo_wait_network_ready(uint32_t timeout_ms)
{
#if defined(APP_NETWORK_USED)
uint32_t elapsed = 0;
while (elapsed < timeout_ms)
{
if (net_is_connected())
{
return 0;
}
rt_thread_mdelay(200);
elapsed += 200;
}
return -1;
#else
rt_kprintf("APP_NETWORK_USED is not enabled\n");
return -1;
#endif
}
6. 不同承载的联网流程¶
不同承载的差异只存在于“如何入网”。业务代码在访问服务器前统一等待 net_is_connected()。示例工程可参考 solution/examples/_app_demo/application/connectivity/demo_connectivity.c 中的 conn_network_ready()、wifi_join_event_cb()、lte_connect_event_cb() 和 ping_event_cb()。
6.1 BT PAN¶
BT PAN 由 profile 负责连接和建立 IP 网络通道,上层只等待 net_is_connected()。conn_network_ready() 和 ping_event_cb() 对 BT PAN 同样适用:
int demo_bt_pan_wait_ready(void)
{
if (demo_wait_network_ready(15000) != 0)
{
rt_kprintf("BT PAN connect timeout\n");
return -1;
}
rt_kprintf("BT PAN connected\n");
return 0;
}
6.2 Wi-Fi¶
连接 AP 时,先发起 Wi-Fi 连接,再等待网络 ready:
#include <rtthread.h>
#if defined(APP_NETWORK_USED)
#include "app_net.h"
#endif
#if defined(APP_WIFI_USED)
#include "app_wifi.h"
#endif
#define DEMO_WIFI_SSID "your_ssid"
#define DEMO_WIFI_PASSWORD "your_password"
int demo_wifi_connect(void)
{
#if defined(APP_WIFI_USED)
app_wifi_open();
app_wifi_sta_open();
app_wifi_connect(DEMO_WIFI_SSID, DEMO_WIFI_PASSWORD, NULL);
if (demo_wait_network_ready(15000) != 0)
{
rt_kprintf("Wi-Fi connect timeout\n");
return -1;
}
rt_kprintf("Wi-Fi connected\n");
return 0;
#else
rt_kprintf("APP_WIFI_USED is not enabled\n");
return -1;
#endif
}
如果只需要扫描 AP,可以使用:
void demo_wifi_scan(void)
{
#if defined(APP_WIFI_USED)
app_wifi_open();
app_wifi_sta_open();
app_wifi_scan();
#else
rt_kprintf("APP_WIFI_USED is not enabled\n");
#endif
}
6.3 4G/LTE¶
4G/LTE 入网后同样等待网络 ready:
#include <rtthread.h>
#if defined(APP_NETWORK_USED)
#include "app_net.h"
#endif
int demo_lte_connect(void)
{
#if defined(APP_NETWORK_USED)
app_net_init();
send_msg_to_net_thread(NET_OP_LTE_BEGIN, NULL, 0);
if (demo_wait_network_ready(30000) != 0)
{
rt_kprintf("LTE connect timeout\n");
return -1;
}
rt_kprintf("LTE connected\n");
return 0;
#else
rt_kprintf("APP_NETWORK_USED is not enabled\n");
return -1;
#endif
}
7. 网络连通性和访问测试¶
7.1 Ping 连通性测试¶
如果工程打开了 ping 支持,可以在网络 ready 后测试基础网络连通性。调用方式和 connectivity demo 一致。
#include <rtthread.h>
#if defined(APP_NETWORK_USED)
#include "app_net.h"
#endif
#if defined(RT_LWIP_USING_PING) && (defined(PKG_NETUTILS_PING) || !defined(RT_USING_NETDEV))
extern rt_err_t ping(char *target_name, rt_uint32_t times, rt_size_t size);
#endif
void demo_ping(void)
{
#if defined(APP_NETWORK_USED)
app_net_init();
if (!net_is_connected())
{
rt_kprintf("network is not connected\n");
return;
}
#if defined(RT_LWIP_USING_PING) && (defined(PKG_NETUTILS_PING) || !defined(RT_USING_NETDEV))
ping("8.8.8.8", 1, 0);
#else
rt_kprintf("RT_LWIP_USING_PING is not enabled\n");
#endif
#endif
}
7.2 HTTP 下载访问测试¶
访问测试建议直接下载一个已知大小的 HTTP 文件,循环 webclient_read() 并统计总字节数和耗时。下载前仍然要确认 net_is_connected()。Solution 框架中的网络临时缓存建议使用 app_cache_alloc() / app_cache_free()。
#include <rtthread.h>
#include <webclient.h>
#include "app_mem.h"
#if defined(APP_NETWORK_USED)
#include "app_net.h"
#endif
#define DEMO_SPEED_HEADER_SIZE 1024
#define DEMO_SPEED_READ_SIZE 4096
int demo_http_speed_test(const char *uri)
{
#if defined(APP_NETWORK_USED)
struct webclient_session *session = RT_NULL;
uint8_t *read_buf = RT_NULL;
rt_tick_t start_tick;
rt_tick_t elapsed_tick;
rt_uint32_t total_bytes = 0;
rt_uint32_t kb_s;
int resp_status;
int bytes_read;
int ret = -1;
if (!net_is_connected())
{
rt_kprintf("network is not connected\n");
return -1;
}
session = webclient_session_create(DEMO_SPEED_HEADER_SIZE);
if (!session)
{
goto exit;
}
read_buf = app_cache_alloc(DEMO_SPEED_READ_SIZE, CACHE_PSRAM);
if (!read_buf)
{
goto exit;
}
resp_status = webclient_get(session, uri);
if (resp_status != 200)
{
rt_kprintf("webclient_get failed, status=%d\n", resp_status);
goto exit;
}
start_tick = rt_tick_get();
while (1)
{
bytes_read = webclient_read(session, read_buf, DEMO_SPEED_READ_SIZE);
if (bytes_read < 0)
{
goto exit;
}
if (bytes_read == 0)
{
break;
}
total_bytes += bytes_read;
}
elapsed_tick = rt_tick_get() - start_tick;
if (elapsed_tick == 0)
{
elapsed_tick = 1;
}
kb_s = (rt_uint32_t)(((rt_uint64_t)total_bytes * RT_TICK_PER_SECOND) / elapsed_tick / 1024);
rt_kprintf("download %u bytes, speed %u KB/s, %u.%u Mbps\n",
total_bytes, kb_s, kb_s * 8 / 1024, (kb_s * 8 % 1024) * 10 / 1024);
ret = 0;
exit:
if (read_buf)
{
app_cache_free(read_buf);
}
if (session)
{
webclient_close(session);
}
return ret;
#else
rt_kprintf("APP_NETWORK_USED is not enabled\n");
return -1;
#endif
}
使用时传入服务器上的测试文件地址:
demo_http_speed_test("http://your_server/test_10m.bin");
connectivity demo 中的 speed 页面偏 UI 展示参考,实际项目中建议以上层 HTTP 下载统计为准。
8. HTTP 下载文件到内存¶
HTTP/HTTPS 可使用 WebClient 访问。WebClient 的接口文档见 RT-Thread WebClient 说明。下载到内存适合 JSON、小图片、短音频等小文件。Solution 框架中避免把下载结果放到普通栈或长时间占用 system heap,建议使用 app_cache_alloc() / app_cache_realloc() / app_cache_free() 管理数据,减少通用 system heap 压力。
#include <rtthread.h>
#include <webclient.h>
#include <string.h>
#include "app_mem.h"
#define DEMO_HTTP_HEADER_SIZE 1024
#define DEMO_HTTP_READ_SIZE 1024
typedef struct
{
uint8_t *data;
uint32_t size;
uint32_t capacity;
} demo_mem_file_t;
static int demo_mem_file_append(demo_mem_file_t *file, const uint8_t *data, uint32_t len)
{
uint8_t *new_data;
uint32_t new_capacity;
if (!file || !data || len == 0)
{
return -1;
}
if (file->size + len > file->capacity)
{
new_capacity = file->capacity ? file->capacity * 2 : 4096;
while (new_capacity < file->size + len)
{
new_capacity *= 2;
}
if (file->data)
{
new_data = app_cache_realloc(file->data, new_capacity);
}
else
{
new_data = app_cache_alloc(new_capacity, CACHE_PSRAM);
}
if (!new_data)
{
return -1;
}
file->data = new_data;
file->capacity = new_capacity;
}
memcpy(file->data + file->size, data, len);
file->size += len;
return 0;
}
int demo_http_get_to_memory(const char *uri, demo_mem_file_t *out_file)
{
struct webclient_session *session = RT_NULL;
uint8_t *read_buf = RT_NULL;
int resp_status;
int bytes_read;
int ret = -1;
if (!uri || !out_file)
{
return -1;
}
memset(out_file, 0, sizeof(*out_file));
session = webclient_session_create(DEMO_HTTP_HEADER_SIZE);
if (!session)
{
goto exit;
}
resp_status = webclient_get(session, uri);
if (resp_status != 200)
{
rt_kprintf("webclient_get failed, status=%d\n", resp_status);
goto exit;
}
read_buf = app_cache_alloc(DEMO_HTTP_READ_SIZE, CACHE_PSRAM);
if (!read_buf)
{
goto exit;
}
while (1)
{
bytes_read = webclient_read(session, read_buf, DEMO_HTTP_READ_SIZE);
if (bytes_read < 0)
{
goto exit;
}
if (bytes_read == 0)
{
break;
}
if (demo_mem_file_append(out_file, read_buf, bytes_read) != 0)
{
goto exit;
}
}
ret = 0;
exit:
if (ret != 0 && out_file->data)
{
app_cache_free(out_file->data);
memset(out_file, 0, sizeof(*out_file));
}
if (read_buf)
{
app_cache_free(read_buf);
}
if (session)
{
webclient_close(session);
}
return ret;
}
使用示例:
demo_mem_file_t file;
if (demo_http_get_to_memory("http://example.com/test.bin", &file) == 0)
{
rt_kprintf("download ok, size=%u\n", file.size);
/* file.data / file.size 交给业务处理 */
app_cache_free(file.data);
}
9. HTTP 下载文件到文件系统¶
WebClient 提供 webclient_get_file(),适合大文件、资源包、音频文件等下载到文件系统的场景。使用前应确认文件系统已挂载、路径可写且空间足够。
#include <rtthread.h>
#include <webclient.h>
int demo_http_get_to_file(const char *uri, const char *path)
{
int ret;
if (!uri || !path)
{
return -1;
}
ret = webclient_get_file(uri, path);
if (ret != 0)
{
rt_kprintf("download file failed, ret=%d\n", ret);
return -1;
}
rt_kprintf("download file ok: %s\n", path);
return 0;
}
如果业务层需要进度、断点续传或自定义校验,可以参考 webclient_read() 循环读取后自行写入文件。
10. WebSocket/MQTT 和大模型访问¶
访问大模型通常有两类方式:
使用现有的小智 AI 组件;
自行使用 HTTPS/WebSocket/MQTT 对接第三方或私有化大模型服务。
10.1 使用小智 AI 组件¶
小智 AI 组件位于:
solution/components/xiaozhi/
组件内已经封装网络连接、WebSocket/MQTT 通信、TTS/STT/LLM 事件处理以及 Opus 音频链路。使用前需要确认:
XIAOZHI_SUPPORT
APP_NETWORK_USED
WebSocket / MQTT 相关组件
小智组件中的服务器参数示例:
#define XIAOZHI_HOST "api.tenclass.net"
#define XIAOZHI_WSPATH "/xiaozhi/v1/"
#define XIAOZHI_TOKEN "Bearer 12345678"
音频参数示例:
{
"format": "opus",
"sample_rate": 16000,
"channels": 1,
"frame_duration": 60
}
10.2 自定义访问大模型 HTTP API¶
如果客户使用 OpenAI、DeepSeek、通义、豆包或私有化模型,推荐流程如下:
业务线程等待 net_is_connected()
↓
创建 HTTPS session
↓
设置 Authorization 和 Content-Type 头
↓
POST JSON 请求
↓
读取 JSON 或流式响应
↓
文本交给 UI,音频交给音频播放链路
WebClient 的 POST 使用方式可参考 WebClient 示例;动态应用中的 AI App 也可作为接入火山 Chat Completions 和火山同传翻译 WebSocket 的参考。服务器返回流式音频时,播放方式请参考 网络音频播放。
11. HTTPS 证书管理¶
动态证书回退机制的完整配置路径为:menuconfig (Top) → SiFli SDK configuration → Third party packages → Seleted MBedTLS (PKG_USING_MBEDTLS) → Enable dynamic cert fallback for mbedtls_client_connect() (PKG_USING_MBEDTLS_TLS_CLIENT_CONNECT_FALLBACK_ALIAS)。先启用 PKG_USING_MBEDTLS,动态证书回退选项才会显示;启用后,mbedtls_client_connect() 可使用动态证书回退逻辑。