Stm32t通讯——蓝牙通讯
蓝牙基础知识点
- 蓝牙:利用低功率无线电,支持设备短距离通信的无线电技术。特点:短距离、低功率。
- 第一代蓝牙:BR(Basic Rate)技术, 传输速率:721.2kbps。 第二代蓝牙:EDR(Enhanced Data Rate)技术,3Mbps; 第三代蓝牙:核心是AMP(Generic Alternate MAC/PHY),这是一种全新的交替射频技术,支持动态地选择正确射频,传输速率高达24Mbps。 第四代蓝牙:主推Low Energy低功耗,BLE(Bluetooth Low Energy)低功耗功能。 第五代蓝牙:开启物联网时代大门,在低功耗模式下具备更快更远的传输能力。
- 蓝牙协议技术:BR 和 LE。分类:经典蓝牙和低功耗蓝牙。
- 蓝牙架构: soc蓝牙单芯片方案,可以作为MCU用,一般用于消费类电子,集成度很高。 soc蓝牙 + MCU方案:外设一个单芯片方案,发送自定义的命令达到想要的功能。 蓝牙host + controller 分开方案:Host和Controller分开,集成更多的蓝牙协议,蓝牙电话、蓝牙音频、蓝牙音乐控制等等。
- 蓝牙协议栈:直接使用。
- 蓝牙芯片架构:
蓝牙的核心系统,由一个Host和一个或多个Controller组成。
-
BT Host:逻辑实体,在HCI(Host Controller Interface)的上层。
-
BT Controller:逻辑实体,在HCI(Host Controller Interface)的下层。
-
-
单模蓝牙芯片:1 Host 结合 1 Controller;双模蓝牙芯片:1 Host 结合 多个 Controller。
-
BLE低功耗蓝牙协议栈框架:蓝牙协议:蓝牙核心协议(Bluetooth Core)和蓝牙应用层协议(Bluetooth Application)
蓝牙协议栈:蓝牙核心协议中除了Radio 物理层。
- PHY物理层:在物理信道上发送和接收信息包,40个射频信道,2402MHz到 2480 MHz。
- LL链路层:控制链路层状态机处于准备、广播、监听/扫描、发起连接、已连接状态中的一种。
- HCI主机控制接口层:向主句和控制器提供一个标准化的接口。
- GAP通用访问配置文件层:代表所有蓝牙设备的通用功能。GAP服务:设备发现、连接模式、安全、身份验证、关联模式、服务发现。
- L2CAP逻辑链路控制及自适应协议层:对主机和协议栈之间交换的数据进行协议复用能力、分段和重组操作。
- SM:安全管理层:SMP层,生成加密密钥和身份密钥。
- ATT属性协议层:定义用户命令及命令操作的数据,比如读取某个数据或者写某个数据。
- GATT通用属性配置文件层:属性服务器和可选的属性客户端的功能。用于发现、读取、写入和指示服务特性和属性的接口。
- ESP32—C3中的蓝牙功能:先烧好固件然后通过AT指令操作蓝牙。
- LL:设备可以划分为主机和从机,从机广播,主机可以发起连接。
- GAP:定义4种特定角色:广播者、观察者、外围设备和中心设备。
- GATT:设备可以分为服务端和客户端。
- BLE地址:
- 公共地址:6字节,3个向IEEE购买,3个公司内部分配:
- 随机地址:静态地址和私有地址,通过最高两位来区分。
- 静态地址:最高两位11,随机部分至少有一个位是0和1 。
- 私有地址:最高两位00/01,随机部分至少有一个位是0和1。
- 广播:从机每经过一个时间间隔发送一次广播数据包,时间间隔:广播间隔,广播动作:广播事件,只有当从机处于广播状态时,主机才能发现该从机。
- 扫描:主机监听从机广播数据包和发送扫描请求的过程,主机通过扫描,获取到从机的广播包以及扫描回应数据包,主机可以对已扫描到的从机设备发起连接请求,从而连接从机设备并通信。
- 通讯:通过GATT的Profile来完成,Profile 可以理解配置、数据格式等。
从机作为GATT的Server端,用来定义和存储Profile。Profile包含一个或者多个Service,每个Service又包含一个或者多个Characteristic,Characteristic是主从通信的最小单元。
主机作为GATT的Client端,用来发现和获取从机的Service和Characteristic,从而与之通信。
- 公共地址:6字节,3个向IEEE购买,3个公司内部分配:
-
蓝牙透传模式:从一端输入数据,原封不动的传输到另一端,不需要对数据惊醒复杂的解析或者处理。
案例:蓝牙通讯—透传模式下收发数据
采用的蓝牙架构:soc + mcu 方案,通过串口向esp32—C3发送AT指令,实现功能。
提前完成ESP32-C3固件烧录,将ESP32作为从机—服务端,开启蓝牙服务,通过手机蓝牙调式助手连接,进行通讯。
代码实现:串口通讯通过Hal库实现。
esp32.h
#ifndef __ESP32_H
#define __ESP32_H
#include "usart.h"
#include <string.h>
// 初始化
void ESP32_Init(void);
// 发送命令
void ESP32_SendCmd(uint8_t *cmd, uint16_t cmdlen);
// 接收响应
void ESP32_ReadResp(uint8_t rBuff[], uint16_t *rDataLen);
#endif
esp32.c
#include "esp32.h"
// 定义接收的缓冲区和数据长度
uint8_t respBuff[1000];
uint16_t respDataLen;
// 初始化
void ESP32_Init(void)
{
// 0. 初始化
MX_USART2_UART_Init();
// 1. 发送重启命令
uint8_t *cmd = "AT+RST=0\r\n";
ESP32_SendCmd(cmd, strlen((char *)cmd));
HAL_Delay(3000);
}
// 发送命令
void ESP32_SendCmd(uint8_t *cmd, uint16_t cmdlen)
{
// 通过串口2直接发送
HAL_UART_Transmit(&huart2, cmd, cmdlen, 1000);
// 每次发送完都接收数据, 循环等待,并判断包含OK
memset(respBuff, 0, 1000);
do
{
ESP32_ReadResp(respBuff, &respDataLen);
} while (strstr((char *)respBuff, "OK") == NULL);
}
// 接收响应
void ESP32_ReadResp(uint8_t rBuff[], uint16_t *rDataLen)
{
HAL_UARTEx_ReceiveToIdle(&huart2, rBuff, 1000, rDataLen, 1000);
}
bluetooth.h
#ifndef __BLUETOOTH_H
#define __BLUETOOTH_H
#include "esp32.h"
// 初始化
void Bluetooth_Init(void);
// 接收数据:读取数据或者连接状态,返回值为0 - 正常数据
uint8_t Bluetooth_ReadDataAndStatus(uint8_t rxBuff[], uint16_t *rxLen);
// 处理连接改变情况的函数,返回值,表示是否有连接改变
uint8_t Bluetooth_HandleConnChange(void);
// 发送数据
void Bluetooth_SendData(uint8_t txBuff[], uint16_t txLen);
#endif
bluetooth.c
#include "bluetooth.h"
// 初始化
void Bluetooth_Init(void)
{
// 1. 初始化 ESP32
ESP32_Init();
// 2. 设置角色 2 - server
printf("设置蓝牙模块角色...\n");
uint8_t *cmd = "AT+BLEINIT=2\r\n";
ESP32_SendCmd(cmd, strlen((char *)cmd));
// 3. 服务端创建服务
printf("服务端创建服务!\n");
cmd = "AT+BLEGATTSSRVCRE\r\n";
ESP32_SendCmd(cmd, strlen((char *)cmd));
// 4. 服务端开启服务
printf("服务端开启服务!\n");
cmd = "AT+BLEGATTSSRVSTART\r\n";
ESP32_SendCmd(cmd, strlen((char *)cmd));
// 5. 服务端设置Bluetooth 设备名称
printf("服务端设置Bluetooth 设备名称!\n");
cmd = "AT+BLENAME=\"BaiLu_BLE\"\r\n";
ESP32_SendCmd(cmd, strlen((char *)cmd));
// 6. 服务端设置广播参数
printf("服务端设置广播参数!\n");
cmd = "AT+BLEADVPARAM=50,50,0,0,7,0,,\r\n";
ESP32_SendCmd(cmd, strlen((char *)cmd));
// 7. 服务端设置广播数据
printf("服务端设置广播数据!\n");
cmd = "AT+BLEADVDATAEX=\"BaiLu_BLE\",\"A666\",\"0102030405\",1\r\n";
ESP32_SendCmd(cmd, strlen((char *)cmd));
// 8. 服务端开始广播
printf("服务端开始广播!\n");
cmd = "AT+BLEADVSTART\r\n";
ESP32_SendCmd(cmd, strlen((char *)cmd));
// 9. 配置 BLE 的透传模式,设置SPP参数
printf("配置 BLE 的透传模式!\n");
// 设置信道号和特征值
cmd = "AT+BLESPPCFG=1,1,7,1,5\r\n";
ESP32_SendCmd(cmd, strlen((char *)cmd));
// 10. 设置透传模式下,连接状态改变打印系统提示信息
printf("设置透传模式下,连接状态改变打印系统提示信息!\n");
cmd = "AT+SYSMSG=4\r\n";
ESP32_SendCmd(cmd, strlen((char *)cmd));
}
// 接收数据:读取数据或者连接状态,返回值为0 - 正常数据, 1 - 空数据,2 - 连接状态改变信息
uint8_t Bluetooth_ReadDataAndStatus(uint8_t rxBuff[], uint16_t *rxLen)
{
// 直接从串口接收数据
HAL_UARTEx_ReceiveToIdle(&huart2, rxBuff, 1000, rxLen, 10000);
// 处理非正常数据的情况
// 1 - 空数据
if (*rxLen == 0)
{
return 1;
}
// 2 - 连接状态改变信息, 先根据函数返回值判断是否有连接改变
if (Bluetooth_HandleConnChange())
{
return 2;
}
// 如果是透传模式下的正常数据,直接返回
return 0;
}
// 引入外部变量,全局接收缓冲区
extern uint8_t rxBuff[1000];
// 处理连接改变情况的函数,返回值表示是否有连接改变
uint8_t Bluetooth_HandleConnChange(void)
{
// 从缓冲区中提取信息,进行判断
// 1. 如果是WiFi连接变化信息,不做处理,直接返回 1
if (strstr((char *)rxBuff, "+STA_CONNECTED") != NULL ||
strstr((char *)rxBuff, "+STA_DISCONNECTED") != NULL ||
strstr((char *)rxBuff, "+DIST_STA_IP") != NULL )
{
printf("WiFi 连接状态发生改变!\n");
return 1;
}
// 2. 如果是BLE连接变化信息,要进一步判断是建立连接还是断开连接
if (strstr((char *)rxBuff, "+BLECONN") != NULL)
{
// 2.1 如果是建立连接,就进入透传(SPP)模式
printf("有BLE客户端连接,即将开启 SPP模式!\n");
uint8_t *cmd = "AT+BLESPP\r\n";
ESP32_SendCmd(cmd, strlen((char *)cmd));
// 额外读取一个字符 (>), 接下来就是真正的数据
uint8_t tmp;
HAL_UART_Receive(&huart2, &tmp, 1, 1000);
return 1;
}
else if (strstr((char *)rxBuff, "+BLEDISCONN") != NULL)
{
// 2.2 如果是断开连接,接退出透传模式
printf("BLE客户端断开连接,即将退出 SPP模式!\n");
// 发送"+++",退出透传模式
HAL_UART_Transmit(&huart2, "+++", 3, 1000);
// 延时2s
HAL_Delay(2000);
return 1;
}
// 其他情况,默认返回0,(认为是数据)
return 0;
}
// 发送数据
void Bluetooth_SendData(uint8_t txBuff[], uint16_t txLen)
{
// 透传模式下,发送,直接串口发送
HAL_UART_Transmit(&huart2, txBuff, txLen, 1000);
}
main.c
/* USER CODE BEGIN 0 */
// 全局接收缓冲区和数据长度
uint8_t rxBuff[1000];
uint16_t rxDataLen;
/* USER CODE END 0 */
// 1. 初始化
Bluetooth_Init();
printf("Bluetooth 初始化完成!\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// 从串口不停轮询读取数据或状态,只有返回值为0,才是真正的数据
if (Bluetooth_ReadDataAndStatus(rxBuff, &rxDataLen) == 0)
{
printf("蓝牙模块接收到数据,长度为:%d, 内容为:%.*s\n", rxDataLen, rxDataLen, rxBuff);
// 数据原样发回去
Bluetooth_SendData(rxBuff, rxDataLen);
rxDataLen = 0;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
作者:嵌界游龙