android BLE Peripheral 模擬 ibeacon 發出ble 廣播

Android對外模模式(peripheral)的支持:java

從Android 5.0+開始才支持。 api level >= 21android

因此5.0 以前設備,是不能向外發送廣播的。ios

Android中心設備(central)的支持:git

從Android 4.3+ 。 api level  >= 18github

一、初始化藍牙api

二、檢查ble是否可用數組

三、開啓廣播ide

四、掃描響應數據ui

五、建立iBeacon 廣播數據this

六、廣播設置

七、開啓廣播後的回調

 

(1)初始化藍牙:

 添加權限:

 1     <uses-permission android:name="android.permission.BLUETOOTH" />
 2     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
 3     <!-- 6.0以後藍牙還須要地理位置權限 -->
 4     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 5     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 6 
 7     <!-- 自行判斷 -->
 8     <uses-feature
 9         android:name="android.hardware.bluetooth_le"
10         android:required="false" />

 初始化:

1 //初始化BluetoothManager和BluetoothAdapter
2  if (mBluetoothManager == null) {
3      mBluetoothManager = (BluetoothManager) mContext.getSystemService(BLUETOOTH_SERVICE);
4  }
5 
6  if (mBluetoothManager != null && mBluetoothAdapter == null) {
7      mBluetoothAdapter = mBluetoothManager.getAdapter();
8  }

(2)檢查是否可以使用ble:

 

 1 if (!activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
 2    Toast.makeText(activity, "不支持ble", Toast.LENGTH_LONG).show(); 4    return;
 5 }
 6 
 7 final BluetoothManager mBluetoothManager = (BluetoothManager) activity.getSystemService(BLUETOOTH_SERVICE);
 8 mBluetoothAdapter = mBluetoothManager.getAdapter();
 9 
10 if (mBluetoothAdapter == null) {
11    Toast.makeText(activity, "不支持ble", Toast.LENGTH_LONG).show();13    return;
14 }
15 // 獲取藍牙ble廣播
16 mBluetoothAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
17 if (mBluetoothAdvertiser == null) {
18    Toast.makeText(activity, "the device not support peripheral", Toast.LENGTH_SHORT).show();
19    Log.e(TAG, "the device not support peripheral");21    return;
22 }

(3)開啓廣播:

 

public void startAdvertising(MockServerCallBack callBack) {
        //獲取BluetoothLeAdvertiser,BLE發送BLE廣播用的一個API
        if (mBluetoothAdvertiser == null) {
            mBluetoothAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
        }
        if (mBluetoothAdvertiser != null) {try {
                //建立BLE beacon Advertising而且廣播
                mBluetoothAdvertiser.startAdvertising(createAdvSettings(true, 0)
                        , createIBeaconAdvertiseData(BluetoothUUID.bleServerUUID,
                                mMajor, mMinor, mTxPower)
                        , createScanAdvertiseData(mMajor, mMinor, mTxPower, mAdvCallback);
            } catch (Exception e) {
                Log.v(TAG, "Fail to setup BleService");
            }
        }
    }

一個ble廣播包:廣播數據其實包含兩部分:Advertising Data(廣播數據) 和 Scan Response Data(掃描響應數據)。

一般狀況下,廣播的一方,按照必定的間隔,往空中廣播 Advertising Data。

當某個監聽設備監聽到這個廣播數據時候,會經過發送 Scan Response Request,請求廣播方發送掃描響應數據數據。

這兩部分數據的長度都是固定的 31 字節。

在 Android 中,系統會把這兩個數據拼接在一塊兒,返回一個 62 字節的數組。

4)掃描響應數據:

能夠自定義數據,好比增長溼度,溫度等。

 1     //設置scan廣播數據
 2     public static AdvertiseData createScanAdvertiseData(short major, short minor, byte txPower) {
 3         AdvertiseData.Builder builder = new AdvertiseData.Builder();
 4         builder.setIncludeDeviceName(true);
 5 
 6         byte[] serverData = new byte[5];
 7         ByteBuffer bb = ByteBuffer.wrap(serverData);
 8         bb.order(ByteOrder.BIG_ENDIAN);
 9         bb.putShort(major);
10         bb.putShort(minor);
11         bb.put(txPower);
12         builder.addServiceData(ParcelUuid.fromString(BluetoothUUID.bleServerUUID.toString())
13                 , serverData);
14 
15         AdvertiseData adv = builder.build();
16         return adv;
17     }

