Android BLE学习(二): Android与51822蓝牙模块通信流程的实现与分析
http://www.itkeyword.com/doc/7802444777000507x499/android-BLE
背景
上一節介紹了藍牙Android搜索BLE模塊方法,搜索到藍牙模塊后,我們就可以進行藍牙與手機間數據的通信,也就是Android手機端與BLE模塊間實現數據的互相傳輸,本文將重點介紹手機端和藍牙端程序的編寫及其交互,從代碼和現象入手,逐步了解BLE使用。
BLE模塊代碼分析
建立一個Service
此處修改了nordic官方代碼。?
這里我們將先貼出代碼,明確代碼所要實現的功能,后面會繼續分析這些代碼是如何與藍牙協議所對應。?
首先從main函數入手,其中有很多初始化代碼,初始化協議棧和寄存器相關內容,我們并不需要過多關注,如圖,我們先看這個服務初始化函數。?
當我們需要為藍牙模塊添加一個功能時,我們需要在BLE模塊中自定義一個自己的service,并按照一定的方式進行初始化,下面是我們為當前定義的service進行的初始化代碼。
/**@brief Function for initializing services that will be used by the application. */ static void services_init(void) {uint32_t err_code;ble_nus_init_t nus_init;memset(&nus_init, 0, sizeof(nus_init));nus_init.data_handler = nus_data_handler;err_code = ble_nus_init(&m_nus, &nus_init);APP_ERROR_CHECK(err_code); }其中的?ble_nus_init_t?是服務提供的一個接口,定義了這服務的基本功能要求,我們可以根據自己的需求進行不同的具體實現nus_data_handler函數即可。
typedef struct {ble_nus_data_handler_t data_handler; /**< Event handler to be called for handling received data. */ } ble_nus_init_t;下面我們實現一下自己的 nus_data_handler,這里我們將藍牙接收到的數據直接原樣通過BLE發送到手機端。
void nus_data_handler(ble_nus_t * p_nus, uint8_t * p_data, uint16_t length) {ble_nus_send_string(&m_nus, p_data, length); }之后調用ble_nus_init函數來對我們的service進行注冊。
uint32_t ble_nus_init(ble_nus_t * p_nus, const ble_nus_init_t * p_nus_init) //參數: // ble_nus_t * p_nus 自定義服務的結構體接口 // ble_nus_init_t * p_nus_init 我們在上面定義的回調接口,存儲回調下面我們來看一下?ble_nus_t,這個接口中存儲著我們服務中包含的屬性和方法,是服務的核心。
typedef struct ble_nus_s {uint8_t uuid_type; //uuid類型uint16_t service_handle; //服務回調ble_gatts_char_handles_t tx_handles; //關聯TX characteristicble_gatts_char_handles_t rx_handles; //關聯 RX characteristicuint16_t conn_handle; //連接bool is_notification_enabled; //是否準備好 RX characteristicble_nus_data_handler_t data_handler; //處理接收到的數據回調 } ble_nus_t;ble_nus_init函數中,我們首先對這個結構體的變量進行初始化。初始化完畢后就可以使用nordic為我們提供的協議棧函數sd_ble_uuid_vs_add進行Service注冊了。這里的參數總我們為服務指定一個uuid。
現在有了服務,我們就需要為服務添加功能了,我們要使用BLE與手機進行通信,所以最基本的功能就是數據的接收與發送。
所以接下來要做的就為Service是添加兩個Characteristic,分別用作發送和接收。我們需要為這兩個Characteristic也分別指定相應的UUid。?
下面是初始化代碼
這樣就完成了藍牙Service的初始化,下面我們就可以開啟廣播。
static void advertising_start(void) {uint32_t err_code;ble_gap_adv_params_t adv_params;// Start advertisingmemset(&adv_params, 0, sizeof(adv_params));// 設置廣播數據包的信息adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND;adv_params.p_peer_addr = NULL;adv_params.fp = BLE_GAP_ADV_FP_ANY;adv_params.interval = APP_ADV_INTERVAL;adv_params.timeout = APP_ADV_TIMEOUT_IN_SECONDS;err_code = sd_ble_gap_adv_start(&adv_params);APP_ERROR_CHECK(err_code);nrf_gpio_pin_set(ADVERTISING_LED_PIN_NO); }這樣,BLE模塊端的基本功能就已經建立起來了。
通信過程簡述
有了直觀的代碼,下面我們看看芯片內部做了些什么。
廣播與連接
手機連接BLE模塊時,BLE模塊會有間隔地發送廣播數據包,每次發送完數據包后模塊會等待連接信息,當手機收到模塊的數據包后,就會發出請求,請求更多數據包,稱為掃描回應。
手機端會間隔地向BLE模塊請求數據,而在這個請求的間隔,BLE模塊就會像手機端發送數據包,數據包的大小是20字節。也就是說,我們在傳輸過程中,每次傳輸的buffer中的內容不應超過20字節。
上述過程在藍牙的GAP中定義,也就是通用訪問規范。
數據傳輸
當手機與BLE模塊建立連接后,我們就可以開始數據傳輸,這些在藍牙的GATT中定義,稱為通用屬性配置文件。
這里我們主要說說上文所建立的Service與Characteristic。
簡單地說,我們可以認為Service中包含著一組Characteristic,而Characteristic就是為我們提供了數據,我們要發送的數據都要被組裝到Characteristic中。
在此之上,一個profile中會包含一組Service,而每個Characteristic中又包含了一些屬性。屬性可以定義為屬性或者描述符,描述符是一段信息,為了給人讀的。
而這些GATT中的這些內容都有UUID,我們在用Android端讀取的時候可以通過UUID一層一層地找出我們需要的內容。而在建立這些內容時,有的UUID需要我們根據情況自己定義,有的在協議棧中給出。
上面的代碼所做的事情就是為我們自己定義一個Service,而在這個Service中我們又定義了兩個Characteristic,一個用來讀取接收到的數據,一個用于發送接收到的數據。當我們收到數據后觸發回調函數,處理我們收到的數據,發送數據則是底層調用了協議棧的sd_ble_gatts_hvx函數。
手機端發送數據時會先找到Service,再由此通過UUID拿到Characteristic,向Characteristic中寫入數據,這樣BLE模塊就可以接收到數據了。
相關推薦:Android 【藍牙4.0 BLE 低功耗可穿戴設備】 開發總結
? ? ?? ?最近在做可穿戴設備APP端的開發,之前沒有相關知識的接觸(android官方4.3版本才推出),在網上找了許久,資料也特別特別的少,無賴自己參考官方demo和
Android端代碼設計與分析
總體結構
通過上述對BLE模塊實現原理的分析,我們可以大概了解到Android端具體要做的一些工作,主要如下。上一節中我們已經了解了Android與BLE搜索,下面我們開始進行后面功能的設計。
我們為BLE連接建立一個Service(注意,這個是Android四大組件中的Service),這個服務用于在后臺維護與BLE模塊的連接及其數據傳輸和一些相關的監控,同時在需要操作BLE的Activity中建立一個廣播接受者。Service中監聽到BLE狀態改變時會通過廣播的方式傳送給我們的廣播接收者,用來改變前端頁面的狀態。
下圖為程序的基本結構
Created with Rapha?l 2.1.0 BLEActivity BLEActivity BLEService BLEService 調用Service中方法 通過內部維護的BluetoothGatt的對象完成與BLE模塊的交互 發送廣播,改變前端狀態下面先介紹幾個藍牙使用的通用類
BluetoothManager?
官網解釋
High level manager used to obtain an instance of an BluetoothAdapter and to conduct overall Bluetooth Management.
Use getSystemService(java.lang.String) with BLUETOOTH_SERVICE to create an BluetoothManager, then call getAdapter() to obtain the BluetoothAdapter.
Alternately, you can just call the static helper getDefaultAdapter().
大概是說用來獲取BluetoothAdapter用的。下面我們看一下BluetoothAdapter。
BluetoothAdapter?
看著眼熟,這不是上一張的主角嗎?具體作用看如下鏈接:?
http://blog.csdn.net/lidec/article/details/50631742
通過如下方法我們可以獲取到BluetoothGatt
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);if (device == null) {Log.w(TAG, "Device not found. Unable to connect.");return false;}// We want to directly connect to the device, so we are setting the autoConnect// parameter to false.mBluetoothGatt = device.connectGatt(this, false, mGattCallback); BluetoothGatt?
從名字上看,這個類包含Gatt,也就是前面我們提到的BLE數據傳輸的協議。咱們在BLE模塊中建立的Service和Characteristic都屬于GATT的內容。?
下面是官網的介紹
Public API for the Bluetooth GATT Profile.
This class provides Bluetooth GATT functionality to enable communication with Bluetooth Smart or Smart Ready devices.
To connect to a remote peripheral device, create a BluetoothGattCallback and call connectGatt(Context, boolean, BluetoothGattCallback) to get a instance of this class. GATT capable devices can be discovered using the Bluetooth device discovery or BLE scan process.
我們與BLE進行GATT所定義的協議就可以直接使用這個類封裝的方法。下面我們看看其中主要功能的實現。
/** * Connects to the GATT server hosted on the Bluetooth LE device. * * @param address The device address of the destination device. * * @return Return true if the connection is initiated successfully. The connection result * is reported asynchronously through the * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} * callback. */public boolean connect(final String address) {if (mBluetoothAdapter == null || address == null) {Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");return false;}// Previously connected device. Try to reconnect.if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)&& mBluetoothGatt != null) {Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");if (mBluetoothGatt.connect()) {mConnectionState = STATE_CONNECTING;return true;} else {return false;}}final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);if (device == null) {Log.w(TAG, "Device not found. Unable to connect.");return false;}// We want to directly connect to the device, so we are setting the autoConnect// parameter to false.mBluetoothGatt = device.connectGatt(this, false, mGattCallback);Log.d(TAG, "Trying to create a new connection.");mBluetoothDeviceAddress = address;mConnectionState = STATE_CONNECTING;return true;}這個方法用來連接藍牙設備,通過Mac地址從BluetoothAdapter中獲取BluetoothGatt對象,而我們定義的Android應用的Service主要功能就是維護這個對象,并使用其提供的方法與BLE模塊進行數據通信。
public void writeRXCharacteristic(byte[] value){BluetoothGattService RxService = mBluetoothGatt.getService(RX_SERVICE_UUID);showMessage("mBluetoothGatt null"+ mBluetoothGatt);if (RxService == null) {showMessage("Rx service not found!");broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);return;}BluetoothGattCharacteristic RxChar = RxService.getCharacteristic(RX_CHAR_UUID);if (RxChar == null) {showMessage("Rx charateristic not found!");broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);return;}RxChar.setValue(value);boolean status = mBluetoothGatt.writeCharacteristic(RxChar);Log.d(TAG, "write TXchar - status=" + status);}上述方法提供了操作GATT協議的基本方法,通過UUID獲取BLE的Service,再從BLE的Service中獲取用于寫的Characteristic,然后調用setValue方法,將值寫入,如果寫入成功,那么我們的BLE模塊此時就接收到數據了。
有了寫入的方法,那么我們如何讀取數據呢??
我們在獲取BluetoothGatt時給它傳入一個GattCallback對象,當收到數據時會觸發回調函數onCharacteristicChanged。
參數的characteristic就是變化的Characteristic,我們可以取出它的UUID,對比一下,如果這個UUID和我們在BLE模塊中定義的發送數據的UUID一致,則說明收到了數據,我們就可以取這個值。示例代碼如下:
if (TX_CHAR_UUID.equals(characteristic.getUuid())) {byte[] value = characteristic.getValue(); }其他的功能以此類推。
下面提供一下APP的源碼,實現其他BLE相關的功能都可以在這個APP的基礎上修改。?
http://download.csdn.net/detail/lidec/9434509
總結
本文主要梳理了51822BLE開發板與Android手機通信的方式,簡要介紹了一些實用的藍牙協議知識以,分析了協議在開發板控制程序和APP程序中的體現。
總結
以上是生活随笔為你收集整理的Android BLE学习(二): Android与51822蓝牙模块通信流程的实现与分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nrf51822-广播模式
- 下一篇: Android BLE学习(一): An