藍牙基礎知識(了解即可):
1.是一種利用低功率無線電,支持設備短距離通信的無線電技術,能在包括移動電話、PDAQ、無線耳機、筆記本電腦、相關外設等眾多設備之間進行無線信息交換,藍牙工作在全球通用的2.4 GHz(2.4 至 2.485 GH)ISM(即工業、科學、醫學)頻段(和WI-FI頻段一樣就能結合起來用),使用IEEE802.11協議。
第一~三藍牙重心都往高速傳輸速率發展,但是由于WI-FI已經發展的很好了,所以沒必要將發展理念和WI-FI重合到來第4代就將重心向低功耗靠攏
第四代藍牙:主推Low Energy低功耗,BLE(Bluetooth Low Energy)低功耗功能。
第五代藍牙:開啟物聯網時代大門,在低功耗模式下具備更快更遠的傳輸能力,為我們提供兩種選擇:低功耗遠距離傳輸/高速近距離傳輸。(物聯網廣泛應用)
第六代藍牙:新增加了信道功能(就是可以定位連接了藍牙的設備,提高到1cm的精度)
藍牙技術類型:
藍牙協議包括兩種技術:BR(Basic Rate經典藍牙)和LE(Low Energy)都是使用2.4G無線電頻率因此雙模設備可以共享同一個天線。
這兩種技術都包括搜索(discovery)管理、連接(connection)管理等機制,但它們是相互獨立的,不能互通的技術!
廠商如果只實現了一種,那么只能與同樣實現該技術的設備互通。如果廠商要確保能和所有的藍牙設備互通,那么就只能同時實現兩種技術,而不去管是否真的需要。
2.市場上常見藍牙架構:
SOC藍牙單芯片方案
此類芯片一般可以直接做為MCU用,這類產品一般用于消費類電子,集成度很高,調一調參數可以直接使用,常見的有藍牙耳機,音響,藍牙心率等產品。
SOC藍牙+MCU方案
接外部主機,WI-FI就是這種架構藍牙芯片內置射頻模塊,電路和物理層的各種支持,但是自己不燒寫應用邏輯,那么我們就可以通過調用它給定的規則來調用它的功能符合我們的項目設想(藍牙手表上網):
在集成好的藍牙芯片基礎上,通過特定的接口(UART居多),發送自定義的command來達到想要的功能,比如發送0x01代表搜索周圍設備。
外設一個單芯片方案,其自定義指令包含藍牙芯片(BT Controller)、微控制單元(MCU)以及藍牙協議棧(BT Host)。
此部分的應用,將藍牙作為一個外設使用,用于遠程通信,例如網上賣的一些藍牙串口。
比如我們后面應用就是采用的這種方案。 STM32作為MCU使用,ESP32作為藍牙芯片使用。
藍牙host+controller分開方案
單獨將協議給功能更加強大的MCU處理目的就是為了實現更加強大的功能:
這種應用算是藍牙最復雜的應用,客戶需要使用藍牙的場景有很多,涉及的藍牙協議也有很多,需要將Host與Controller分開,集成更多的藍牙協議,比如藍牙電話(HFP)、藍牙音頻(A2DP)、藍牙音樂控制(AVRCP)、藍牙電話本(PBAP)、藍牙短信(MAP),手機藍牙等。
此部分應用,將定制藍牙的各種服務,實現藍牙多功能需求。
3.藍牙協議棧:很復雜,廠家規定且打包好了,我們會用它的函數或者指令就行(一般都有用戶指南,可以根據提示自己內測)
藍牙芯片架構:
藍牙的核心系統,由一個Host和一個或多個Controller組成。
(1)BT Host:邏輯實體(控制軟件架構),在HCI(Host Controller Interface(host和Controller的接口層))的上層。
(2)BT Controller:邏輯實體(控制偏底層和硬件),在HCI(Host Controller Interface)的下層。
根據Host與Controller的組成關系,常見的藍牙芯片也分為以下幾種:
(1)單模藍牙芯片:單一傳統藍牙的芯片,單一低功耗藍牙的芯片。即1個Host結合1個Controller。
(2)雙模藍牙芯片:同時支持傳統藍牙和低功耗藍牙的芯片。即1個Host結合多個Controller。
對于BT Host而言雖然下層有不同的BT Controller但是我們都可以用軟件來分開控制,寫代碼而已,所以就一個BT Host就行。
藍牙協議是通信協議的一種,一般而言,我們把某個協議的實現代碼稱為協議棧(protocol stack),BLE協議棧就是實現低功耗藍牙協議的代碼。
4.BLE低功耗藍牙協議棧框架
要實現一個BLE應用,首先需要一個支持BLE射頻的芯片,然后還需要提供一個與此芯片配套的BLE協議棧,最后在協議棧上開發自己的應用。
可以看出BLE協議棧是連接芯片和應用的橋梁,是實現整個BLE應用的關鍵。
簡單來說,因為是由上到下的層層協議,所以BLE協議棧主要用來對你的應用數據進行層層封包(類似與CAN協議的控制段,應答段等),以生成一個滿足BLE協議的空中數據包,也就是說,把應用數據包裹在一系列的幀頭(header)和幀尾(tail)中。
藍牙協議規定了兩個層次的協議,分別為藍牙核心協議(Bluetooth Core)和藍牙應用層協議(Bluetooth Application)。
藍牙核心協議關注對藍牙核心技術的描述和規范,它只提供基礎的機制,并不關心如何使用這些機制;
藍牙應用層協議,是在藍牙核心協議的基礎上,根據具體的應用需求,百花齊放,定義出各種各樣的策略,如FTP、文件傳輸、局域網等等。
ESP32-C3中的藍牙功能:
ESP32-C3支持Bluetooth 5(LE)。下載好固件之后(我們前面下載的固件已經支持Wi-Fi和藍牙了),STM32仍然可以通過AT指令操作藍牙。
1.BLE角色劃分
在 Bluetooth LE 協議棧中不同的層級有不同的角色劃分。這些角色劃分互不影響。
LL:設備可以劃分為主機和從機,從機廣播,主機可以發起連接。
GAP:定義了4種特定角色:廣播者、觀察者、外圍設備和中心設備。(我們在編程不會涉及它)
GATT:設備可以分為服務端和客戶端。定義了數據的組織結構和服務器端提供哪些服務,客戶端就可以調用哪些服務。如果其為主機廣播信號發起連接,本質底層是聯網操作和上層是否是服務器或客戶端無關,如果對方連接了主機,對方就是服務器,主機就是客戶端,就好比你用手機監聽到藍牙,連上藍牙不就是將手機數據發到藍牙上,我們就可以戴藍牙耳機聽音樂了
2.BLE的地址(主機和從機建立連接后就知曉對方地址)
分為公共地址和隨機地址。公共地址:BLE的公共地址,就類似于我們日常的身份證號碼,是全球唯一的且不可改變的。為了保證BLE公共地址的全球唯一性,其需要向IEEE購買,然后IEEE組織就會對應地分配公共地址給買家。BLE的公共地址是全球唯一的,且在BLE設備的整個生命周期都不會改變。總長度為6個字節,共48位。
隨機地址除了公共地址類型之外,還有一個隨機地址類型,提供額外的隱私保護,其又分為靜態地址(Static Address)和私有地址(Private Address),它們之間主要通過最高的2位有效位來區分。
(1)靜態地址。總長度也是48位,但是最高位的2位必須是0b11。隨機部分至少有一個位是0和1。也就是說不能全部是0或全部是1。設備重啟之前不會改變。
(2)私有地址。總長度也是48位。最高位是00/01。隨機部分至少有一個位是0和1。也就是說不能全部是0或全部是1。定時更新改變。
3.藍牙掃描和通訊,藍牙結點要廣播信息通知別人進行連接
咱們藍牙就作為LL的從機角色和GATT的服務端角色每各一段時間就發送廣播信號,告訴別的設備咱們的存在。藍牙可以使用40多個信道,但是廣播信號只允許在37,38,39三個信道發出去,所以我們就能配是按順序依次發出去,還是單獨選一信道給發出去
通過掃描,主機能收到從機的廣播包和回應數據包,然后給這些從機設備發連接請求,連接上后就能通信了。?這里還能配藍牙能不能掃到,掃到能不能連,只能誰來連.......都能設,可以看用戶指南
通訊可以配的:一個從機設備包括一個或者多個服務;一個服務中又可以包括一條或者多條特征值,每個特征值都有自己的屬性 Property,屬性的取值有:可讀 Read、可寫 Write、通知 Notify、指示Indicate。每個服務和特征值都有自己的唯一標識 UUID,標準UUID為128位,藍牙協議棧中一般采用16位,也就是兩個字節的UUID格式。
藍牙通訊案例:透傳模式下收發數據(選的芯片ESP32-C3)
用戶指南翻到Bluetooth LE AT實例就有教怎么配的,我們選這個例子
.設置藍牙透傳模式:
透傳模式:設備 A 發送一串字節流,通過藍牙透傳模式,設備 B 接收到的就是完全相同的字節流,就如同這兩個設備直接用一根線連接起來進行數據傳輸一樣,ESP32無需先知道數據有多長也不用做一堆命令解析的操作了,避免了普通數據傳輸模式的很多麻煩。
針對藍牙的操作:
1.在手機端下載Bluetooth LE(隨便在你手機的商城那搜索藍牙助手,隨便下個就行都能用)
2.初始化藍牙
printf("設置模塊角色為:服務器...\n");uint8_t *cmd = "AT+BLEINIT=2\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));
3.創建藍牙服務
// 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.獲取/設置藍牙自己的MAC地址,廠家就給設備買了唯一的MAC地址,我們直接查詢就行,避免我們藍牙和別的藍牙名稱一致分不出來
printf("獲取服務端MAC地址...\n");cmd = "AT+BLEADDR?\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=\"mydevice\",\"A123\",\"0102030405\",1\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));
8.開啟藍牙廣播
// 8. 開始廣播printf("開始廣播...\n");cmd = "AT+BLEADVSTART\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));
9.查詢藍牙給連接的設備提供了什么服務,可寫也可以不寫
10.配置藍牙 SPP
?
printf("配置 BLE SPP 參數...\n");cmd = "AT+BLESPPCFG=1,1,7,1,5\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));
11.使能藍牙 SPP(反饋OK,就說明能給手機發數據和收數據了)和手機連上了就開啟,斷開了就關閉
11.1使能SPP,和手機連上了就開啟,斷開了就關閉,要接收變化狀態就給SYSMSG配成100就是4唄
printf("設置系統提示信息...\n");cmd = "AT+SYSMSG=4\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));
static uint8_t BLE_IsConnChanged(uint8_t msg[])
{// 1. 如果是BLE建立連接,就進入SPP透傳模式if (strstr((char *)msg, "+BLECONN:") != NULL){printf("有BLE客戶端連接,馬上進入SPP模式...\n");uint8_t * cmd = "AT+BLESPP\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));return 1;}// 2. 如果是BLE斷開連接,就退出透傳模式else if (strstr((char *)msg, "+BLEDISCONN:") != NULL){printf("BLE客戶端斷開連接,即將退出SPP模式...\n");// 直接串口發送 +++ 退出透傳模式HAL_UART_Transmit(&huart2, "+++", 3, 1000);// 延時等待HAL_Delay(2000);return 1;}// 3. 如果是其它類型的連接變化(Wi-Fi),不做任何處理,返回1else if(strstr((char *)msg, "WIFI CONNECTED") != NULL|| strstr((char *)msg, "WIFI GOT IP") != NULL|| strstr((char *)msg, "+DIST_STA_IP:") != NULL){return 1;}// 4. 其它情況默認是正常數據return 0;
}
11.2STM32通過串口給ESP32發什么,藍牙就會通過電磁波向外發送一樣的數據
// 發送數據
void BLE_SendData(uint8_t txBuff[], uint16_t txLen)
{// 直接通過串口發送數據HAL_UART_Transmit(&huart2, txBuff, txLen, 1000);
}
11.3接收數據,沒有進入SPP模式時接收到的都是藍牙的內部數據,不是手機發來的,所以要判斷連沒連上手機
// 接收(讀取)數據
void BLE_ReadData(uint8_t rxBuff[], uint16_t *rxLen)
{// 從串口接收數據或狀態改變信息HAL_UARTEx_ReceiveToIdle(&huart2, rxBuff, 1024, rxLen, 1000);// 如果是空數據,rxLen = 0,就直接返回if (*rxLen == 0){return;}// 如果是連接變化信息,執行相應的SPP操作,并清零len,表示不是正常數據if ( BLE_IsConnChanged(rxBuff) ){*rxLen = 0;}
}
針對手機的操作:
1.藍牙開啟廣播后,手機就能在藍牙助手那連藍牙了
2.連上后,要勾選需要的服務(點單),勾上藍牙就能上菜了
3.通過串口助手給藍牙發數據
ble.h
#ifndef __BLE_H
#define __BLE_H#include "esp32.h"// 初始化
void BLE_Init(void);// 發送數據
void BLE_SendData(uint8_t txBuff[], uint16_t txLen);// 接收(讀取)數據
void BLE_ReadData(uint8_t rxBuff[], uint16_t *rxLen);#endif
ble.c
#include "ble.h"// 初始化
void BLE_Init(void)
{// 1. 初始化ESP32ESP32_Init();// 2. 設置藍牙模塊角色:2-serverprintf("設置模塊角色為:服務器...\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. 獲取服務端MAC地址printf("獲取服務端MAC地址...\n");cmd = "AT+BLEADDR?\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=\"mydevice\",\"A123\",\"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 SPP 參數...\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));
}// 發送數據
void BLE_SendData(uint8_t txBuff[], uint16_t txLen)
{// 直接通過串口發送數據HAL_UART_Transmit(&huart2, txBuff, txLen, 1000);
}// 自定義函數,判斷是否有連接變化信息;如果有則返回1,沒有返回0
static uint8_t BLE_IsConnChanged(uint8_t msg[]);// 接收(讀取)數據
void BLE_ReadData(uint8_t rxBuff[], uint16_t *rxLen)
{// 從串口接收數據或狀態改變信息HAL_UARTEx_ReceiveToIdle(&huart2, rxBuff, 1024, rxLen, 1000);// 如果是空數據,rxLen = 0,就直接返回if (*rxLen == 0){return;}// 如果是連接變化信息,執行相應的SPP操作,并清零len,表示不是正常數據if ( BLE_IsConnChanged(rxBuff) ){*rxLen = 0;}
}static uint8_t BLE_IsConnChanged(uint8_t msg[])
{// 1. 如果是BLE建立連接,就進入SPP透傳模式if (strstr((char *)msg, "+BLECONN:") != NULL){printf("有BLE客戶端連接,馬上進入SPP模式...\n");uint8_t * cmd = "AT+BLESPP\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));return 1;}// 2. 如果是BLE斷開連接,就退出透傳模式else if (strstr((char *)msg, "+BLEDISCONN:") != NULL){printf("BLE客戶端斷開連接,即將退出SPP模式...\n");// 直接串口發送 +++ 退出透傳模式HAL_UART_Transmit(&huart2, "+++", 3, 1000);// 延時等待HAL_Delay(2000);return 1;}// 3. 如果是其它類型的連接變化(Wi-Fi),不做任何處理,返回1else if(strstr((char *)msg, "WIFI CONNECTED") != NULL|| strstr((char *)msg, "WIFI GOT IP") != NULL|| strstr((char *)msg, "+DIST_STA_IP:") != NULL){return 1;}// 4. 其它情況默認是正常數據return 0;
}
main.c
// 全局變量,接收數據緩沖區、長度
uint8_t rxBuff[1024];
uint16_t rxLen;int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();// MX_USART2_UART_Init();/* USER CODE BEGIN 2 */// 藍牙初始化BLE_Init();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){// 讀取數據BLE_ReadData(rxBuff, &rxLen);// 如果接收到數據,就原樣發回去if (rxLen > 0){printf("接收到數據!數據長度 = %d, 內容 = %.*s\n", rxLen, rxLen, rxBuff);BLE_SendData(rxBuff, rxLen);// 將長度清0rxLen = 0;}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}