任务介绍
本项目实现了Funpack4-3活动板卡一的任务2,基于MAX32655FTHR开发板的蓝牙低功耗数据客户端应用,通过BLE的GATT服务连接并读取小米温湿度计2的数据,并将温度、湿度等信息实时显示在ST7735S彩色LCD屏幕上,同时支持按钮控制和数据交互功能。
硬件平台
本次使用Analog Devices MAX32655FTHR开发板,是一款面向物联网应用的高性能蓝牙低功耗开发平台。该开发板搭载了基于ARM Cortex-M4内核的MAX32655微控制器,具备丰富的外设资源,包括多个I2C接口、SPI接口、GPIO端口等,非常适合用于蓝牙低功耗应用的开发。开发板集成了蓝牙射频模块、调试接口、用户可编程按键与LED,便于快速搭建无线传感器网络系统。
在软件方面,本项目采用WSF(Wireless Software Framework)蓝牙协议栈作为底层运行环境。WSF以其模块化设计、高可靠性和良好的跨平台支持而著称,适用于资源受限的嵌入式设备。通过将蓝牙协议栈移植至MAX32655平台,实现了BLE客户端连接、GATT服务发现以及数据传输等功能。
主控设备:Analog Devices MAX32655FTHR开发板
- 搭载MAX32655 MCU(ARM Cortex-M4F)
- 集成蓝牙5.0低功耗射频模块
- 板载2个用户可编程LED指示灯
- 支持多连接蓝牙客户端应用

米家温湿度计2
ST7735S彩色OLED显示屏