五、建立ibeacon 廣播數據。

iBeacon 的廣播結構:iBeacon 只是協議.

  • the 2 byte beacon identifier (0xBEAC)
  • the 16 bytes UUID
  • the 2 byte major
  • the 2 byte minor
  • the 1 byte tx power

Byte 0-2: Standard BLE Flags

 Byte 0: Length :  0x02
 Byte 1: Type: 0x01 (Flags)
 Byte 2: Value: 0x06 (Typical Flags)

Byte 3-29: Apple Defined iBeacon Data

 Byte 3: Length: 0x1a
 Byte 4: Type: 0xff (Custom Manufacturer Packet)
 Byte 5-6: Manufacturer ID : 0x4c00 (Apple)
 Byte 7: SubType: 0x2 (iBeacon)
 Byte 8: SubType Length: 0x15
 Byte 9-24: Proximity UUID
 Byte 25-26: Major
 Byte 27-28: Minor
 Byte 29: Signal Power

ManufactureData : 設備廠商的自定義數據

使用addManufactureData(int manufacturerId, byte[] manufacturerSpecificData);

第一個參數0x004c,是廠商id,id長度爲2個字節,不足2個字節系統會補0.

(好比id傳入0xac, 不足兩個字節,輸入廣播時:ac, 00)

 1 /**
 2      * create AdvertiseDate for iBeacon
 3      */
 4     public static AdvertiseData createIBeaconAdvertiseData(UUID proximityUuid, short major, short minor, byte txPower) {
 5 
 6         String[] uuidstr = proximityUuid.toString().replaceAll("-", "").toLowerCase().split("");
 7         byte[] uuidBytes = new byte[16];
 8         for (int i = 1, x = 0; i < uuidstr.length; x++) {
 9             uuidBytes[x] = (byte) ((Integer.parseInt(uuidstr[i++], 16) << 4) | Integer.parseInt(uuidstr[i++], 16));
10         }
11         byte[] majorBytes = {(byte) (major >> 8), (byte) (major & 0xff)};
12         byte[] minorBytes = {(byte) (minor >> 8), (byte) (minor & 0xff)};
13         byte[] mPowerBytes = {txPower};
14         byte[] manufacturerData = new byte[0x17];
15         byte[] flagibeacon = {0x02, 0x15};
16 
17         System.arraycopy(flagibeacon, 0x0, manufacturerData, 0x0, 0x2);
18         System.arraycopy(uuidBytes, 0x0, manufacturerData, 0x2, 0x10);
19         System.arraycopy(majorBytes, 0x0, manufacturerData, 0x12, 0x2);
20         System.arraycopy(minorBytes, 0x0, manufacturerData, 0x14, 0x2);
21         System.arraycopy(mPowerBytes, 0x0, manufacturerData, 0x16, 0x1);
22 
23         AdvertiseData.Builder builder = new AdvertiseData.Builder();
24         builder.addManufacturerData(0x004c, manufacturerData);
25 
26         AdvertiseData adv = builder.build();
27         return adv;
28     }

六、建立廣播設置:模式,是否可鏈接,功率

 setAdvertiseMode(int advertiseMode)
         設置廣播的模式,低功耗,平衡和低延遲三種模式;
         對應  AdvertiseSettings.ADVERTISE_MODE_LOW_POWER  ,ADVERTISE_MODE_BALANCED ,ADVERTISE_MODE_LOW_LATENCY
         從左右到右,廣播的間隔會愈來愈短 
 setConnectable(boolean connectable)
          設置是否能夠鏈接。
          廣播分爲可鏈接廣播和不可鏈接廣播,通常不可鏈接廣播應用在iBeacon設備上,這樣APP沒法鏈接上iBeacon設備
 setTimeout(int timeoutMillis)
          設置廣播的最長時間,最大值爲常量AdvertiseSettings.LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;  180秒
          設爲0時,表明無時間限制會一直廣播
 setTxPowerLevel(int txPowerLevel)
          設置廣播的信號強度
          常量有AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW, ADVERTISE_TX_POWER_LOW, ADVERTISE_TX_POWER_MEDIUM, ADVERTISE_TX_POWER_HIGH 
          從左到右分別表示強度愈來愈強. 
          舉例:當設置爲ADVERTISE_TX_POWER_ULTRA_LOW時,
          手機1和手機2放在一塊兒,手機2掃描到的rssi信號強度爲-56左右,
          當設置爲ADVERTISE_TX_POWER_HIGH  時, 掃描到的信號強度爲-33左右,
          信號強度越大,表示手機和設備靠的越近

