yii.
← Writing
開發筆記 · · 1 min read

Android | BLE | Beacon | 如何廣播的像iBeacon一樣?原生方式與AltBeacon套件

這篇寫於 2021 年。工具和 API 變化很快,部分步驟可能已經不適用。

持續的學習紀錄

Photo by Denny Müller on Unsplash
Photo by Denny Müller on Unsplash

封包格式#

https://os.mbed.com/blog/entry/BLE-Beacons-URIBeacon-AltBeacons-iBeacon/

020106 :0、1、2,02表示後面數據有2個字元長度,01表示類型為flag,06表示數據。

1AFF :3、4,1A表示接下來的一段數據長度是0x1A=26, 26加上020106這3個長度,再加上1A本身1個長度,剛好是協議上的30字節長度

004C:5與6,廠商ID ,表示蘋果公司。

0215:7與8,類型,表示廣播是iBeacon廣播。

可以解析BLE廣播數據包的第5, 6,7,8字節的數據來判斷這個BLE設備是不是iBeacon設備


1. Java#

    private void startAdvertise(String uuid, int major, int minor) {
        BluetoothLeAdvertiser advertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
        AdvertiseSettings settings=createAdvertiseSettings(1000);
        AdvertiseData data = createAdvertiseData( uuid ,  major, minor, -59);
        //
        advertiser.startAdvertising(settings, data, new AdvertiseCallback() {
            @Override
            public void onStartSuccess(AdvertiseSettings settingsInEffect) {
                super.onStartSuccess(settingsInEffect);
            }

            @Override
            public void onStartFailure(int errorCode) {
                super.onStartFailure(errorCode);
            }
        });
    }
    
    //---------------------------------------------------------------------------------

    public AdvertiseSettings createAdvertiseSettings(  int timeoutMillis) {
        AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
        builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);
        builder.setConnectable(true);
        builder.setTimeout(timeoutMillis);
        builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
        return builder.build();
    }

    public AdvertiseData createAdvertiseData(String uuidString, int major,
                                             int minor, int txPower) {
        AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();
        String beaconType = "0215"; 
        String uuid = uuidString.replace("-", "");
        String majorStr = formatStringLenth(4, Integer.toHexString(major), '0');
        String minorStr = formatStringLenth(4, Integer.toHexString(minor), '0');
        String measuredPower = formatStringLenth(2, Integer.toHexString(-59), '0');
	//
        String dataStr = beaconType + uuid + majorStr + minorStr + measuredPower;
	//
        byte[] data = hexStringToBytes(dataStr);
        mDataBuilder.addManufacturerData(0x004C, data);
        AdvertiseData mAdvertiseData = mDataBuilder.build();
        return mAdvertiseData;
    }
    
    //---------------------------------------------------------------------------------

   /**
     *
     * @param targetLenth 補齊後的長度
     * @param src 需要補齊的字串
     * @param leftCharacter 用來補齊長度的字元
     * @return 補齊字串
     */
    public static String formatStringLenth(int targetLenth, String src, char leftCharacter) {

        if(src.length()>targetLenth){
            return src.substring(src.length()-targetLenth);
        }else{

            int delta=targetLenth-src.length();
            for (int i = 0; i <delta ; i++) {
                src=leftCharacter+src;
            }
            return src;
        }
    }

    /**
     * 十六進制字串 轉 字元陣列
     * @param hexString "A0B102"
     * @return new byte[]{(byte) 0xA0,(byte) 0xB1,2}
     */
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        if(hexString.length()%2!=0){
            hexString="0"+hexString;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

2. AltBeacon(Library)#

Beacon beacon = new Beacon.Builder()
		.setId1(uuid)
		.setId2(major + "")
		.setId3(minor + "")
		.setManufacturer(0x004C)
		.setTxPower(-59)
		.setDataFields(Arrays.asList(new Long[]{0l})) // Remove this for beacon layouts without d: fields
		.build();
BeaconParser beaconParser = new BeaconParser()
		.setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
BeaconTransmitter beaconTransmitter = new BeaconTransmitter(MyApplication.getInstance(), beaconParser);
beaconTransmitter.setConnectable(true);
beaconTransmitter.startAdvertising(beacon, new AdvertiseCallback() {
	@Override
	public void onStartFailure(int errorCode) {
		KLog.e("Advertisement start failed with code: " + errorCode);
	}

	@Override
	public void onStartSuccess(AdvertiseSettings settingsInEffect) {
		KLog.i("Advertisement start succeeded.");
	}
});

關鍵點:#

setConnectable()需要設為true

最後:#

希望有幫助到你,可以順便給我拍手鼓勵哦 : )

參考#

  1. http://a1anwang.com/post-44.html
  2. https://os.mbed.com/blog/entry/BLE-Beacons-URIBeacon-AltBeacons-iBeacon/

本文原刊登於 Medium。