任务分析与实现
本系统实现了基于WSF蓝牙协议栈的温湿度监测平台,主要功能包括:
- 传感器数据采集
- 小米温湿度计2数据读取:实时监测
- 支持温度、湿度、电压等多参数获取
- LCD显示控制
- 显示刷新率:实时更新
- 彩色图形界面渲染
方案框图:
本任务实现的核心是在 MAX32655FTHR 开发板上完成 WSF 蓝牙协议栈的成功调用,并在此基础上实现了对温湿度传感器数据的读取与彩色LCD屏幕的显示控制。接下来详细描述关键流程和核心要点。
一、使用前的准备
1. 硬件环境
- 主控芯片:MAX32655(ARM Cortex-M4F 内核)
- 开发板:MAX32655FTHR
- 外设资源:
- SPI 接口用于驱动 ST7735S 彩色显示屏
- GPIO 控制用户按键输入
- UART 串口调试输出
2. 软件环境
- 开发环境:VS Code + CodeFusion Studio 插件
- 协议栈:WSF Bluetooth Stack
- 编译工具链:GCC ARM Embedded (arm-none-eabi-gcc)
二、核心步骤
1. 应用程序框架创建
应用程序框架主要包括事件处理、连接管理、服务发现以及数据解析等部分。
a. 事件处理机制 (datc_main.c)
- 定义蓝牙事件回调函数
- 处理连接建立、断开事件
- 实现服务发现与数据接收逻辑
static void datcDmCback(dmEvt_t *pDmEvt)
{
dmEvt_t *pMsg;
uint16_t len;
len = DmSizeOfEvt(pDmEvt);
if ((pMsg = WsfMsgAlloc(len + pDmEvt->scanReport.len)) != NULL) {
memcpy(pMsg, pDmEvt, len);
if (pDmEvt->hdr.event == DM_SCAN_REPORT_IND) {
pMsg->scanReport.pData = (uint8_t *)((uint8_t *)pMsg + len);
memcpy(pMsg->scanReport.pData, pDmEvt->scanReport.pData, pDmEvt->scanReport.len);
}
WsfMsgSend(datcCb.handlerId, pMsg);
}
}
b. 数据接收处理 (datc_main.c)
- 解析小米温湿度计数据包
- 提取温度、湿度、电压信息
- 调用显示更新函数
static void datcValueNtf(attEvt_t *pMsg)
{
if (pMsg->handle == pSecDatHdlList[pMsg->hdr.param - 1][SEC_DAT_HDL_IDX]) {
if (pMsg->valueLen >= 5) {
int16_t temp = (int16_t)(pMsg->pValue[0] | (pMsg->pValue[1] << 8));
uint16_t humidity = pMsg->pValue[2];
uint16_t voltage = (pMsg->pValue[3] | (pMsg->pValue[4] << 8));
float battery = ((float)(voltage - 2000) / (float)(3261 - 2000)) * 100.0f;
extern void update_display_values(int16_t temp, uint16_t humidity, uint16_t voltage, float battery);
update_display_values(temp, humidity, voltage, battery);
}
}
}
2. 蓝牙服务发现 (sdsc_main.c)
完成GATT服务和特征值发现:
void SecDatSvcDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_128_UUID_LEN, (uint8_t *)SecDatSvcUuid, SEC_HDL_LIST_LEN,
(attcDiscChar_t **)secDatDiscCharList, pHdlList);
}
3. 设备驱动适配
a. SPI驱动 (main.c)
- 使用SPI接口实现显示屏通信
- 配置SPI时序参数以匹配ST7735S要求
static int spi_tx(uint8_t *data, unsigned int len)
{
mxc_spi_req_t req;
req.spi = MXC_SPI1;
req.txData = data;
req.rxData = NULL;
req.txLen = len;
req.rxLen = 0;
req.ssIdx = 0;
req.ssDeassert = 1;
req.txCnt = 0;
req.rxCnt = 0;
req.completeCB = NULL;
return MXC_SPI_MasterTransaction(&req);
}
b. 显示驱动 (st7735s.c)
- 封装显示控制器初始化序列
- 实现像素绘制和区域刷新功能
- 集成LVGL图形库支持
4. 图形界面集成
使用LVGL图形库提供用户界面:
static void disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
int32_t x, x1, x2;
int32_t y, y1, y2;
uint8_t *lineptr;
unsigned int len;
x1 = area->x1;
x2 = area->x2;
y1 = area->y1;
y2 = area->y2;
for (y = y1; y <= y2; y++) {
st7735s_xyloc(y, x1);
len = 0;
lineptr = linebuf;
for (x = x1; x <= x2; x++) {
*lineptr++ = color_p->ch.blue << 3;
*lineptr++ = color_p->ch.green << 2;
*lineptr++ = color_p->ch.red << 3;
len += 3;
color_p++;
}
st7735s_write_pixels(linebuf, len);
}
lv_disp_flush_ready(disp_drv);
}
代码详解
整体软件流程图:
一、硬件初始化与传感器配置
系统启动后首先完成外设初始化序列,关键流程如下:
- 配置GPIO引脚为输入/输出模式,确保按键与LED正常工作
- 初始化SPI接口,设置合适速率,用于驱动ST7735S显示屏
- 启动蓝牙协议栈并开始扫描周边设备
int main(void)
{
// 初始化LVGL和显示
MX_DISPLAY_Init();
// 创建简单UI界面
lv_obj_t *screen = lv_scr_act();
lv_obj_set_style_bg_color(screen, lv_color_black(), 0);
// 温湿度标签(四行显示)
temp_hum_label = lv_label_create(screen);
lv_obj_set_style_text_font(temp_hum_label, &lv_font_unscii_8, 0);
lv_obj_set_style_text_color(temp_hum_label, lv_color_white(), 0);
lv_label_set_text(temp_hum_label, "Temp:\n--\n\nHumi:\n--");
lv_obj_align(temp_hum_label, LV_ALIGN_CENTER, 0, 0);
// 其他初始化...
mainWsfInit();
StackInitDatc();
DatcStart();
WsfOsEnterMainLoop();
}
二、蓝牙数据处理任务
static void datcValueNtf(attEvt_t *pMsg)
{
if (pMsg->handle == pSecDatHdlList[pMsg->hdr.param - 1][SEC_DAT_HDL_IDX]) {
APP_TRACE_INFO0(">> 来自小米温湿度传感器的通知 <<<");
/* 解析小米温湿度传感器数据 */
if (pMsg->valueLen >= 5) {
int16_t temp = (int16_t)(pMsg->pValue[0] | (pMsg->pValue[1] << 8));
uint16_t humidity = pMsg->pValue[2];
uint16_t voltage = (pMsg->pValue[3] | (pMsg->pValue[4] << 8));
float battery = ((float)(voltage - 2000) / (float)(3261 - 2000)) * 100.0f;
if (battery > 100.0f) battery = 100.0f;
if (battery < 0.0f) battery = 0.0f;
APP_TRACE_INFO3("温度: %d.%02d'C, 湿度: %d%%",
temp/100, abs(temp%100), humidity);
APP_TRACE_INFO2("电压: %d mV, 电量: %.2f%%", voltage, (double)battery);
// 更新显示屏标签内容
extern void update_display_values(int16_t temp, uint16_t humidity, uint16_t voltage, float battery);
update_display_values(temp, humidity, voltage, battery);
}
}
}
效果展示

遇到的问题与解决办法
问题:小米温湿度计2的蓝牙连接不稳定,时常断开。
解法:原因是电量不足,更换新电池后问题消失。
活动感想
通过本项目实践,我第一次接触WSF框架,经过学习最终掌握了WSF蓝牙协议栈在嵌入式平台上的应用方法,学习到了蓝牙低功耗在Cortex-M4架构上的连接管理、服务发现机制及数据传输规范。在这一过程中,MAX32655开发板完善的文档支持极大提升了开发效率。活动中还发现本板卡还支持Zephyr系统,接下来准备用它来进一步学习Zephyr,更加丰富我的技能树。
感谢硬禾科技和得捷电子联合举办的Funpack活动,祝硬禾的活动越办越好!