STM32驱动HC-05蓝牙+DHT11温湿度采集+OLED显示完整实验
前言
本篇基于 STM32F103C8T6 最小系统板,完成DHT11 温湿度采集、OLED 屏幕本地显示、HC‑05 蓝牙双向串口通信综合实验。单片机定时读取环境温湿度,一方面在 OLED 屏幕实时展示,另一方面通过 HC‑05 蓝牙模块把数据上传到手机;手机蓝牙串口 APP 发送指令123,单片机会识别命令并回复指定字符串,同时 OLED 提示收到蓝牙指令。
HC‑05 可以简单理解成一条无线串口线:单片机串口发出去的数据,无线传到手机;手机发送的数据,又通过串口给到 STM32 单片机。
一、实验演示目标
整体业务流程:
- DHT11 传感器采集环境温度、湿度;
- STM32 读取 DHT11 数据,一份数据给到 OLED 屏幕做本地显示;
- 另一份数据通过 USART1 串口交给 HC‑05,无线发送至手机蓝牙串口 APP;
- 手机 APP 发送文本指令
123,经过 HC‑05 转为串口电平给到 STM32; - 单片机串口接收中断捕获字符,识别完整命令后,蓝牙回复
I get it!Sir!,OLED 同步打印接收状态。
二、硬件清单与接线说明
硬件清单
- STM32F103C8T6 最小系统板
- HC‑05 蓝牙串口模块
- DHT11 温湿度传感器模块
- 0.96 寸 I2C OLED 显示屏
- 杜邦线若干
引脚接线对照表
| 模块引脚 | STM32F103C8T6 引脚 | 功能说明 |
|---|---|---|
| HC‑05 VCC | 5V | 蓝牙模块供电 |
| HC‑05 TXD | PA10 | HC‑05 发送,STM32 USART1 接收 |
| HC‑05 RXD | PA9 | HC‑05 接收,STM32 USART1 发送 |
| HC‑05 GND | GND | 共地 |
| DHT11 VCC | 5V | 温湿度传感器供电 |
| DHT11 DATA | PA11 | DHT11 单总线数据引脚 |
| DHT11 GND | GND | 共地 |
| OLED SCL | PB8 | 软件 I2C 时钟 |
| OLED SDA | PB9 | 软件 I2C 数据 |
接线重点注意事项
- TX、RX 必须交叉相连 TX 是发送引脚,RX 是接收引脚。设备 A 的发送必须接设备 B 的接收。
- STM32 PA9(TX) → HC‑05 RXD
- STM32 PA10 (RX) ← HC‑05 TXD 如果接成 TX 接 TX、RX 接 RX,双方完全无法通信。
- 所有模块必须共地 串口识别高低电平依赖 GND 作为参考电位。哪怕供电、TX/RX 接线完全正确,如果不共地,会出现乱码、收不到数据的现象。
三、HC‑05 模块工作原理
HC‑05 是经典的主从一体蓝牙串口模块,工作在透明传输模式。 简单来说模块本身不会解析温湿度、不会识别我们自定义的123指令,它只做信号转换:
- STM32 串口输出的字节电平信号,HC‑05 转为经典蓝牙无线信号发给手机;
- 手机蓝牙发出的数据,HC‑05 转换成 UART 串口电平输出给单片机;
- 数据代表什么含义、什么指令,全部由单片机程序和手机 APP 双方约定。
注意:本实验的
123是应用层自定义指令,不是 HC‑05 模块的 AT 配置命令。HC‑05 的 AT 模式只用来修改模块名字、配对密码、波特率等硬件参数,本实验不进入 AT 模式。
四、USART1 串口通信配置
本实验使用 STM32 的 USART1 外设,串口参数配置为 9600 8N1
| 参数 | 配置值 |
|---|---|
| 外设 | USART1 |
| 发送引脚 | PA9 |
| 接收引脚 | PA10 |
| 波特率 | 9600 |
| 数据位 | 8 位 |
| 校验位 | 无 |
| 停止位 | 1 位 |
| 硬件流控 | 关闭 |
UART 属于异步串口通信,没有额外时钟线,通信双方必须约定一模一样的波特率。 一帧完整字节包含:1 位起始位 + 8 位数据位 +1 位停止位,合计 10bit。波特率 9600 情况下,传输 1 字节大约 1.04ms。
如果 STM32、HC‑05、手机 APP 三者波特率不统一,直接出现乱码或者完全收不到数据。
五、业务逻辑讲解
5.1 温湿度上报流程
- 主程序每 2 秒钟读取一次 DHT11 传感器;
- 读取校验成功:OLED 打印温度湿度,程序拼接字符串,通过 USART1 发送给 HC‑05 上传手机;
- 读取失败:OLED 打印
Sensor Error,蓝牙向手机上报DHT11 read error;
手机收到正常数据格式示例:
Temp:25.0C,Humi:60.0%
- Temp:温度标识;Humi:湿度标识;
\r\n为换行符,让手机串口助手自动换行。
上报周期设置为 2 秒,降低手机端解析刷新压力;两行报文之间增加 50ms 延时间隔,减少字节流分包问题。
5.2 手机指令接收与应答逻辑
手机 APP 发送连续字符123:
- HC‑05 把蓝牙数据转为串口电平送入 STM32 PA10;
- 每收到 1 个字节触发 USART1 接收中断;
- 中断服务函数把字节存入环形接收缓冲区,同时匹配目标命令;
- 程序检测完整匹配
123,置位命令接收标志位; - 主循环里面执行应答发送,回复字符串
I get it!Sir!,OLED 屏幕更新提示BT RX: 123 OK。
关键点:数据接收放在中断,回复发送放在主循环,不要在中断函数里面做耗时的串口发送操作。哪怕手机发送
123附带回车换行,程序依旧可以识别命令。
5.3 接收中断 + 环形缓冲区作用
如果不使用中断,主循环就要一直等待蓝牙数据,DHT11 采集、OLED 刷新都会卡住。
- 没有蓝牙数据的时候,CPU 正常跑主程序,采集传感器、刷新屏幕;
- 手机发来一个字节,立刻触发中断,快速保存数据之后回到原来程序继续运行。
环形缓冲区大小设置 128 字节,拥有读指针、写指针,写满之后丢弃最早数据,优先保存最新收到字节。中断里面只做最简单的保存、状态标记,复杂业务全部交给主循环处理。
六、实物演示
实验现象
- 上电之后 OLED 屏幕显示标题、实时温度湿度,底部提示
BT TX: every 2s,代表每 2 秒向手机上报温湿度; - 手机蓝牙搜索 HC‑05,配对连接,打开蓝牙串口助手;手机端每隔 2 秒接收一行温湿度字符串;
- APP 文本模式发送
123; - 手机收到回复
I get it!Sir!,同时 OLED 底部刷新显示BT RX: 123 OK; - DHT11 接触不良或者接线错误时,OLED 显示
Sensor Error,手机收到传感器读取错误提示。
演示操作步骤
- 按照接线表完成全部接线,重点确认全部模块 GND 共地;
- 编译下载工程到 STM32 开发板,上电;观察 OLED 屏幕输出;
- 手机打开蓝牙,搜索配对 HC‑05 模块,打开蓝牙串口 APP 建立连接;
- 观察手机周期性收到温湿度数据;
- APP 文本模式发送
123,观察手机回复和 OLED 状态变化; - 可选拓展:修改代码里面预设命令字符串,实现自定义指令控制。

