BLE分為三部分Service、Characteristic、Descriptor,這三部分都由UUID作為唯一標示符。一個藍牙4.0的終端可以包含多個Service,一個Service可以包含多個Characteristic,一個Characteristic包含一個Value和多個Descriptor,一個Descriptor包含一個Value。一般來說,Characteristic是手機與BLE終端交換數據的關鍵,Characteristic有較多的跟權限相關的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY,本文所用的BLE藍牙模塊竟然沒有標準的Characteristic的PERMISSION。Characteristic的PROPERTY可以通過位運算符組合來設置讀寫屬性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此讀取PROPERTY后要分解成所用的組合(本文代碼已含此分解方法)。
處理Service發送過來的各種時間.
ACTION_GATT_CONNECTED: 連接上了一個GATT服務.
ACTION_GATT_DISCONNECTED: 斷開了一個GATT服務.
ACTION_GATT_SERVICES_DISCOVERED: 發現了GATT服務.
ACTION_DATA_AVAILABLE: 從設備接收到數據. 這里可能是一個讀取或者通知操作的結果。
BluetoothAdapter.ACTION_STATE_CHANGED 藍牙狀態值發生改變
BluetoothAdapter.ACTION_SCAN_MODE_CHANGED 藍牙掃描狀態(SCAN_MODE)發生改變
BluetoothAdapter.ACTION_DISCOVERY_STARTED 藍牙掃描過程開始
BluetoothAdapter.ACTION_DISCOVERY_FINISHED 藍牙掃描過程結束
BluetoothAdapter. ACTION_LOCAL_NAME_CHANGED 藍牙設備Name發生改變
BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE 請求用戶選擇是否使該藍牙能被掃描
PS:如果藍牙沒有開啟,用戶點擊確定后,會首先開啟藍牙,繼而設置藍牙能被掃描。
BluetoothAdapter. ACTION_REQUEST_ENABLE 請求用戶選擇是否打開藍牙
BluetoothDevice.ACTION_FOUND (該常量字段位于BluetoothDevice類中,稍后講到)
說明:藍牙掃描時,掃描到任一遠程藍牙設備時,會發送此廣播。
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
mConnected = true;
updateConnectionState(R.string.connected);
invalidateOptionsMenu();
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
mConnected = false;
updateConnectionState(R.string.disconnected);
invalidateOptionsMenu();
clearUI();
} else if (BluetoothLeService.
ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
// 顯示所有支持的service和characteristic。
displayGattServices(mBluetoothLeService.getSupportedGattServices());
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
}else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
addBandDevices(device);
} else {
addUnbondDevices(device);
}
}
}
};
1、BluetoothAdapter STATE 狀態值 , 即開關狀態
int STATE_OFF 藍牙已經關閉
int STATE_ON 藍牙已經打開
int STATE_TURNING_OFF 藍牙處于關閉過程中 ,關閉ing
int STATE_TURNING_ON 藍牙處于打開過程中 ,打開ing
2、在這里首先要了解對藍牙操作一個核心類BluetoothAdapter
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
//直接打開系統的藍牙設置面板
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, 0x1);
//直接打開藍牙
adapter.enable();
//關閉藍牙
adapter.disable();
//打開本機的藍牙發現功能(默認打開120秒,可以將時間最多延長至300秒)
Intent discoveryIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);//設置持續時間(最多300秒)
3、搜索藍牙設備
使用BluetoothAdapter的startDiscovery()方法來搜索藍牙設備
startDiscovery()方法是一個異步方法,調用后會立即返回。該方法會進行對其他藍牙設備的搜索,該過程會持續12秒。該方法調用后,搜索過程實際上是在一個System Service中進行的,所以可以調用cancelDiscovery()方法來停止搜索(該方法可以在未執行discovery請求時調用)。
請求Discovery后,系統開始搜索藍牙設備,在這個過程中,系統會發送以下三個廣播:
ACTION_DISCOVERY_START:開始搜索
ACTION_DISCOVERY_FINISHED:搜索結束
ACTION_FOUND:找到設備,這個Intent中包含兩個extra fields:EXTRA_DEVICE和EXTRA_CLASS,分別包含BluetooDevice和BluetoothClass。
GATT層中定義的所有屬性都有一個UUID值,UUID是全球唯一的128位的號碼,它用來識別不同的特性。
首先來說明一下含義:
GATT(Generic Attribute Profile),通用屬性配置文件,其中的數據都是實際發送的,也就是藍牙事件所產生的協議棧事件都是在這里發生的。
UUID(Universally Unique Identifier),通用唯一識別碼。
UUID一般可以分為兩種:1、藍牙技術聯盟UUIDs;2、供應商特定的UUID
1、藍牙技術聯盟UUIDs
藍牙核心規范制定了兩種不同的UUID,1、基本的UUID;2、代替基本UUID的16位UUID。
注意:所有的藍牙技術聯盟定義UUID共用了一個基本的UUID:0x0000xxxx-0000-1000-8000-00805F9B34FB。總共128位,換算成8位位組(octet)也就是16個8位位組(8*16=128嘛)。為了進一步簡化基本UUID,每一個藍牙技術聯盟定義的屬性有一個唯一的16位UUID,以代替上面的基本UUID的‘x’部分,也就是第12、13個八位位組。
2、供應商特定的UUID
與藍牙技術聯盟定義的UUID類似,供應商特定的UUID也有基本UUID和16位的UUID(類似一個別名,再加載在基本UUID之上)。基本UUID由nRFgo Studio產生,16位UUID可以按照自己的意圖來任意分配。
因此,按照上述原則,nRF51822的SDK關于UUID的數據結構如下所示:
/** @brief 128 bit UUID values. */
typedef struct
{
unsigned char uuid128[16];
} ble_uuid128_t;
/** @brief Bluetooth Low Energy UUID type, encapsulates both 16-bit and 128-bit UUIDs. */
typedef struct
{
uint16_t uuid; /**< 16-bit UUID value or octets 12-13 of 128-bit UUID. */
uint8_t type; /**< UUID type, see @ref BLE_UUID_TYPES. If type is BLE_UUID_TYPE_UNKNOWN, the value of uuid is undefined. */
} ble_uuid_t;
結構體ble_uuid128_t內部只有一個結構體成員,其中結構體成員為一個包含16個無符號字符型元素的一維數組,也就是16個8位位組,剛好能夠表示128位UUID。
按照SDK中的注釋,結構體ble_uuid_t是低功耗藍牙UUID類型,壓縮了16位和128位UUID。其中,包含兩個結構體成員,1、無符號16位整型數uuid,也就是16位UUID值或者128位UUID的第12-13個八位位組;2、無符號8位整型數type,也就是UUID類型,其值有如下三種情況:
/** @defgroup BLE_UUID_TYPES Types of UUID
* @{ */
#define BLE_UUID_TYPE_UNKNOWN 0x00 /**< Invalid UUID type. */
#define BLE_UUID_TYPE_BLE 0x01 /**< Bluetooth SIG UUID (16-bit). */
#define BLE_UUID_TYPE_VENDOR_BEGIN 0x02 /**< Vendor UUID types start at this index (128-bit). */
/** @} */
1、BLE_UUID_TYPE_UNKNOWN:不可用的UUID類型,這也與ble_uuid_t中的如果類型是BLE_UUID_TYPE_UNKNOWN,UUID值是未定義的。
2、BLE_UUID_TYPE_BLE:藍牙興趣小組的UUID。
3、BLE_UUID_TYPE_VENDOR_BEGIN:供應商UUID類型開始在這個指針(128位)。
通過如下協議棧函數可以添加一個供應商特定的UUID。
uint32_t sd_ble_uuid_vs_add| ( ble_uuid128_t const *const p_vs_uuid)
Note
Bytes 12 and 13 of the provided UUID will not be used internally, since those are always replaced by the 16-bit uuid field inble_uuid_t
Parameters
[in] p_vs_uuid Pointer to a 16-octet (128-bit) little endian Vendor Specific UUID disregarding bytes 12 and 13.
[out] p_uuid_type Pointer where the type field inble_uuid_tcorresponding to this UUID will be stored.
由注意可以知道,提供的UUID的12、13字節并不會被內部直接使用,因為他們通常被ble_uuid_t中的16位UUID位域代替。
5、手機的BLE默認有2個服務
(1)Service 通用屬性規范 00001801-0000-1000-8000-00805f9b34fb (null)
a)Characteristic 服務改變 00002a05-0000-1000-8000-00805f9b34fb
(2)Service 通用接入規范 00001800-0000-1000-8000-00805f9b34fb
a)Characteristic 設備名稱 00002a00-0000-1000-8000-00805f9b34fb
b)Characteristic 設備外觀 00002a01-0000-1000-8000-00805f9b34fb (00 00)
c)Characteristic 設備外觀 00002aa6-0000-1000-8000-00805f9b34fb (01)
注意:因為00002aa6不在定義中,因此可以認為是廠商或者用戶自定義的特征(或服務)