一、环境搭建
按照站内其他大佬的环境搭建一步步走即可,目前工程所用到的环境有
iCEasy官方资料下载区的SDK,以及兆易官网下的GD32EmbeddedBuilder IDE


本篇内容参考了 作者@我爱小易大佬的点灯项目MAX版,不过修改WiFi模式为STA模式(即板子连接手机或电脑热点)
本项目参考了官方的GD32VW55x_RELEASE_V1.0.3\MSDK\examples\wifi\softap_tcp_server工程和GD32VW55x_RELEASE_V1.0.3\MSDK\examples\combo\blufi工程,在softap_tcp_server工程的基础上进行修改,增加所需的库
二、项目主要功能讲解
整体架构如下

2.1 蓝牙BLE功能
主要功能:充当 WiFi 账号密码的“无线投递通道”,实现无屏设备的便捷配网。
系统上电后,ble_prov_init() 完成 BLE 协议栈初始化,注册一个自定义 GATT Service(UUID: 0xFFF0),该服务包含三个特征值:
- 0xFFF1(Write):手机端通过该通道写入 ssid=xxx、pass=xxx、apply 等命令。
- 0xFFF2(Notify):设备通过该通道主动推送配网状态(如 connecting、connected:192.168.1.100)。
- 0xFFF3(Read):只读设备信息(MAC 地址、固件版本)。
手机连接板子蓝牙后,通过向 0xFFF1 写入 WiFi 凭证,触发 ble_prov_write_handler() 解析命令。收到 apply 指令后,不直接在 BLE 任务中执行 WiFi 连接(避免阻塞蓝牙协议栈),而是通过消息队列向 wifi_sta_task 投递连接请求,实现任务间解耦。(任务之间使用信号量进行通信,使用FreeRTOS后对任务的管理可以直接使用对应的函数接口,不用手搓状态机)
低功耗部分:
ble_prov.c 中定义宏 BLE_PROV_TIMEOUT_MS 为 120 秒。若在该时间内未收到 apply 指令,ble_prov_timeout_handler() 被触发,调用 SDK 提供的 ble_stack_disable() 关闭蓝牙广播,使设备进入低功耗静默状态,避免 BLE 持续广播带来的额外功耗。
唤醒机制:
物理按键检测任务 board_key_task 捕获到 三击(1.5 秒内三次单击)事件后,调用 ble_prov_start() 重新开启蓝牙广播,并将超时倒计时重置回 120 秒,实现无需断电、无需重新烧录即可再次进入配网模式。
2.2 WiFi STA 管理与重连机制
核心作用:负责 WiFi 联网、断线自愈以及凭证持久化,实现“永不掉线”的稳定连接。
系统启动后,wifi_sta_app_init() 创建 wifi_sta_task 任务,该任务主循环阻塞在消息队列 wifi_sta_queue 上,等待外部事件驱动。
联网流程:
- 上电后首先调用 app_storage_load() 读取 NVDS Flash 中保存的 WiFi 凭证。
- 若存在有效配置(valid 标记为 0xA5),调用 wifi_sta_connect(ssid, password) 连接路由器。
- 若无有效配置,任务进入 IDLE 状态,等待 BLE 配网模块投递凭证。
断线重连机制:
LwIP 协议栈或 WiFi 驱动检测到断线后,触发 wifi_sta_event_callback() 回调,向 wifi_sta_queue 投递 WIFI_MSG_DISCONNECTED 消息。wifi_sta_task 收到该消息后调用 wifi_sta_reconnect(),间隔 5 秒(宏 WIFI_RECONNECT_INTERVAL_MS)重试一次,直至连接恢复。重试次数及当前状态记录在 wifi_sta_ctx 结构体中,可通过 Web 接口查询。
时间回绕安全处理:
time_reached() 函数通过将 uint32_t 差值强制转换为 int32_t 进行比较,解决了 FreeRTOS 系统时钟运行约 49 天后溢出的问题,确保长时间运行的设备不会因为时钟回绕导致重连逻辑失效。(AI给的,我也不知道说的啥>_<)
2.3 Web 控制台界面(让AI自己发挥就好)
核心作用:无需安装 App,手机/电脑通过浏览器即可查看设备状态并远程控制,充当无屏设备的“可视化调试面板”。
WiFi 连接成功后,web_server_init() 创建 web_server_task 任务,启动一个极简 HTTP 服务器。该服务器未使用第三方库,而是手写轻量级解析器,通过 http_server_parse_request() 解析请求行,实现两种核心路由:
| 路由 | 处理函数 | 功能 |
|---|---|---|
GET / | web_server_send_html() | 返回内嵌的 HTML/CSS/JS 页面(单页应用) |
GET /api/status | web_server_get_status_json() | 返回包含 10 项运行指标的 JSON 数据 |
POST /api/control | web_server_handle_control() | 执行重启、清除配网、切换 LED 等远程操作 |
前端交互:
页面 JavaScript 通过 setInterval(fetchStatus, 2000) 每 2 秒调用 /api/status 接口刷新数据,无需手动刷新浏览器。虽然相较 WebSocket 有一定延迟,但对于调试和现场监控场景已足够,且零额外依赖、内存占用极低。
监控指标涵盖:
- 网络层:SSID、IP 地址、RSSI 信号强度
- 配网层:BLE 剩余时间(秒)
- 系统层:运行时间、空闲堆内存、历史最小堆内存
- 统计层:重连次数、断线次数、配网成功/失败计数、按键触发次数
实时状态监控信息:
| 标签 | 显示内容 | 说明 |
|---|---|---|
| Wi-Fi | connected / LED off | WiFi连接状态 + LED状态 |
| SSID | LongGuXT | 当前连接的WiFi名称 |
| IP address | 192.168.137.15 | 设备分配的IP地址 |
| RSSI | -36 dBm | WiFi信号强度(仅连接时显示) |
| BLE provisioning | 87 s remaining | 蓝牙配网剩余时间 / sleep状态 |
| Uptime | 53 s | 设备运行时间(秒) |
| Free heap | 143872 B (min 141008 B) | 当前空闲堆内存及历史最小值 |
| Reconnect / disconnect | 0 / 0 | WiFi重连次数/断开次数 |
| Provision success / timeout | 0 / 0 | 配网成功次数/超时次数 |
| KEY2 press / triple | 0 / 0 | 按键按下次数/三击次数 |
控制按钮:
- Toggle LED (蓝色)功能:切换LED灯开关状态API:GET /api/control?action=toggle
- Reconnect Wi-Fi (绿色)功能:重新连接WiFi网络API:GET /api/wifi/reconnect
- Start BLE provisioning (绿色)功能:启动蓝牙配网模式API:GET /api/provision/start
- Clear Wi-Fi configuration (红色警告按钮)功能:清除已保存的WiFi配置并启动蓝牙配网会弹出确认对话框API:GET /api/wifi/clear
2.4 按键交互
核心作用:无需手机、无需串口即可完成设备重置与重新配网。
board_key_init() 初始化 GPIO 引脚中断,board_key_task 任务负责按键扫描与动作识别。去抖动算法采用非阻塞时间戳法,通过记录 xTaskGetTickCount() 获取按下/释放时刻,避免使用 vTaskDelay() 阻塞任务调度。
动作判定逻辑(board_key_process()):
| 动作 | 判定条件 | 触发函数 | 功能描述 |
|---|---|---|---|
| 单击 | 40ms < 按下时长 < 600ms | app_led_toggle() | 翻转板载 LED 状态 |
| 三击 | 1.5 秒内连续三次单击 | ble_prov_start() + app_storage_clear() | 清除 WiFi 配置并开启 BLE 配网 |
| 长按 | 按下时长 > 1500ms | 当前未启用(可扩展) | 预留扩展接口 |
三击唤醒的实现链路:
当 board_key_task 检测到三击事件后,依次调用:
- app_storage_clear():删除 NVDS 命名空间 app_wifi 下的 valid、ssid、password 键值对,擦除已有配置。
- ble_prov_start():重新开启 BLE 广播,重置 120 秒超时,使设备重新进入可配网状态。
2.5 数据持久化存储(NVDS)
核心作用:在 Flash 中保存 WiFi 凭证,实现掉电不丢配置;同时通过“原子写入”机制,防止写入过程中断电导致数据损坏或系统变砖。
NVDS(Non-Volatile Data Storage)是 SDK 提供的基于 Flash 的键值对存储接口,通过命名空间隔离不同模块的数据。app_storage.c 封装了 WiFi 凭证的读写操作,命名空间固定为 "app_wifi",包含三个键:
| 键名 | 数据类型 | 说明 |
|---|---|---|
ssid | 字符串 | WiFi 名称(不超过 32 字节) |
password | 字符串 | WiFi 密码(不超过 64 字节) |
valid | 单字节 | 有效标记,固定值 0xA5 |
原子写入(app_storage_save()):
为确保写入过程的原子性,代码严格按照以下顺序执行 Flash 操作:
- nvds_data_del("app_wifi", "valid"):先删除有效标记,避免写入中途断电后系统误读不完整数据。
- nvds_data_put("app_wifi", "ssid", ...):写入 SSID。
- nvds_data_put("app_wifi", "password", ...):写入密码。
- nvds_data_put("app_wifi", "valid", &valid, 1):最后写入有效标记 0xA5,表示整组数据已完整写入。
安全读取(app_storage_load()):
系统上电时,先读取 valid 键值:
- 若为 0xA5:说明数据完整,继续读取 ssid 和 password 并返回成功。
- 若不是 0xA5:说明上次写入被中断或从未写入,跳过 SSID/Password 读取,返回 APP_RET_NO_CONFIG,上层收到该返回值后自动进入 BLE 配网模式。
三、工程编译、烧录、实现效果
在GD32EmbeddedBuilder中打开ble_tcp_test工程,点击编译

