前面的文章里面講了很多藍(lán)牙基礎(chǔ)的協(xié)議和硬件方面的知識(shí),現(xiàn)在進(jìn)入實(shí)踐篇
今天我介紹一下,我使用磐啟微的PAN1070來實(shí)現(xiàn)一個(gè)室內(nèi)自行車的應(yīng)用,最后把源碼和硬件放在gitee上面,大家可以回家改造一下自己的傻瓜運(yùn)動(dòng)器械。
先介紹一下PAN1070,這是一個(gè)藍(lán)牙SOC,主打低功耗來替換Nordic的藍(lán)牙SOC,眾所周知哈,國(guó)產(chǎn)替代勢(shì)不可擋。
這個(gè)芯片的接收功耗還是比較低的,并且有512KB的FLASH,可以實(shí)現(xiàn)非常多的功能,最主要是的磐啟微自己提供了很多的實(shí)際的案例,在開發(fā)的時(shí)候,我們可以直接參照案例修改一下就可以應(yīng)用起來。
同時(shí),PAN1070的最新的SDK是基于開源的NimBLE協(xié)議棧開發(fā),和火熱的ESP32的藍(lán)牙協(xié)議棧一樣,這樣上手起來也會(huì)比較輕松。
首先,我們先把官方的SDK下載到本地,然后在我們的Gitee上創(chuàng)建一個(gè)倉(cāng)庫,并將SDK上傳上去,作為一個(gè)基礎(chǔ)版本。
https://gitee.com/geek-pi/pan1070.git
官方的SDK從這里下載:https://wiki.panchip.com/download/2799/
官方文檔地址:https://docs.panchip.com
也可以從上面網(wǎng)址進(jìn)入文檔介紹后,從最左下角選擇對(duì)應(yīng)的版本的SDK,對(duì)了官方叫NDK,是指的基于NimBLE協(xié)議棧的版本,后續(xù)還有ZDK,這個(gè)是基于Zephyr的,所以別迷糊了。
整個(gè)SDK包括幾個(gè)內(nèi)容
SDK包含了我們編程序是需要的源代碼和示例代碼,HDK更多的是硬件相關(guān)的,比如各種開發(fā)板等,我們可以先不理會(huì)。MCU是一些控制器相關(guān)的使用例程,我們可以在用到某些單片機(jī)外設(shè)的時(shí)候再去了解,DOC和Tools都是一些文檔和工具。遇到問題時(shí)在去看吧。
我們先來看原廠提供的例程有哪些?
在目錄SDK→nimble→samples→solutions中有很多現(xiàn)成的解決方案,我們做新的應(yīng)用的時(shí)候,可以在這里找相似的去做修改,有一些也可以直接用,比如透?jìng)髂K,直接使用appuart,還有多主機(jī)多從機(jī)連接的案例,可以使用blehiduartmult_roles這個(gè)案例。
因?yàn)槲冶酒诮榻B的是實(shí)現(xiàn)一個(gè)室內(nèi)自行車的方案,所以我選擇了一個(gè)比較簡(jiǎn)單的案例來修改,也就是blevehicleskey 。
這個(gè)方案本來功能是實(shí)現(xiàn)接近解鎖的,內(nèi)部通過RSSI來判斷是否要對(duì)電動(dòng)車或者電動(dòng)門之類的設(shè)備進(jìn)行解鎖。這個(gè)有空可以了解下,今天主要看如何在這個(gè)例程上修改為室內(nèi)自行車。
先說一下我的開發(fā)習(xí)慣
在基礎(chǔ)的倉(cāng)庫上,我會(huì)新建一個(gè)分支,并以實(shí)現(xiàn)的功能作為命名,然后直接在官方的例程上做修改,這樣基本上不需要自己搭建環(huán)境,同時(shí)還可以在vs code中看到所有例程的代碼,方便來回copy。
每個(gè)例程中的代碼其實(shí)很簡(jiǎn)單,一個(gè)文件件存放工程配置信息,一個(gè)文件件存放源代碼,用戶自己添加的代碼都可以放到src中。
我們使用keil打開工程
雖然這里引用了很多文件,大部分都是藍(lán)牙協(xié)議棧和freeRTOS相關(guān)的底層代碼,我們可以先不關(guān)心他們,主要先要了解的是上圖中的2部分,第一個(gè)是SDK的配置文件,他里面是一些配置選型,利用的keil的頭文件注釋界面來配置各種參數(shù)。第二部分就是調(diào)用nimble相關(guān)的API來實(shí)現(xiàn)藍(lán)牙的功能,基本上就是配置廣播幀廣播,然后配置服務(wù)和服務(wù)對(duì)應(yīng)的讀寫接口。
先來看一下配置界面
由于配置信息很多,這里只介紹我用到的,就是這里的log打印系統(tǒng),這里要根據(jù)我們手里的板子的鏈接來選擇一下,我的板子使用的串口log輸出引腳是P05,波特率我選擇的115200 。
之后,我們編譯這個(gè)項(xiàng)目,下載運(yùn)行后,就可以看到串口的logo信息了。
下載不了?是不是燒錄算法沒有找到,我們需要把SDK目錄下的燒錄算法拷貝到keil中,keil就可以找到了,SDK燒錄算法的路徑為:SDK3MCUmcumisc 。
下圖是串口打印信息
現(xiàn)在,我們的例程已經(jīng)跑起來了,我們的DIY才剛剛開始。
一、創(chuàng)建新的分支來開發(fā)室內(nèi)自行車,命名為ftms
在基礎(chǔ)工程上新建分支來開發(fā)新功能,可以保證各個(gè)項(xiàng)目公用一個(gè)基礎(chǔ),不必新建立很多工程,直接利用SDK現(xiàn)有的工程即可。通過git管理,還可以保證他們之間互不影響。
ftms是運(yùn)動(dòng)健康協(xié)議的縮寫,這里使用它來作為分支名。
二、修改廣播信息,讓頑鹿等APP認(rèn)識(shí)我們的藍(lán)牙設(shè)備
廣播信息的修改在main函數(shù)中,函數(shù)名稱為:bleprph_advertise(),所有的廣播參數(shù)都是在這里寫入到一個(gè)緩存中,基本上就是按照數(shù)據(jù)+長(zhǎng)度這樣的結(jié)構(gòu)堆積進(jìn)去的。
這里我們需要把廣播幀設(shè)置為下圖這樣:
具體含義可以參考這個(gè)文章
//首先增加UUID,也就是FTM的標(biāo)準(zhǔn)UUID,這個(gè)是一個(gè)宏定義。
fields.uuids16 = (bleuuid16t[]) {
BLEUUID16INIT(BTUUIDFTM)
};
fields.num_uuids16 = 1;
// Service Data AD Type: 0x16 (1 byte)
uint8_t service_data[] = {
0x26, 0x18, // Fitness Machine Service UUID (0x2618, 小端存儲(chǔ))
0x01, // Flags (0x01, bit0 set to indicate Fitness Machine Available)
0x30, 0x00 // Fitness Machine Type (0x1000, 表示劃船機(jī)類型,2字節(jié)小端)
};
fields.svc_data_uuid16 = service_data;
fields.svc_data_uuid16_len = sizeof(service_data);
這樣廣播以后,頑鹿APP中就可以看到我們的設(shè)備了。
三、添加一些對(duì)應(yīng)的服務(wù)和特性
讓上位機(jī)APP發(fā)現(xiàn)我們的設(shè)備只是第一步,第二部是讓我們的設(shè)備可以和APP通信。那么,我們就需要設(shè)定一個(gè)接口協(xié)議來和上位機(jī)通信,這就是藍(lán)牙協(xié)議中的標(biāo)準(zhǔn),我們要配置多個(gè)服務(wù),以及服務(wù)內(nèi)部包含的特征。這些特征有的是可讀寫的,有的是只讀的,也有的是可以Notify的,可以理解為藍(lán)牙設(shè)備主動(dòng)給APP發(fā)的。
我們下面在代碼中看看如何增加這樣的屬性和特性進(jìn)去。這個(gè)修改要在gatt_svr.c中進(jìn)行,它是定義了一個(gè)數(shù)據(jù)類型,可以說是結(jié)構(gòu)體套著結(jié)構(gòu)體的一個(gè)結(jié)構(gòu),這樣寫主要是為了直觀,因此我們改代碼也就直觀了。
static?const?struct?blegattsvcdef?gattsvrsvcs[]?=?{
//服務(wù)
{
/* Service: 顯示屬性的DIS/
.type = BLEGATTSVCTYPEPRIMARY,
.uuid = BLEUUID16DECLARE(BTUUIDDIS),
.characteristics = (struct blegattchrdef[]) {
{
.uuid = BLEUUID16DECLARE(BTUUIDDISMODEL),
.accesscb = gattsvrchraccessftminfo, //只讀的只需要回調(diào)函數(shù)
.flags = BLEGATTCHRFREAD, //只讀
},
{
.uuid = BLEUUID16DECLARE(BTUUIDDISMNS),
.accesscb = gattsvrchraccessftminfo, //只讀的只需要回調(diào)函數(shù)
.flags = BLEGATTCHRFREAD, //只讀
},
{
0, / No more characteristics in this service /
}, }
},
//新的服務(wù)
{
/ Service: vendor ble uart /
.type = BLEGATTSVCTYPEPRIMARY,
.uuid = BLEUUID16DECLARE(BTUUIDFTM),
.characteristics = (struct blegattchrdef[]) {
{
/ Characteristic: Heart-rate measurement /
.uuid = BLEUUID16DECLARE(BTUUIDFTMINDOORBIKE),
.accesscb = gattsvrchraccessftms,
.valhandle = &hrsftmshandle, //Notify 對(duì)應(yīng)的handle句柄
.flags = BLEGATTCHRFNOTIFY,
},
// {
// / Characteristic: Heart-rate measurement /
// .uuid = BLEUUID16DECLARE(BTUUIDFTMSTATUS),
// .accesscb = gattsvrchraccessftms,
// .valhandle = &hrsftmshandle,
// .flags = BLEGATTCHRFNOTIFY | BLEGATTCHRFREAD,
// },
{
/ Characteristic: Heart-rate measurement /
.uuid = BLEUUID16DECLARE(BTUUIDFTMCTRL),
.accesscb = gattsvrchraccessftms,
.valhandle = &ftmctrlhandle,//Notify 對(duì)應(yīng)的handle句柄
.flags = BLEGATTCHRFWRITE | BLEGATTCHRF_INDICATE,
},
{
0, / No more characteristics in this service /
},
}
},
//其他服務(wù)
{
0, / No more services */
},
};
第一個(gè)屬性是FTMS要求的服務(wù),APP會(huì)從這個(gè)服務(wù)中讀取各種屬性,比如廠家,序列號(hào),型號(hào)以及產(chǎn)品類型等等,他們基本上都是只讀的,因此,可以從程序中看到,他的flags屬性的設(shè)置。
對(duì)于只讀的特性,我們只需要設(shè)置一個(gè)回調(diào)函數(shù)就可以了,比如:.accesscb = gattsvrchraccessftminfo, ?//只讀的只需要回調(diào)函數(shù)。
在回調(diào)函數(shù)中,我們來處理上位機(jī)的讀取請(qǐng)求,就是把我們本地定義好的字符串返回給協(xié)議棧。