* AdvertiseSettings.ADVERTISE_TX_POWER_HIGH -56 dBm @ 1 meter with Nexus 5

* AdvertiseSettings.ADVERTISE_TX_POWER_LOW -75 dBm @ 1 meter with Nexus 5

* AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM -66 dBm @ 1 meter with Nexus 5

*AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW not detected with Nexus 5

 1 public AdvertiseSettings createAdvSettings(boolean connectable, int timeoutMillis) {
 2         AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
 3         //設置廣播的模式, 功耗相關
 4         builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED);
 5         builder.setConnectable(connectable);
 6         builder.setTimeout(timeoutMillis);
 7         builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
 8         AdvertiseSettings mAdvertiseSettings = builder.build();
 9         if (mAdvertiseSettings == null) {
10             Log.e(TAG, "mAdvertiseSettings == null");
11         }
12         return mAdvertiseSettings;
13     }

七、開始廣播後的回調。提示廣播開啓是否成功。

 

 1 //發送廣播的回調,onStartSuccess/onStartFailure很明顯的兩個Callback
 2     private AdvertiseCallback mAdvCallback = new AdvertiseCallback() {
 3         public void onStartSuccess(android.bluetooth.le.AdvertiseSettings settingsInEffect) {
 4             super.onStartSuccess(settingsInEffect);
 5             if (settingsInEffect != null) {
 6                 Log.d(TAG, "onStartSuccess TxPowerLv=" + settingsInEffect.getTxPowerLevel() + " mode=" + settingsInEffect.getMode() + " timeout=" + settingsInEffect.getTimeout());
 7             } else {
 8                 Log.d(TAG, "onStartSuccess, settingInEffect is null");
 9             }
10         }
11 
12         public void onStartFailure(int errorCode) {
13             super.onStartFailure(errorCode);
14             Log.d(TAG, "onStartFailure errorCode=" + errorCode);
15 
16             if (errorCode == ADVERTISE_FAILED_DATA_TOO_LARGE) {
17                 Toast.makeText(mContext, "advertise_failed_data_too_large", Toast.LENGTH_LONG).show();
18                 Log.e(TAG, "Failed to start advertising as the advertise data to be broadcasted is larger than 31 bytes.");
19             } else if (errorCode == ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) {
20                 Toast.makeText(mContext, "advertise_failed_too_many_advertises", Toast.LENGTH_LONG).show();
21                 Log.e(TAG, "Failed to start advertising because no advertising instance is available.");
22 
23             } else if (errorCode == ADVERTISE_FAILED_ALREADY_STARTED) {
24                 Toast.makeText(mContext, "advertise_failed_already_started", Toast.LENGTH_LONG).show();
25                 Log.e(TAG, "Failed to start advertising as the advertising is already started");
26 
27             } else if (errorCode == ADVERTISE_FAILED_INTERNAL_ERROR) {
28                 Toast.makeText(mContext, "advertise_failed_internal_error", Toast.LENGTH_LONG).show();
29                 Log.e(TAG, "Operation failed due to an internal error");
30 
31             } else if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
32                 Toast.makeText(mContext, "advertise_failed_feature_unsupported", Toast.LENGTH_LONG).show();
33                 Log.e(TAG, "This feature is not supported on this platform");
34 
35             }
36         }
37     };

 注意:對於ios 設備接受廣播,外圍設備仍是要廣播出來一個16位的serviceUUID,由於掃描的時候要用(若是不指定特定服務的UUID,沒有辦法進行後臺持續掃描鏈接).

Android 上的低功耗藍牙實踐

 

源代碼demo

相關文章
相關標籤/搜索