七、源代码
这里包括 HC‑05 驱动以及 main.c 主函数。
核心宏定义参考:
#define HC05_DEFAULT_BAUDRATE 9600u
#define HC05_TX_LINE_GAP_MS 50u
#define HC05_RX_BUFFER_SIZE 128u
#define HC05_APP_COMMAND "123"
关键函数说明:
| 函数 | 功能 |
|---|---|
| HC05_Init | USART1 初始化,波特率 9600 |
| HC05_SendByte | 发送单个字节 |
| HC05_SendString | 发送字符串 |
| HC05_SendLine | 发送字符串附带回车换行 |
| HC05_SendTemperatureHumidity | 打包发送温湿度报文 |
| USART1_IRQHandler | 串口 1 接收中断服务函数 |
main.c代码
// Teaching demo: DHT11 + OLED + HC-05 on STM32F103C8T6.
// HC-05: TX->PA10, RX->PA9; DHT11 DATA->PA11; OLED SCL->PB8, SDA->PB9.
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "dht11.h"
#include "HC_05.h"
/* 每 2 秒采集并发送一次,避免手机 APP 接收过于频繁。 */
#define SENSOR_REFRESH_MS 2000
static void Show_DHT11_Data(u8 humiH,u8 humiL,u8 tempH,u8 tempL,u8 commandReceived);
static void Show_DHT11_Error(void);
int main(void)
{
u8 humiH=0;
u8 humiL=0;
u8 tempH=0;
u8 tempL=0;
u8 commandReceived=0;
delay_init();
delay_ms(1000);
NVIC_Configuration();
HC05_Init();
OLED_Init();
OLED_ColorTurn(0);
OLED_DisplayTurn(0);
DHT11_Init();
HC05_SendLine("STM32 DHT11 + HC-05 ready");
HC05_SendLine("Send 123 to test Bluetooth RX");
while(1)
{
commandReceived=HC05_TakeAppCommandReceived();
if(commandReceived!=0)
{
HC05_SendLine("I get it!Sir!");
}
if(DHT11_Read_Data(&humiH,&humiL,&tempH,&tempL)==0)
{
Show_DHT11_Data(humiH,humiL,tempH,tempL,commandReceived);
HC05_SendTemperatureHumidity(tempH,tempL,humiH,humiL);
}
else
{
Show_DHT11_Error();
HC05_SendLine("DHT11 read error");
DHT11_Init();
}
delay_ms(SENSOR_REFRESH_MS);
}
}
static void Show_DHT11_Data(u8 humiH,u8 humiL,u8 tempH,u8 tempL,u8 commandReceived)
{
OLED_Clear();
OLED_ShowString(16,0,(u8 *)"DHT11 + HC05",16);
OLED_ShowString(0,18,(u8 *)"Temp:",16);
OLED_ShowNum(48,18,tempH,2,16);
OLED_ShowString(64,18,(u8 *)".",16);
OLED_ShowNum(72,18,tempL,1,16);
OLED_ShowString(88,18,(u8 *)"C",16);
OLED_ShowString(0,34,(u8 *)"Humi:",16);
OLED_ShowNum(48,34,humiH,2,16);
OLED_ShowString(64,34,(u8 *)".",16);
OLED_ShowNum(72,34,humiL,1,16);
OLED_ShowString(88,34,(u8 *)"%",16);
if(commandReceived!=0)
{
OLED_ShowString(0,52,(u8 *)"BT RX: 123 OK",12);
}
else
{
OLED_ShowString(0,52,(u8 *)"BT TX: every 2s",12);
}
OLED_Refresh();
}
static void Show_DHT11_Error(void)
{
OLED_Clear();
OLED_ShowString(16,0,(u8 *)"DHT11 + HC05",16);
OLED_ShowString(8,22,(u8 *)"Sensor Error",16);
OLED_ShowString(0,46,(u8 *)"Check PA11/VCC",12);
OLED_Refresh();
}
HC_05.c代码
#include "HC_05.h"
#include "stm32f10x.h"
#include "delay.h"
/*
* 文件说明:HC-05 蓝牙串口模块驱动。
*
* 本驱动使用 USART1 完成蓝牙数据的收发:
* PA9 配置为复用推挽输出,连接 HC-05 的 RXD;
* PA10 配置为浮空输入,连接 HC-05 的 TXD。
*
* 接收数据由 USART1 中断写入环形缓冲区,同时在接收字节流中匹配
* HC05_APP_COMMAND。主循环可读取命令标志,也可逐字节读取原始数据。
*/
/* 环形缓冲区至少需要两个位置,才能区分“空”和“满”两种状态。 */
#if (HC05_RX_BUFFER_SIZE < 2u)
#error "HC05_RX_BUFFER_SIZE must be at least 2"
#endif
/* 需要在手机接收数据中匹配的命令字符串,当前为“123”。 */
static const char hc05_app_command[] = HC05_APP_COMMAND;
/* 串口接收环形缓冲区及其读写位置。 */
static volatile uint8_t hc05_rx_buffer[HC05_RX_BUFFER_SIZE];
static volatile uint16_t hc05_rx_head = 0u;
static volatile uint16_t hc05_rx_tail = 0u;
/* 命令匹配位置及完整命令接收标志。 */
static volatile uint8_t hc05_command_index = 0u;
static volatile uint8_t hc05_command_received = 0u;
/*
* 功能:计算环形缓冲区的下一个位置。
* 参数:index 为当前位置。
* 返回:下一个有效数组下标,到达末尾后自动回到 0。
*/
static uint16_t HC05_NextRxIndex(uint16_t index)
{
index++;
if(index >= HC05_RX_BUFFER_SIZE)
{
index = 0u;
}
return index;
}
/* 清空接收缓冲区位置、命令匹配位置和命令标志。 */
static void HC05_ResetReceiveState(void)
{
hc05_rx_head = 0u;
hc05_rx_tail = 0u;
hc05_command_index = 0u;
hc05_command_received = 0u;
}
/*
* 功能:将中断收到的一个字节保存到环形缓冲区。
* 说明:缓冲区已满时丢弃最早的一个字节,始终保留最新数据。
*/
static void HC05_StoreReceivedByte(uint8_t data)
{
uint16_t next_head;
next_head = HC05_NextRxIndex(hc05_rx_head);
if(next_head == hc05_rx_tail)
{
/* 缓冲区已满,尾指针前移一格,丢弃最早的数据。 */
hc05_rx_tail = HC05_NextRxIndex(hc05_rx_tail);
}
hc05_rx_buffer[hc05_rx_head] = data;
hc05_rx_head = next_head;
}
/* 将 0~255 的无符号整数转换为十进制字符并通过蓝牙发送。 */
static void HC05_SendUInt8Decimal(uint8_t value)
{
uint8_t hundreds;
uint8_t tens;
uint8_t ones;
hundreds = (uint8_t)(value / 100u);
tens = (uint8_t)((value / 10u) % 10u);
ones = (uint8_t)(value % 10u);
if(hundreds != 0u)
{
HC05_SendByte((uint8_t)('0' + hundreds));
}
if((hundreds != 0u) || (tens != 0u))
{
HC05_SendByte((uint8_t)('0' + tens));
}
HC05_SendByte((uint8_t)('0' + ones));
}
/* 使用默认波特率初始化 HC-05 串口。 */
uint8_t HC05_Init(void)
{
return HC05_InitWithBaudRate(HC05_DEFAULT_BAUDRATE);
}
/*
* 功能:使用指定波特率初始化 HC-05 通信接口。
* 配置:USART1、PA9、PA10 和 USART1 接收中断。
*/
uint8_t HC05_InitWithBaudRate(uint32_t baudrate)
{
GPIO_InitTypeDef gpio_init;
USART_InitTypeDef usart_init;
NVIC_InitTypeDef nvic_init;
if(baudrate == 0u)
{
baudrate = HC05_DEFAULT_BAUDRATE;
}
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |
RCC_APB2Periph_AFIO |
RCC_APB2Periph_USART1, ENABLE);
/* PA9:STM32 发送端,连接 HC-05 的 RXD。 */
GPIO_StructInit(&gpio_init);
gpio_init.GPIO_Pin = GPIO_Pin_9;
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
gpio_init.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &gpio_init);
/* PA10:STM32 接收端,连接 HC-05 的 TXD。 */
gpio_init.GPIO_Pin = GPIO_Pin_10;
gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &gpio_init);
/* USART1 配置为指定波特率、8 位数据位、无校验、1 位停止位。 */
USART_DeInit(USART1);
USART_StructInit(&usart_init);
usart_init.USART_BaudRate = baudrate;
usart_init.USART_WordLength = USART_WordLength_8b;
usart_init.USART_StopBits = USART_StopBits_1;
usart_init.USART_Parity = USART_Parity_No;
usart_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart_init.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &usart_init);
HC05_ResetReceiveState();
/* 配置并使能 USART1 中断通道。 */
nvic_init.NVIC_IRQChannel = USART1_IRQn;
nvic_init.NVIC_IRQChannelPreemptionPriority = 3u;
nvic_init.NVIC_IRQChannelSubPriority = 3u;
nvic_init.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_init);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
return HC05_OK;
}
/* 等待发送数据寄存器为空,然后向 HC-05 发送一个字节。 */
void HC05_SendByte(uint8_t data)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
{
}
USART_SendData(USART1, data);
}
/* 连续发送一段二进制数据;空指针不会执行发送操作。 */
void HC05_SendBuffer(const uint8_t *buffer, uint16_t length)
{
uint16_t index;
if(buffer == 0)
{
return;
}
for(index = 0u; index < length; index++)
{
HC05_SendByte(buffer[index]);
}
}
/* 发送以 '\0' 结束的字符串;空指针不会执行发送操作。 */
void HC05_SendString(const char *string)
{
if(string == 0)
{
return;
}
while(*string != '\0')
{
HC05_SendByte((uint8_t)*string);
string++;
}
}
/*
* 发送一行字符串,并在末尾自动追加回车和换行。
* 行内字节连续发送;整行完全离开 USART1 后再执行行间延时,
* 避免多行消息紧挨着到达手机 APP,给接收端留出处理时间。
*/
void HC05_SendLine(const char *string)
{
HC05_SendString(string);
HC05_SendString("\r\n");
HC05_Flush();
if(HC05_TX_LINE_GAP_MS > 0u)
{
delay_ms(HC05_TX_LINE_GAP_MS);
}
}
/*
* 将 DHT11 的温度和湿度组合为一行便于手机查看的文本。
* 数据格式:Temp:温度.C,Humi:湿度.%
*/
void HC05_SendTemperatureHumidity(uint8_t temperature_integer,
uint8_t temperature_decimal,
uint8_t humidity_integer,
uint8_t humidity_decimal)
{
HC05_SendString("Temp:");
HC05_SendUInt8Decimal(temperature_integer);
HC05_SendByte((uint8_t)'.');
HC05_SendUInt8Decimal(temperature_decimal);
HC05_SendString("C,Humi:");
HC05_SendUInt8Decimal(humidity_integer);
HC05_SendByte((uint8_t)'.');
HC05_SendUInt8Decimal(humidity_decimal);
HC05_SendLine("%");
}
/*
* 功能:处理 USART1 接收到的一个字节。
* 步骤:先保存原始字节,再逐字节匹配 HC05_APP_COMMAND。
* 完整收到“123”后置位命令标志,由主循环读取并回复手机。
*/
void HC05_HandleReceivedByte(uint8_t data)
{
HC05_StoreReceivedByte(data);
if(hc05_app_command[0] == '\0')
{
return;
}
if(data == (uint8_t)hc05_app_command[hc05_command_index])
{
/* 当前字节匹配,继续检查命令中的下一个字符。 */
hc05_command_index++;
if(hc05_app_command[hc05_command_index] == '\0')
{
/* 已连续收到完整命令,置位标志并准备匹配下一条命令。 */
hc05_command_received = 1u;
hc05_command_index = 0u;
}
}
else if(data == (uint8_t)hc05_app_command[0])
{
/* 当前字节可作为一条新命令的第一个字符。 */
hc05_command_index = 1u;
}
else
{
hc05_command_index = 0u;
}
}
/*
* 返回环形缓冲区中尚未读取的字节数。
* 读取首尾指针期间暂时关闭接收中断,避免中断同时修改指针。
*/
uint16_t HC05_RxAvailable(void)
{
uint16_t head;
uint16_t tail;
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
head = hc05_rx_head;
tail = hc05_rx_tail;
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
if(head >= tail)
{
return (uint16_t)(head - tail);
}
return (uint16_t)(HC05_RX_BUFFER_SIZE - tail + head);
}
/*
* 从环形缓冲区读取一个字节。
* 返回 1 表示读取成功,返回 0 表示参数无效或当前没有数据。
*/
uint8_t HC05_ReadRxByte(uint8_t *data)
{
if(data == 0)
{
return 0u;
}
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
if(hc05_rx_head == hc05_rx_tail)
{
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
return 0u;
}
*data = hc05_rx_buffer[hc05_rx_tail];
hc05_rx_tail = HC05_NextRxIndex(hc05_rx_tail);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
return 1u;
}
/* 清空接收环形缓冲区,不影响已经识别到的命令标志。 */
void HC05_ClearRxBuffer(void)
{
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
hc05_rx_tail = hc05_rx_head;
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
/*
* 读取并清除命令接收标志。
* 该设计确保一条命令在主循环中只被处理一次。
*/
uint8_t HC05_TakeAppCommandReceived(void)
{
uint8_t received;
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
received = hc05_command_received;
hc05_command_received = 0u;
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
return received;
}
/* 等待发送完成标志置位,确保最后一个字节已离开串口移位寄存器。 */
void HC05_Flush(void)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{
}
}
/* USART1 接收中断服务函数。 */
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
HC05_HandleReceivedByte((uint8_t)USART_ReceiveData(USART1));
}
/* 依次读取状态寄存器和数据寄存器可清除串口溢出错误。 */
if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)
{
(void)USART_ReceiveData(USART1);
}
}
HC_05.h代码
#ifndef __HC_05_H
#define __HC_05_H
#include <stdint.h>
/*
* 文件说明:STM32F103C8T6 的 HC-05 蓝牙串口驱动头文件。
*
* 硬件连接:
* HC-05 RXD <- PA9 (USART1_TX,单片机发送端)
* HC-05 TXD -> PA10(USART1_RX,单片机接收端)
* HC-05 VCC -> 5V
* HC-05 GND -> GND
*
* 默认串口参数:9600 波特率、8 位数据位、无校验、1 位停止位、无硬件流控。
*/
/* 驱动函数执行成功时的返回值。 */
#define HC05_OK 0u
/* HC-05 数据透传模式的默认通信波特率。 */
#define HC05_DEFAULT_BAUDRATE 9600u
/*
* 每发送完一整行数据后的等待时间,单位为毫秒。
* 行内字节保持连续发送,行与行之间留出处理时间,减轻手机 APP 接收压力。
*/
#define HC05_TX_LINE_GAP_MS 50u
/* 串口接收环形缓冲区大小,实际最多保存 127 个尚未读取的字节。 */
#define HC05_RX_BUFFER_SIZE 128u
/* 手机 APP 发送该命令后,主程序回复“I get it!Sir!”。 */
#define HC05_APP_COMMAND "123"
/* 使用默认波特率初始化 PA9、PA10、USART1 和接收中断。 */
uint8_t HC05_Init(void);
/* 使用指定波特率初始化 HC-05;baudrate 为 0 时使用默认波特率。 */
uint8_t HC05_InitWithBaudRate(uint32_t baudrate);
/* 向 HC-05 发送一个字节。 */
void HC05_SendByte(uint8_t data);
/* 向 HC-05 连续发送指定长度的二进制数据。 */
void HC05_SendBuffer(const uint8_t *buffer, uint16_t length);
/* 向 HC-05 发送以 '\0' 结束的字符串。 */
void HC05_SendString(const char *string);
/* 发送字符串,追加“\r\n”,等待整行发送完成,然后执行行间延时。 */
void HC05_SendLine(const char *string);
/*
* 发送 DHT11 温湿度数据。
* 输出示例:Temp:25.0C,Humi:60.0%
*/
void HC05_SendTemperatureHumidity(uint8_t temperature_integer,
uint8_t temperature_decimal,
uint8_t humidity_integer,
uint8_t humidity_decimal);
/* 等待 USART1 中最后一个字节发送完成。 */
void HC05_Flush(void);
/* 返回接收环形缓冲区中尚未读取的字节数。 */
uint16_t HC05_RxAvailable(void);
/* 从接收缓冲区读取一个字节;成功返回 1,无数据或参数无效时返回 0。 */
uint8_t HC05_ReadRxByte(uint8_t *data);
/* 丢弃接收缓冲区中所有尚未读取的数据。 */
void HC05_ClearRxBuffer(void);
/* 处理一个接收字节,由 USART1_IRQHandler 调用。 */
void HC05_HandleReceivedByte(uint8_t data);
/*
* 查询并清除手机命令接收标志。
* 完整收到一次 HC05_APP_COMMAND 后返回 1,否则返回 0。
*/
uint8_t HC05_TakeAppCommandReceived(void);
/* USART1 接收中断服务函数。 */
void USART1_IRQHandler(void);
#endif
八、常见问题与故障排查
1、手机搜索不到 HC‑05 蓝牙
- 确认 HC‑05 供电正常,模块电源指示灯点亮;
- 确认模块没有被别的手机提前连接占用;
- 确认 APP 支持经典蓝牙,不是 BLE 低功耗蓝牙。
2、蓝牙可以连接,但是手机收不到温湿度
- 检查 PA9 到 HC‑05 RXD 接线,是否松动、接反;
- 确认 STM32 和 HC‑05GND 共地;
- HC‑05 模块实际波特率确认是 9600;
- APP 已经成功建立蓝牙串口通道。
3、手机发送 123,没有收到回复
- 检查 HC‑05 TXD 接到 PA10,接线不能接反;
- APP 必须是文本 ASCII 发送模式,不要打开 16 进制发送;
- 工程不要重复定义 USART1 中断函数。
4、手机接收出现乱码
- STM32、HC‑05、手机 APP 三方波特率全部设置 9600;
- APP 使用文本显示,不要开启十六进制显示。
5、手机偶尔收到半行数据
串口蓝牙属于字节流通信,系统底层接收分包是随机的,一行报文可能被拆分成多次给到 APP。
APP 解析逻辑:持续累积接收字符,检测
\r\n换行标记之后,再当做一条完整消息处理,不要把单次回调当成一帧完整数据。本实验使用 2 秒上报周期 + 行间隔延时用来缓解分包现象,但是不能彻底消除字节流分包。
更多推荐
所有评论(0)