聚豐項目 > 基于AB32VG1的藍(lán)牙通信小程序
藍(lán)訊驕龍 AB32VG1 是中科藍(lán)訊在 2020 RT-Thread 開發(fā)者大會上首度面向通用市場發(fā)布的其自主 RISC-V 內(nèi)核 32 位 MCU 芯片,具有豐富的軟硬件資源和低成本優(yōu)勢,微信小程序作為微信公眾號和app不足之處的補(bǔ)充,能夠提供更好的用戶體驗,多元性的曝光,在餐飲行業(yè),服務(wù)行業(yè),電子商務(wù)行業(yè)等領(lǐng)域有顯著的用戶流量。本設(shè)計意在為AB32開發(fā)板提供更多的應(yīng)用方向。
尋游記
尋游記
團(tuán)隊成員
尋游記 嵌入式軟件
RT-Thread使用情況概述:
整個方案涉及的技術(shù)有:rtt模塊化任務(wù)代碼設(shè)計,BLE-GATT,微信小程序軟件開發(fā)(wxml+wxss+js);
內(nèi)核部分:使用了線程、信號量、定時器 、PWM
設(shè)備驅(qū)動:
GPIO/PWM/BLE 等
PC端將程序燒錄到MCU,通過downloader調(diào)用ble命令開始開發(fā)板藍(lán)牙廣播,使其處于可被發(fā)現(xiàn)狀態(tài);小程序端打開藍(lán)牙搜索,找到開發(fā)板,獲取Notify/read/write信息;
這里著重說一下BLE-GATT。
現(xiàn)在低功耗藍(lán)牙(BLE)連接都是建立在 GATT (Generic Attribute Profile) 協(xié)議之上。GATT 是一個在藍(lán)牙連接之上的發(fā)送和接收很短的數(shù)據(jù)段的通用規(guī)范,這些很短的數(shù)據(jù)段被稱為屬性(Attribute)。
詳細(xì)介紹 GATT 之前,需要了解 GAP(Generic Access Profile),它在用來控制設(shè)備連接和廣播。GAP 使你的設(shè)備被其他設(shè)備可見,并決定了你的設(shè)備是否可以或者怎樣與合同設(shè)備進(jìn)行交互。例如 Beacon 設(shè)備就只是向外廣播,不支持連接,小米手環(huán)就等設(shè)備就可以與中心設(shè)備連接。
GAP 給設(shè)備定義了若干角色,其中主要的兩個是:外圍設(shè)備(Peripheral)和中心設(shè)備(Central)。
外圍設(shè)備:這一般是簡單的低功耗設(shè)備,用來提供數(shù)據(jù),并連接到一個更加相對強(qiáng)大的中心設(shè)備。這里指我們的開發(fā)板。
中心設(shè)備:中心設(shè)備相對比較強(qiáng)大,用來連接其他外圍設(shè)備。這里指手機(jī)端。
GAP 的廣播工作流程如下圖所示
GATT通信的雙方是C/S關(guān)系。外設(shè)作為GATT服務(wù)端(Server),它維持了ATT的查找表以及service和characteristic的定義。中心設(shè)備是GATT客戶端(Client),他向Server發(fā)起請求。需要注意的是,所有的通信事件,都是由客戶端(也叫主設(shè)備,Master)發(fā)起,并且接收服務(wù)端(也叫從設(shè)備,Slava)的響應(yīng)。而GATT事務(wù)是建立在嵌套的Profiles,Services和Characteristics之上的,如下如所示:
Profile :
Profile并不是實際存在于BLE外設(shè)上的,它只是一個被Bluetooth SIG或者外設(shè)設(shè)計者預(yù)先定義的Service的集合。例如我們的例程心率Profile(Heart Rate Profile)就是結(jié)合了Heart Rate Service和Device Information Sercvice。所有官方通過GATT Profile的列表可以從這里找到。
這里給出官網(wǎng)的列表Assigned Numbers。
Service:
Service是把數(shù)據(jù)分成一個個的獨立邏輯項,它包含一個或者多個Characteristic。每個Service有一個UUID唯一標(biāo)識。UUID有16bit的,或者128bit的。16bit的UUID是官方通過認(rèn)證的,需要花錢購買,128bit是自定義的,這個就可以自己隨便設(shè)置。
官方通過了一些標(biāo)準(zhǔn)Service,完整列表在這里。以Heart Rate Service為例,可以看到它的官方通過16bitUUID是0x180D,包含3個Characteristic:Heart Rate Measurement,Body Sensor Location和Heart Control Point,并且定義了只有一個第一個必須的,它是可選實現(xiàn)的。
Characteristic :
Characteristic 在GATT事務(wù)中的最低界別的是Characteristic,Characteristic是最小的邏輯數(shù)據(jù)單元,當(dāng)然它可能包含一個組關(guān)聯(lián)的數(shù)據(jù),例如加速度計的X/Y/Z三軸值。與Service類似,每個Characteristic用16bit或者128bit的UUID唯一標(biāo)識。你可以免費(fèi)使用Bluetooth SIG官方定義的標(biāo)準(zhǔn)Characteristic,使用官方定義的,可以確保BLE的軟件和硬件能相互理解。當(dāng)然,你可以自定義Characteristic,這樣的話,就只有你自己的軟件和外設(shè)能夠相互理解。
舉個例子,Heart Rate Measurement Characteristic,這是上面提到的Heart Rate Service必需實現(xiàn)的Characteristic,它的UUID是0x2A37。它的數(shù)據(jù)結(jié)構(gòu)是,開始8bit定義心率數(shù)據(jù)格式(是UINT8還是UINT16?),接下來就是對應(yīng)格式的實際心率數(shù)據(jù)。
實際上,和BLE外設(shè)打交道,主要是通過Characteristic。你可以從Characteristic讀取數(shù)據(jù),也可以往Characteristic寫數(shù)據(jù)。這樣就實現(xiàn)了雙向的通信。所以你可以自己實現(xiàn)一個類似串口(UART)的service,這個Service中包含兩個Characteristic,一個被配置只讀的通道(RX),另一個配置為只寫的通道(TX)。
軟件部分設(shè)計邏輯還是十分明確的;
開發(fā)板:打開ble,開始廣播等待被發(fā)現(xiàn)。建立通信后,按照主機(jī)端的請求發(fā)送相應(yīng)數(shù)據(jù);
小程序:打開藍(lán)牙適配器,搜索周圍藍(lán)牙,獲取搜索過程中所搜索到的設(shè)備信息,連接想要連接的設(shè)備,獲取服務(wù)、特征值,進(jìn)行數(shù)據(jù)交互;
貼上代碼:
在進(jìn)行完基本初始化之后,進(jìn)行對BLE的設(shè)置:
//設(shè)置回調(diào)函數(shù)
ble_hs_cfg.sync_cb = blehr_on_sync;
//初始化定時器
ble_npl_callout_init(&blehr_tx_timer, nimble_port_get_dflt_eventq(),
blehr_tx_hrate, NULL);
ble_npl_callout_init(&my_notify_tx_timer,nimble_port_get_dflt_eventq(),
my_notify_tx,NULL);
//初始化gatt服務(wù)
rc = gatt_svr_init();
assert(rc == 0);
//設(shè)備默認(rèn)名字
//這里可以更改設(shè)備名字
rc = ble_svc_gap_device_name_set(device_name);
我在DEVICE_INFO Service里添加了String(0x2A3D)和0X5A5D 的自定義characteristic;
屬性分別為READ和NOTIFY
{
/*Characteristic:Model number string */
.uuid=BLE_UUID16_DECLARE(GATT_STRING_UUID),
.access_cb=gatt_svr_chr_access_device_info,
.flags=BLE_GATT_CHR_F_READ,
},
{
/*Characteristic:Model number string */
.uuid=BLE_UUID16_DECLARE(GATT_RIGHT_UUID),
.access_cb=gatt_svr_chr_access_device_info,
.val_handle = &my_notify_handle,
.flags=BLE_GATT_CHR_F_NOTIFY,
},
給2A3D的value為:"ab32vg1";
在blehr中寫了(0x5A5D)notify的實現(xiàn);
代碼如下:
//my notify--
static void my_notify_stop(void)
{
ble_npl_callout_stop(&my_notify_tx_timer);
}
//重置
static void my_notify_reset(void)
{
int rc;
rc = ble_npl_callout_reset(&my_notify_tx_timer, RT_TICK_PER_SECOND);
assert(rc == 0);
}
static void my_notify_tx(struct ble_npl_event* ev2)
{
static char my_notify_hrm[2];
int rc;
struct os_mbuf *om;
if (!notify_state) {
my_notify_stop();
XXX = 15;
return;
}
my_notify_hrm[0] = 0x02; /* contact of a sensor *///傳感器的聯(lián)系
my_notify_hrm[1] = XXX; /* storing dummy data *///儲存虛擬數(shù)據(jù)
/* Simulation of heart beats *///模擬心跳
XXX++;
if (XXX == 50) {
XXX = 90;
}
om = ble_hs_mbuf_from_flat(my_notify_hrm, sizeof(my_notify_hrm));
rc = ble_gattc_notify_custom(my_notify_conn_handle, my_notify_handle, om);
assert(rc == 0);
my_notify_reset();
}
在blehr的gap事件函數(shù)中設(shè)置notify事件:
case BLE_GAP_EVENT_SUBSCRIBE:
//對等方訂閱狀態(tài)的狀態(tài)更改1?2:3
MODLOG_DFLT(INFO, "subscribe event; cur_notify=%d\n value handle; "
,"val_handle2=%d\n",
event->subscribe.cur_notify, my_notify_handle);
if (event->subscribe.attr_handle !=0) {
if (event->subscribe.attr_handle==my_notify_handle)
{
notify_state = event->subscribe.cur_notify;
my_notify_conn_handle = event->subscribe.conn_handle;
my_notify_reset();
}
} else if (event->subscribe.attr_handle != (&my_notify_handle)) {
notify_state = event->subscribe.cur_notify;
notify_conn_handle = 0;
my_notify_conn_handle=0;
my_notify_stop();
}
break;
最后進(jìn)行定時器的初始化:
ble_npl_callout_init(&my_notify_tx_timer,nimble_port_get_dflt_eventq(),my_notify_tx,NULL);
現(xiàn)在進(jìn)行測試:
開發(fā)板程序運(yùn)行正常!
現(xiàn)在展示小程序的代碼:
wxml:
<view class="content">
<text>\ntext>
<view class="p2" bindtap="getBluetoothDevices">
<text style="margin-left:36px">可用設(shè)備text>
view>
<block wx:if="{{show_available_devices_switch === 0}}">
<block wx:for="{{devices}}">
<view>
<text style="margin-left:36px" class="p3" id="{{item.deviceId}}" data-device-name="{{item.name}}" bindtap="connectTO">{{item.name}}text>
view>
block>
block>
<block wx:if="{{connected_device_switch === 0}}">
<view>
<text class="p3" style="margin-left:36px">{{device_name}} 已連接text>
view>
block>
<text>\n\n\ntext>
<view>
<text class="p2" style="margin-left:36px">接收字符串 text>
view>
<view>
<text class="p3" style="margin-left:36px">{{cur_string}}《-text>
view>
<text>\ntext>
<view>
<text class="p2" style="margin-left:36px">接收訂閱text>
view>
<view>
<text class="p3" style="margin-left:36px">{{cur_notify}}《-text>
view>
<text>\ntext>
wxss:
page {
color: #333;
}
.devices_summary {
margin-top: 30px;
padding: 10px;
font-size: 16px;
}
.device_list {
height: 300px;
margin: 50px 5px;
margin-top: 0;
border: 1px solid #EEE;
border-radius: 5px;
width: auto;
}
.device_item {
border-bottom: 1px solid #EEE;
padding: 10px;
color: #666;
}
.device_item_hover {
background-color: rgba(0, 0, 0, .1);
}
.connected_info {
position: fixed;
bottom: 0;
width: 100%;
background-color: #F0F0F0;
padding: 10px;
padding-bottom: 20px;
margin-bottom: env(safe-area-inset-bottom);
font-size: 14px;
min-height: 100px;
box-shadow: 0px 0px 3px 0px;
}
.connected_info .operation {
position: absolute;
display: inline-block;
right: 30px;
}
.p1 {
position: relative;
left: 36px;
}
.p2 {
font-size: 1.3em;
font-weight: 500;
font-family: 'Times New Roman', Times, serif;
}
.p3 {
font-weight: 400;
color: rgb(171, 171, 171);
}
.p4 {
position: relative;
right: 36px;
}
.p5 {
font-family: SimSun;
}
js后端:
// 初始化藍(lán)牙適配器
this.initBluetoothAdapter();
// 本機(jī)藍(lán)牙適配器狀態(tài)
this.getBluetoothAdapterState();
// 開始搜索外圍設(shè)備
this.startBluetoothDevicesDiscovery();
/* 獲取在藍(lán)牙模塊生效期間所有已發(fā)現(xiàn)的藍(lán)牙設(shè)備。包括已經(jīng)和本機(jī)處于連接狀態(tài)的設(shè)備。*/
getBluetoothDevices: function() {
var that = this;
wx.getBluetoothDevices({
success: function(res) {
console.log('搜到的藍(lán)牙設(shè)備數(shù)目:' + res.devices.length);
console.log(res.devices);
that.setData({
devices: res.devices,
/* 顯示可用設(shè)備開關(guān) */
show_available_devices_switch: 0,
});
},
}
...
具體代碼在我的評論區(qū)
(6.94 MB)下載
尋游記: 這里是代碼:https://gitee.com/YYYYYao/ab32-vg1-ble.git
回復(fù)