得到编译后的bin固件后,打开GD32AllInOneProgrammer烧录工具,选择板子对应的端口,点击烧录(我这里配置波特率为256000是感觉可能会快一点,并且烧录也没出错,问一下大佬们这个有影响吗?)

烧录成功后,复位板子(烧录,运行程序板子上的boot1开关状态是不一样的,烧录是ON(上),运行程序是下,每次切换状态都需要复位板子)
串口工具打印对应的日志

这里我已经配置过板子,所以自动连上了我的热点
正常流程是,板子上电后,连接板子蓝牙(我使用的是nRF Connect蓝牙调试工具,后续可能会让AI写个APP)
选择UUID为0xFFF0的GATT服务,选择0xFFF1,依次写入ssid=你的WiFi名称、pass=你的WiFi密码、apply命令


板子收到命令后执行对应函数,写入配置,并重启STA

打开日志显示的连接,可以得到下面的Web界面

点击Toggle LED按钮就可以控制LED了。
感谢观看,后续有空会再更新这块板子的学习经历。
工程链接:通过网盘分享的文件:ble_tcp_test.zip 链接: https://pan.baidu.com/s/1cSFH4hGyTJrU3ne5Psq2DA?pwd=wjkh 提取码: wjkh
--来自百度网盘超级会员v5的分享
下载后,解压放在你的GD32VW55x_RELEASE_V1.0.3\MSDK\examples\wifi目录下,然后使用GD32EmbeddedBuilder打开编译。

开源社区 


