最後帶一下一種滿好用的 AD Type, 叫作 Service UUID, 是一個很值得被善用的 AD Type, 如下所示, 會有 16-bit, 32-bit 以及 128-bit 三種
128-bit UUID 是完整的 UUID, 如下所示
75BEB663-74FC-4871-9737-AD184157450E
而 32-bit 則是基於 Bluetooth base UUID 後只改變前 32 bits, 如下所示
XXXXXXXX-0000-1000-8000-00805F9B34FB
而 16-bit 則是如下所示填入 XXXX 的這 16-bit 資料的結果就是實際 UUID 的內容
0000XXXX-0000-1000-8000-00805F9B34FB
參考連結
BLE物理層
在我們討論如何發送廣告包之前,我們想談談BLE物理層。物理層負責實際通過空中發送信號。這包括實際的RF無線電。
藍牙低功耗與經典藍牙有一些相似之處。兩者都使用2.4GHz頻譜。基本速率(BR)和BLE都使用1Mbps的GFSK調製,但它們的調製指數不同。增強數據速率(EDR)使用與GFSK完全不同的調製。與LE的40個頻道相比,經典藍牙有79個頻道。通道的間隔也不同。這兩種差異使LE和Classic不同且不兼容,因此無法進行通信。雙模式無線電,如CC256x,通過切換調製參數及其運行的通道來支持LE和Classic。
藍牙的2.4GHz頻譜從2402MHz擴展到2480MHz。LE使用40個1MHz寬的通道,編號為0到39.每個通道間隔2MHz。
信道37,38和39僅用於發送廣告分組。其餘用於連接期間的數據交換。我們對這3個頻道中發生的事情感興趣,這就是我們將在這裡介紹的內容。
在BLE通告期間,BLE外圍設備一個接一個地在3個廣告信道上發送分組。掃描設備或信標的中央設備將收聽廣告數據包的這些頻道,這有助於它發現附近的設備。
信道37,38和39故意分佈在2.4GHz頻譜上。37和39是樂隊中的第一個和最後一個頻道,而38則在中間。如果任何單個廣告頻道被阻止,其他頻道可能是免費的,因為它們被相當於幾MHz的帶寬分開。
這尤其正確,因為大多數干擾BLE的其他設備都是窄帶。特別是通道38放置在Wi-Fi信道1和6之間,因此它避免了Wi-Fi信號。廣告頻道的寬間距有助於BLE更好地管理來自Wi-Fi,經典藍牙,微波爐,嬰兒監視器等的干擾,以確保廣告成功。
Android 蓝牙 BLE 开发笔记Android 蓝牙 BLE 开发笔记
最近公司头戴换了一块蓝牙4.0 BLE模块,所以我们Android组要适配 BLE。
Android BLE 需要 4.3 以上系统,api 还是非常简单的, 第一步就是扫描, 扫描到设备后就可以连接了,
连接成功后在 onServicesDiscovered 中拿到 Service Characteristic Descriptor 就可以了。
不过还是遇到了几个小坑
第一: setCharacteristicNotification 时 onCharacteristicChanged 中收不到回调 原因是 BLE 不能连续执行写操作,必须等前一个
写完了再写一下个, 所以应该维护一个队列等前一个写完了再写下一个。
启用通知
复制代码
BluetoothGattCharacteristic rxCharacteristic = uartService
.getCharacteristic(UUID.fromString(BleConst.UUID_RX_CHARACTERISTIC));
if (rxCharacteristic != null) {
BluetoothGattDescriptor rxCharacteristicDescriptor = rxCharacteristic
.getDescriptor(UUID.fromString(BleConst.UUID_RX_CHARACTERISTIC_DESCRIPTOR));
// 设置 Characteristic 可以接收通知
gatt.setCharacteristicNotification(rxCharacteristic, true);
if (rxCharacteristicDescriptor != null) {
// enable descriptor notification
rxCharacteristicDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(rxCharacteristicDescriptor);
}
}
复制代码
第二:蓝牙自动配对
自动配对就是通过反射去调用 BluetoothDevice 的 createBond 方法
Method createBondMethod = BluetoothDevice.class.getMethod("createBond");
createBondMethod.invoke(btDevice);
第三:蓝牙自动连接
配对和连接是不一样的,自动配对后还要连接上蓝牙, stackoverflow 查了一些下面的方法可以自动连接 A2DP 和 HFP 两个 profile
监听 BluetoothDevice.ACTION_BOND_STATE_CHANGED 广播,蓝牙配对成功后调用下面的方法连接上两个 profile 就行了。
复制代码
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
adapter.getProfileProxy(this, new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
try {
Log.d(TAG, "a2dp onServiceConnected");
Method connect = BluetoothA2dp.class.getDeclaredMethod("connect", BluetoothDevice.class);
connect.invoke(proxy, device);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(int profile) {
Log.d(TAG, "a2dp onServiceDisconnected");
}
}, BluetoothProfile.A2DP);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
adapter.getProfileProxy(this, new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
try {
Log.d(TAG, "hfp onServiceConnected");
Method connect = BluetoothHeadset.class.getDeclaredMethod("connect", BluetoothDevice.class);
connect.invoke(proxy, device);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(int profile) {
Log.d(TAG, "hfp onServiceDisconnected");
}
}, BluetoothProfile.HEADSET);
复制代码
目前头戴所有按键走的都是蓝牙自定义协议,本来Android是可以用 HID 标准键值的, 但由于 IOS 不支持 HID 所已也走自定义协议。
-------------------------------------------------------------------https://stackoverflow.com/questions/36918109/android-ble-list-of-discovered-characteristics-in-service-is-incomplete
Attemptingto read characteristics from a GATT service, the list is incomplete compared tothe list I get from an iOS device, or the manufacturer guide.
I amreading the characteristics as follows:
private
final
BluetoothGattCallback mGattCallback = new
BluetoothGattCallback() {
@Override
public
void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mConnectionState =STATE_CONNECTED;
mBluetoothGatt = gatt;
if (shouldStartWriting &&writeCharacteristicsQueue.peek() != null) {
mBluetoothGatt.writeCharacteristic(writeCharacteristicsQueue.poll());
} else {
EventBus.getDefault().post(new
BTGattStatusEvent(BTGattStatusEvent.Status.CONNECTED));
Log.i(TAG, "Attempting to start service discovery:" +
gatt.discoverServices());
}
} else
if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mConnectionState =STATE_DISCONNECTED;
mBluetoothGatt = null;
EventBus.getDefault().post(new
BTGattDisconnectedEvent());
}
}
@Override
// New services discovered
public
void onServicesDiscovered(BluetoothGatt gatt, int status) {
EventBus.getDefault().post(new
BTGattStatusEvent(BTGattStatusEvent.Status.DISCOVERED));
if (status == BluetoothGatt.GATT_SUCCESS) {
for (BluetoothGattService service :gatt.getServices()) {
LOGD(TAG, "Found Service " + service.getUuid().toString());
discoverCharacteristics(service);
}
gatt.readCharacteristic(readCharacteristicsQueue.poll());
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
// Result of acharacteristic read operation
public
void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
parseCharacteristic(characteristic);
LOGD(TAG, String.format("%s | %s ", characteristic.getUuid(),characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0)));//(BluetoothGattCharacteristic.FORMAT_UINT16));
if (readCharacteristicsQueue.peek() != null) {
gatt.readCharacteristic(readCharacteristicsQueue.poll());
} else {
EventBus.getDefault().post(new
BTGattStatusEvent(BTGattStatusEvent.Status.DONE_DISCOVERING));
}
}
}
private
void discoverCharacteristics(BluetoothGattService service)
{
for
(BluetoothGattCharacteristic gattCharacteristic : service.getCharacteristics())
{
LOGD(TAG,
"Discovered UUID: "
+ gattCharacteristic.getUuid());
readCharacteristicsQueue.add(gattCharacteristic);
} }
I also attempted to get this characteristic using getCharacteristic, but Ireceived a null.
Thecharacteristics found are:
Discovered UUID:
0000fff1-0000-1000-8000-00805f9b34fbDiscovered UUID:
0000fff2-0000-1000-8000-00805f9b34fbDiscovered UUID:
0000fff3-0000-1000-8000-00805f9b34fbDiscovered UUID:
0000fff4-0000-1000-8000-00805f9b34fbDiscovered UUID:
0000fff5-0000-1000-8000-00805f9b34fb
------------------------------------------------------------------------------------------------------------------
https://blog.csdn.net/mapeifan/article/details/50113669
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import com.example.bledemo.BLEService;
import com.example.bledemo.R;
importcom.example.bledemo.SampleGattAttributes;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.BluetoothGattCharacteristic;
importandroid.bluetooth.BluetoothGattService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ExpandableListView;
importandroid.widget.SimpleExpandableListAdapter;
import android.widget.TextView;
public class ScanDeviceActivity extendsActivity {
privatefinal static String TAG = ScanDeviceActivity.class
.getSimpleName();
publicstatic final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
publicstatic final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";
privateTextView mConnectionState;
privateTextView mDataField;
privateString mDeviceName;
privateString mDeviceAddress;
privateExpandableListView mGattServicesList;
//listview
下拉清單
privateBLEService mBluetoothLeService;
privateArrayList<ArrayList<BluetoothGattCharacteristic>>mGattCharacteristics = newArrayList<ArrayList<BluetoothGattCharacteristic>>();
privateboolean mConnected = false;
privateBluetoothGattCharacteristic mNotifyCharacteristic;
privatefinal String LIST_NAME = "NAME";
privatefinal String LIST_UUID = "UUID";
//Code to manage Service lifecycle.
//代碼管理服務生命週期
privatefinal ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
publicvoid onServiceConnected(ComponentName componentName,
IBinderservice) {
mBluetoothLeService= ((BLEService.LocalBinder) service)
.getService();
if(!mBluetoothLeService.initialize()) {
Log.e(TAG,"Unable to initialize Bluetooth"); //無法初始化藍牙
finish();
}
//Automatically connects to the device upon successful start-up
//initialization.自動連接到設備成功啟動初始化
mBluetoothLeService.connect(mDeviceAddress);
}
@Override
publicvoid onServiceDisconnected(ComponentName componentName) {
mBluetoothLeService= null;
}
};
//Handles various events fired by the Service.
//ACTION_GATT_CONNECTED: connected to a GATT server.
//ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
//ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
//ACTION_DATA_AVAILABLE: received data from the device. This can be a
//result of read
//or notification operations.
//處理各種事件的發射服務。
//ACTION_GATT_CONNECTED:連接到一個伺服器關貿總協定。
//ACTION_GATT_DISCONNECTED:從關貿總協定伺服器斷開連接。
//ACTION_GATT_SERVICES_DISCOVERED:關貿總協定發現服務。
//ACTION_DATA_AVAILABLE:從設備接收的資料。這可能是一個
//讀取的結果
//或通知操作。
privatefinal BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@SuppressLint("NewApi")
@Override
publicvoid onReceive(Context context, Intent intent) {
finalString action = intent.getAction();
System.out.println("action= " + action);
//連接到一個GATT server
if(BLEService.ACTION_GATT_CONNECTED.equals(action)) {
mConnected= true;
updateConnectionState(R.string.connected);
invalidateOptionsMenu();
//從GATT server斷開連接
}else if (BLEService.ACTION_GATT_DISCONNECTED
.equals(action)){
mConnected= false;
updateConnectionState(R.string.disconnected);
invalidateOptionsMenu();
clearUI();
//發現GATT services
}else if (BLEService.ACTION_GATT_SERVICES_DISCOVERED
.equals(action)){
//Show all the supported services and characteristics on the
//user interface.
displayGattServices(mBluetoothLeService
.getSupportedGattServices());
//從設備接收的資料。這可能是一個讀取的結果或通知操作。
}else if (BLEService.ACTION_DATA_AVAILABLE.equals(action)) {
displayData(intent.getStringExtra(BLEService.EXTRA_DATA));
}
}
};
//If a given GATT characteristic is selected, check for supported features.
//This sample
//demonstrates 'Read' and 'Notify' features. See
//http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for
//the complete
下拉清單的點擊事件
//list of supported characteristic features.
@SuppressLint("NewApi")
privatefinal ExpandableListView.OnChildClickListener servicesListClickListner = newExpandableListView.OnChildClickListener() {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
@SuppressLint("NewApi")
@Override
publicboolean onChildClick(ExpandableListView parent, View v,
intgroupPosition, int childPosition, long id) {
if(mGattCharacteristics != null) {
finalBluetoothGattCharacteristic characteristic = mGattCharacteristics
.get(groupPosition).get(childPosition);
finalint charaProp = characteristic.getProperties();
System.out.println("charaProp= " + charaProp + ",UUID = "
+characteristic.getUuid().toString());
Randomr = new Random();
if(characteristic.getUuid().toString()
.equals("0000fff2-0000-1000-8000-00805f9b34fb")){
inttime= 0;
while((time=r.nextInt(9))<=0){
}
Stringdata = time+","+"1,,,,,";
characteristic.setValue(data.getBytes());
mBluetoothLeService.wirteCharacteristic(characteristic);
}
if(characteristic.getUuid().toString()
.equals("0000fff1-0000-1000-8000-00805f9b34fb")){
intR = r.nextInt(255);
intG = r.nextInt(255);
intB = r.nextInt(255);
intBB = r.nextInt(100);
Stringdata = R + "," + G + "," + B + "," + BB;
while(data.length() < 18) {
data += ",";
}
System.out.println(data);
characteristic.setValue(data.getBytes());
mBluetoothLeService.wirteCharacteristic(characteristic);
}
if(characteristic.getUuid().toString()
.equals("0000fff3-0000-1000-8000-00805f9b34fb")){
intR = r.nextInt(255);
intG = r.nextInt(255);
intB = r.nextInt(255);
intBB = r.nextInt(100);
Stringdata = R + "," + G + "," + B + "," + BB;
while(data.length() < 18) {
data+= ",";
}
System.out.println("RT");
characteristic.setValue("RT".getBytes());
mBluetoothLeService.wirteCharacteristic(characteristic);
}
if(characteristic.getUuid().toString()
.equals("0000fff5-0000-1000-8000-00805f9b34fb")){
characteristic.setValue("S".getBytes());
mBluetoothLeService.wirteCharacteristic(characteristic);
System.out.println("sendS");
}else {
if((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
//If there is an active notification on a
//characteristic, clear
//it first so it doesn't update the data field on the
//user interface.
if(mNotifyCharacteristic != null) {
mBluetoothLeService.setCharacteristicNotification(
mNotifyCharacteristic,false);
mNotifyCharacteristic= null;
}
mBluetoothLeService.readCharacteristic(characteristic);
}
}
if((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
if(characteristic.getUuid().toString().equals("0000fff6-0000-1000-8000-00805f9b34fb")||characteristic.getUuid().toString().equals("0000fff4-0000-1000-8000-00805f9b34fb")){
System.out.println("enablenotification");
mNotifyCharacteristic= characteristic;
mBluetoothLeService.setCharacteristicNotification(
characteristic,true);
}
}
returntrue;
}
returnfalse;
}
};
privatevoid clearUI() {
mGattServicesList.setAdapter((SimpleExpandableListAdapter)null);
mDataField.setText(R.string.no_data);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@SuppressLint("NewApi")
@Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scandevice);
finalIntent intent = getIntent();
mDeviceName= intent.getStringExtra(EXTRAS_DEVICE_NAME);
mDeviceAddress= intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);
//Sets up UI references.設置介面的引用
((TextView)findViewById(R.id.device_address)).setText(mDeviceAddress);
mGattServicesList= (ExpandableListView) findViewById(R.id.gatt_services_list);
mGattServicesList.setOnChildClickListener(servicesListClickListner);
mConnectionState= (TextView) findViewById(R.id.connection_state);
mDataField= (TextView) findViewById(R.id.data_value);
getActionBar().setTitle(mDeviceName);
getActionBar().setDisplayHomeAsUpEnabled(true);
IntentgattServiceIntent = new Intent(this, BLEService.class);
booleanbll = bindService(gattServiceIntent, mServiceConnection,
BIND_AUTO_CREATE);
if(bll) {
System.out.println("---------------");
}else {
System.out.println("===============");
}
}
//在整個Activity的生命週期的onResume狀態時,註冊廣播 ,檢查BluetoothLeService的連結情況,如果空,則連結
@Override
protectedvoid onResume() {
super.onResume();
//
註冊廣播
registerReceiver(mGattUpdateReceiver,makeGattUpdateIntentFilter());
if(mBluetoothLeService != null) {
finalboolean result = mBluetoothLeService.connect(mDeviceAddress);
Log.d(TAG,"Connect request result=" + result);
}
}
//取消註冊廣播
@Override
protectedvoid onPause() {
super.onPause();
unregisterReceiver(mGattUpdateReceiver);
}
//解除 綁定的 service
@Override
protectedvoid onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
mBluetoothLeService= null;
}
@Override
publicboolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.gatt_services,menu);
if(mConnected) {
menu.findItem(R.id.menu_connect).setVisible(false);
menu.findItem(R.id.menu_disconnect).setVisible(true);
}else {
menu.findItem(R.id.menu_connect).setVisible(true);
menu.findItem(R.id.menu_disconnect).setVisible(false);
}
returntrue;
}
@Override
publicboolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
caseR.id.menu_connect:
mBluetoothLeService.connect(mDeviceAddress);
returntrue;
caseR.id.menu_disconnect:
mBluetoothLeService.disconnect();
returntrue;
caseandroid.R.id.home:
onBackPressed();
returntrue;
}
returnsuper.onOptionsItemSelected(item);
}
privatevoid updateConnectionState(final int resourceId) {
runOnUiThread(newRunnable() {
@Override
publicvoid run() {
mConnectionState.setText(resourceId);
}
});
}
privatevoid displayData(String data) {
if(data != null) {
mDataField.setText(data);
}
}
//Demonstrates how to iterate through the supported GATT
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
//Services/Characteristics.
//In this sample, we populate the data structure that is bound to the
//ExpandableListView
//on the UI.
/*
* 演示了如何遍歷所支援的GATT服務/特徵。在這個示例中,我們填充資料結構綁定到ExpandableListViewUI
*/
@SuppressLint("NewApi")
privatevoid displayGattServices(List<BluetoothGattService> gattServices) {
if(gattServices == null)
return;
Stringuuid = null;
StringunknownServiceString = getResources().getString(
R.string.unknown_service);
StringunknownCharaString = getResources().getString(
R.string.unknown_characteristic);
ArrayList<HashMap<String,String>> gattServiceData = new ArrayList<HashMap<String,String>>();
ArrayList<ArrayList<HashMap<String,String>>> gattCharacteristicData = newArrayList<ArrayList<HashMap<String, String>>>();
mGattCharacteristics= new ArrayList<ArrayList<BluetoothGattCharacteristic>>();
//Loops through available GATT Services.
for(BluetoothGattService gattService : gattServices) {
HashMap<String,String> currentServiceData = new HashMap<String, String>();
uuid= gattService.getUuid().toString();
currentServiceData.put(LIST_NAME,
SampleGattAttributes.lookup(uuid,unknownServiceString));
currentServiceData.put(LIST_UUID,uuid);
gattServiceData.add(currentServiceData);
ArrayList<HashMap<String,String>> gattCharacteristicGroupData = newArrayList<HashMap<String, String>>();
List<BluetoothGattCharacteristic>gattCharacteristics = gattService
.getCharacteristics();
ArrayList<BluetoothGattCharacteristic>charas = new ArrayList<BluetoothGattCharacteristic>();
//Loops through available Characteristics.
for(BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
charas.add(gattCharacteristic);
HashMap<String,String> currentCharaData = new HashMap<String, String>();
uuid= gattCharacteristic.getUuid().toString();
currentCharaData.put(LIST_NAME,
SampleGattAttributes.lookup(uuid,unknownCharaString));
currentCharaData.put(LIST_UUID,uuid);
gattCharacteristicGroupData.add(currentCharaData);
}
mGattCharacteristics.add(charas);
gattCharacteristicData.add(gattCharacteristicGroupData);
}
SimpleExpandableListAdaptergattServiceAdapter = new SimpleExpandableListAdapter(
this,gattServiceData,
android.R.layout.simple_expandable_list_item_2,new String[] {
LIST_NAME,LIST_UUID }, new int[] { android.R.id.text1,
android.R.id.text2}, gattCharacteristicData,
android.R.layout.simple_expandable_list_item_2,new String[] {
LIST_NAME,LIST_UUID }, new int[] { android.R.id.text1,
android.R.id.text2});
mGattServicesList.setAdapter(gattServiceAdapter);
}
// 廣播清單
privatestatic IntentFilter makeGattUpdateIntentFilter() {
finalIntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BLEService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BLEService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(BLEService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BLEService.ACTION_DATA_AVAILABLE);
returnintentFilter;
}
}
-------------------------------------------------------------------------------------
許可權:
<uses-permission
android:name="android.permission.BLUETOOTH"/><uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"/>
5.0以上需要
<!-- 只有當你的 targets API 等於或大於 Android 5.0 (API level 21) 才需要此許可權 --><uses-feature
android:name="android.hardware.location.gps" />
6.0以上設備需要
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"/><uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"/>
--------------------------------------------------------------------------
Bluetooth 藍芽無線通訊 隨著視聽產品的演進,耳機已成為不可或缺的需求,有線耳機總是不方便,無線耳機就應運而生,剛開始的無線耳機採用無線電頻率(Radio Frequency)來傳輸, 由於 RF 訊號屬於類比且無通訊協定,所以 RF 無線耳機在傳輸的過程中,音頻 可能斷斷續續無法連續,手機業者Ericsson 在 1994 年構思了以無線方式來取代 有線 RS-232 傳輸,以解決音訊在無線傳輸時的同步問題;透過 2.4GHz 頻率來傳 遞聲波,所有的聲波資料透過封包來傳遞,資料交換時透過 master 所產生的時脈 來傳送或接收,就好像有線的 RS-232 傳送方式,除非是藍芽晶片製造公司,讀者不需要對底層多做瞭解,只要針對其應用瞭解,在嵌入式系統內,即可發揮應用,本文也僅針對其應用加以說明。如果讀者要在 Window’s 作業系統下,擷取藍芽的資料,大部分是從 L2CAP 層以上來擷取,如果外界裝置為 HID,為呼叫 Window's 作業系統的 HID.dll 函式 庫來完成,如果外界裝置為模擬 RS-232 通訊,就利用 RFCOMM 層來連結外界藍 芽裝置,當然,如果讀者所在的作業系統非視窗作業系統,那就要看那個作業系 統對藍芽所提供的驅動程式到哪一個地步,最壞的狀況就是,驅動程是要自己寫,如藍芽晶片的製造商,那就要從 HCI Transport Layer 開始囉! 一般人聽到藍芽傳輸,通常會嚇的不知道從何著手,其實瞭解了以後,不過就是 RS-232 的傳輸罷了! 做為系統開發者,重要的是如何將這些不同的介面銜 接起來,往後將會想辦法做一些實作讀取 HID,或 RFCOMM 藍芽裝置的資料, 或是透過藍芽無線傳輸來控制這些裝置。 Victor 於加拿大
|