1.准备
BLE通讯原理
使用的ESP32芯片 包含有WIFI和BLE模块 => 把ESP32当做BLE的外设模块使用
(1) 使用官方提供的AT固件 => 烧录进ESP32中
(2) 采用USART2通信 => 发送AT指令给ESP32 => 做出对应的响应
硬件原理图
(1) USART2 => ESP32
项目准备
(1) 使用cube 构建代码
(2) 修改系统时钟 => 打开USART1 用于printf输出 => 打开USART2 用于和ESP32通信
(3) 修改项目名称 => 创建代码
(4) 添加Int_ESP32.c和Int_ESP32.h文件
(5) 修改keil => 勾选lib => 修改debug => 添加对应的文件和目录
项目开发
1. 初始化ESP32
(1) 初始化底层驱动 usart2
(2) 调用接收ESP32信息的函数
(3) 重置ESP32即可 => 延时3s
2. 初始化BLE
(1) 选择作为服务端
(2) 创建对应的服务
(3) 启动服务
(4) 设置广播参数
(5) 设置广播数据 => 使用自动方式设置
(6) 开始广播
(7) 配置透传参数
(8) 设置打印连接变更的信息 SYSMSG
3. 处理连接状态变化的方法
Inf_BLE_Handle_Change
(1) 如果接收到的信息包含 +BLECONN 连接成功信息
开启透传 (会自动生成1字节的信息 需要消耗掉)
(2) 如果接收到的信息包含 "+BLEDISCONN 断开连接信息
关闭透传 重新广播
4. 接收数据的函数
Inf_BLE_ReadData => BLE_DATA
只有当前接收的数据是透传模式之后的数据 才需要完成接收响应
进入透传模式之后 ESP32使用USART2和STM32交互的信息全部都是 纯净的数据信息
5. 发送数据的函数
Inf_BLE_SendData
只有在透传模式下 => 才能使用USART2发数据给ESP32中 => 当做数据回复给另外一个蓝牙设备
2. 简介
2.1 蓝牙技术类型
经典蓝牙(BR/EDR)
低功耗蓝牙(BLE)
2.2 市场上常见的蓝牙架构
SOC单芯片
SOC蓝牙芯片 + MCU
蓝牙 host 和 Controller 分开方案(HC)
2.3 数据传输
普通模式,加一些头尾信息
透传模式,不加其他信息
2.4 BLE 角色划分
LL:设备可以划分为 主机 和 从机,从机广播,主机可以发起连接。
GAP:定义了 4 种特定角色:广播者、观察者、外围设备 和 中心设备。
GATT:设备可以分为 服务端 和 客户端
2.5 蓝牙通信的动作
广播:当从机处于广播状态时,主机(客户端)才能发现该从机(服务端)。在每个广播事件中,广播包会分别在37、38和39三个信道上依次广播
扫描:扫描是主机监听从机广播数据包和发送扫描请求的过程
通讯:主机作为GATT的Client端,用来发现和获取从机的Service和Characteristic,从而与之通信。
3.案例
2.1 透传模式下收发数据
-
Inf_ESP32.c
(1)AT指令的发送及返回数据的处理
#include "Inf_ESP32.h"
uint8_t rBuff[128];
uint16_t rDataLength;
uint8_t resPonseBuff[1024];
uint16_t resDataLength;
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
// 健壮性判断
if (huart->Instance == USART2)
{
// 此时是ESP32回复的消息
rDataLength = Size;
// 一次调用HAL_UARTEx_ReceiveToIdle_IT中断只触发一次回调
HAL_UARTEx_ReceiveToIdle_IT(&huart2, rBuff, 128);
// 打印收到的数据
// 原则性问题:不能在中断处理中使用运行时间过长的函数
// printf("接收到的ESP32收到的数据为%s\n",rBuff);
}
}
void Inf_Handle_Response(void)
{
// 循环·等待接收多次5消息 ==> 一直到接收完成 或者收到OK或ERROR
uint8_t count = 2;
/* 1. 初始化responseBuff */
memset(resPonseBuff, 0, 1024);
resDataLength = 0;
do
{
// 单次接收到的数据存放到resPonseBuff中
while (rDataLength == 0)
;
// 直到数据过来,存放在大的缓存中
memcpy(&resPonseBuff[resDataLength], rBuff, rDataLength);
resDataLength += rDataLength;
rDataLength = 0;
memset(rBuff, 0, 128);
} while (count-- && (strstr((char *)resPonseBuff, "OK") == NULL) && (strstr((char *)resPonseBuff, "ERROR") == NULL));
printf("%s\n", resPonseBuff);
printf("--------------\n");
}
void Inf_ESP32_SendCmd(uint8_t *cmd)
{
// 直接使用USART2发送命令就是发送指令
HAL_UART_Transmit(&huart2, cmd, strlen((char *)cmd), 1000);
// 由于不能一次执行多条指令 ==> 挂起等待执行响应
Inf_Handle_Response();
}
(2)ESP32初始化
void Inf_ESP32_Init(void)
{
/*1. 初始化usart2 */
MX_USART2_UART_Init();
/*2. 提前调用接收ESP32返回消息的函数*/
HAL_UARTEx_ReceiveToIdle_IT(&huart2, rBuff, 128);
/*3. 让ESP32发送初始化消息*/
uint8_t *cmd = "AT+RST=0\r\n";
Inf_ESP32_SendCmd(cmd);
// 延时3s,等待ESP32重启
HAL_Delay(3000);
}
(3)蓝牙初始化
void Inf_ESP32_BLE_Init(void)
{
/*0. 初始化ESP32*/
Inf_ESP32_Init();
/*1. 初始化BLE, 选择作为服务端*/
uint8_t *cmd = "AT+BLEINIT=2\r\n";
Inf_ESP32_SendCmd(cmd);
/*2. 创建BLE服务端服务。*/
cmd = "AT+BLEGATTSSRVCRE\r\n";
Inf_ESP32_SendCmd(cmd);
/*3. BLE服务端开启服务*/
cmd = "AT+BLEGATTSSRVSTART\r\n";
Inf_ESP32_SendCmd(cmd);
/*4. 设置广播参数*/
// 50ms,50ms 最小和最大广播间隔;
// 7:广播信道,7表示使用37,38,39三个信道
cmd = "AT+BLEADVPARAM=50,50,0,0,7,0,,\r\n";
Inf_ESP32_SendCmd(cmd);
/* 5. 设置广播数据 -> 使用自动档 */
// 第一个参数:设备名称; 第二个参数:UUID;
cmd = "AT+BLEADVDATAEX=\"test-10086\",\"A123\",\"1122334455\",1\r\n";
Inf_ESP32_SendCmd(cmd);
/*6. 开始广播*/
cmd = "AT+BLEADVSTART\r\n";
Inf_ESP32_SendCmd(cmd);
/*7. 配置透传模式SPP*/
// 第一个参数 “1”:0表示重置,1才能填写后面
// 第二个参数“1”:tx服务序号 第三个参数 “7”:tx服务特征序号 对应306
// 第四个参数“1”:rx服务序号 第三个参数 “5”:rx服务特征序号 对应304
cmd = "AT+BLESPPCFG=1,1,7,1,5\r\n";
Inf_ESP32_SendCmd(cmd);
// 不能直接使能透传模式 => 需要等待手机连接完成才能开透传
/* 8. 打开连接状态信息 => 方便判断什么时候进入透传模式 */
// bit2为1,及其值为4
cmd = "AT+SYSMSG=4\r\n";
Inf_ESP32_SendCmd(cmd);
}
(4)根据ESP32返回消息判断处于什么状态
ESP32State Inf_BLE_Handle_Change(void)
{
/*根据ESP32返回的消息来判断处于什么状态*/
// 1. +BLECONN 处于连接状态
if (strstr((char *)rBuff, "+BLECONN") != NULL)
{
// 清空数据
rDataLength = 0;
memset(rBuff, 0, sizeof(rBuff));
// 进入透传模式
printf("连接BLE成功 准备进入透传模式\n");
uint8_t *cmd = "AT+BLESPP\r\n";
Inf_ESP32_SendCmd(cmd);
// 将额外返回的一个字节 '>' 给消耗掉 => 不影响后续的透传收发的数据
uint8_t tmp;
HAL_UART_Receive(&huart2, &tmp, 1, 1000);
return BLE_SPP;
}
// 2. +BLEDISCONN 处于关闭连接状态
if (strstr((char *)rBuff, "+BLEDISCONN") != NULL)
{
// 清空数据
rDataLength = 0;
memset(rBuff, 0, sizeof(rBuff));
// 断开连接
printf("BLE断开连接 关闭透传 重新广播\n");
// 2.1 关闭透传
// 当系统收到只含有 +++ 的包时,设备返回到普通命令模式,请至少等待一秒再发送下一个 AT 命令。
HAL_UART_Transmit(&huart2, "+++", 3, 1000);
HAL_Delay(2000);
// 2.2 重新广播
uint8_t *cmd = "AT+BLEADVSTART\r\n";
Inf_ESP32_SendCmd(cmd);
return BLE_DIS;
}
return BLE_DATA;
}
(5)数据收发
void Inf_BLE_ReadData(uint8_t data[], uint16_t *rxDataSize)
{
if (rDataLength == 0)
{
return;
}
// 此时的数据不是透传收发的数据 属于状态变更数据
if (Inf_BLE_Handle_Change() != BLE_DATA)
{
return;
}
// 健壮性处理
memset(data, 0, sizeof(data));
// 将rBuff中接收到的数据 赋值到 data
memcpy(data, rBuff, rDataLength);
*rxDataSize = rDataLength;
rDataLength = 0;
}
void Inf_BLE_SendData(uint8_t data[], uint16_t size)
{
// 处于透传模式 => 直接使用USART2发送数据即是传输给BLE
HAL_UART_Transmit(&huart2, data, size, 1000);
}
- Inf_ESP32.h
#ifndef __INF_ESP32_H__
#define __INF_ESP32_H__
#include "main.h"
#include "usart.h"
#include "string.h"
typedef enum
{
BLE_SPP, // 连接状态 => 指令进入透传模式
BLE_DIS, // 关闭连接 => 关闭SPP透传模式 => 正常发送接收AT指令
BLE_DATA, // 稳定连接可以传输数据的状态 => 接收和发送的所有数据都当做纯净的数据收发
WIFI_CHANGE, // WIFI的变更信息
}ESP32State;
void Inf_ESP32_Init(void);
void Inf_ESP32_SendCmd(uint8_t *cmd);
void Inf_Handle_Response(void);
void Inf_ESP32_BLE_Init(void);
ESP32State Inf_BLE_Handle_Change(void);
void Inf_BLE_ReadData(uint8_t data[],uint16_t * rxDataSize);
void Inf_BLE_SendData(uint8_t data[],uint16_t size );
#endif /* __INF_ESP32_H__ */
- main.c
/* USER CODE BEGIN 2 */
printf("hello BLE...\n");
// 初始化蓝牙
Inf_ESP32_BLE_Init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
uint8_t rxBuff[128];
uint16_t rxDataSize;
while (1)
{
Inf_BLE_ReadData(rxBuff, &rxDataSize);
if (rxDataSize != 0)
{
printf("BLE接收到数据%s\n", rxBuff);
// 将数据返回
Inf_BLE_SendData(rxBuff, rxDataSize);
rxDataSize = 0;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */