您的足迹:首页 > 蓝牙BLE >android BLE 手机模拟iBeacon发出广播

android BLE 手机模拟iBeacon发出广播


iBeacon其实就是在BLE的基础上把广播报的数据格式化了一下。

可以先看一下上一篇文章 android BLE Peripheral 手机模拟设备发出BLE广播 BluetoothLeAdvertiser

熟悉一下BLE的广播数据。

本次文章就是在上一章发出BLE广播的基础上进行改造,让手机成为iBeacon设备。


iBeacon是apple公司定义的,那么我先上一张iBeacon设备的广播协议:

Proximity Beacon Advertising Packet



这个协议规定了 BLE设备广播数据包的格式,按照这个格式进行广播的BLE设备就是一个  合格的iBeacon设备



首先我先上一段真正的 iBeacon设备广播数据包来分析一下:

scandata:0201061AFF4C000215FDA50693A4E24FB1AFCFC6EB0764782500010016C50B0945786869626974696F6E0000000000000000000000000000000000000000

我把这个广播包拆开一下:

020106  这前三个字节跟 iBeacon协议定义是一样的,02表示下面的数据有2个字节长度(0106这2个),01表示数据类型为flag,06是数据.

1AFF  第3-4个字节也是跟协议上一样,蓝牙广播格式,1A表示接下来的一段数据长度是0x1A=26, 26加上020106这3个长度,再加上1A本身1个长度,正好是上图协议上的30字节长度。 FF表示这段数据类型是厂商数据ManufacturerData。

4C00  这个对饮第5-6字节,company id,厂商id ,看过我上篇文章的同学都知道,这个其实就是ManufacturerData ID,这个顺序是倒过来的,所以跟协议上也是一样,4C00表示苹果公司,固定数据,不能变

0215 第7-8字节 跟上图协议上也是一样,Beacon Type 表示这个广播是iBeacon广播,固定字段,不能变,所以其实我们可以解析BLE广播数据包的第5,    6,7,8字节的数据来判断这个BLE设备是不是iBeacon设备

FDA50693A4E24FB1AFCFC6EB07647825  这段数据对应上图协议的第9-24字节,16个字节长度,表示uuid

0001 对应上图协议的第25-26字节,表示major,major共占2字节,所以最大值为 0xFF =65535

0016 对应上图协议的第27-28字节,表示minor,minor也占2字节,所以最大值也是65535

C5对应上图最后一个字节,是个负数 其值对应 10进制的-59,表示iBeacon距离手机1米时候的参考 信号强度(rssi)

从上面的分析可以看出,其实iBeacon就是一个BLE设备,只是其中的某些广播数据符合apple的规定


那么接下来直接基于上篇文章 上代码来实现手机模拟iBeacon设备,主要就是修改 广播数据那一块

先来一个预览图,我把uuid,major,minor等都作为可编辑,方便使用,点击“开启广播”按钮,便会广播iBeacon数据


代码如下:


 public void startAction(View v){
        if(!mBluetoothAdapter.isEnabled()){
            ToastUtils.showToast(this,"请开启蓝牙",2000);
            return;
        }
        String uuid =edit_uuid.getText().toString().toUpperCase();
        boolean isvaliduuid= isValidUUID(uuid);
        if(!isvaliduuid){
            ToastUtils.showToast(this,"UUID 格式不正确",2000);
            return;
        }
        int major = -1;
        if (!TextUtils.isEmpty(edit_major.getText().toString())) {
            major = Integer.parseInt(edit_major.getText().toString());
        }
        int minor = -1;
        if (!TextUtils.isEmpty(edit_minor.getText().toString())) {
            minor = Integer.parseInt(edit_minor.getText().toString());
        }
        if (major < 0 || major > 65535) {
            ToastUtils.showToast(this,"major ranges:0- 65535",2000);
            return;
        }
        if (minor < 0 || minor > 65535) {
            ToastUtils.showToast(this,"minor ranges:0- 65535",2000);
            return;
        }
        startAdvertise(uuid,major,minor);
    }

private void startAdvertise(String uuid, int major, int minor) {
        if (mBluetoothAdapter == null) {
            return;
        }
        if (mBluetoothLeAdvertiser == null) {
            mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
        }
        if (mBluetoothLeAdvertiser != null) {

            AdvertiseSettings settings=createAdvertiseSettings(0);
            AdvertiseData data=createAdvertiseData( uuid ,  major, minor, -59);
            //-59是 measuredPower,一般设备默认都是-59,这里固定了
            mBluetoothLeAdvertiser.startAdvertising(settings ,data  ,  mAdvCallback);
        }
    }

 public AdvertiseSettings createAdvertiseSettings(  int timeoutMillis) {
        AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
        builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);

        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"; //按照apple iBeacon协议
        String uuid=uuidString.replace("-","");
        String majorStr= Conversion.formatStringLenth(4,Integer.toHexString(major),'0');
        String minorStr=Conversion.formatStringLenth(4,Integer.toHexString(minor),'0');
        String measuredPower=Conversion.formatStringLenth(2,Integer.toHexString(txPower),'0');

        String dataStr=beaconType+uuid+majorStr+minorStr+measuredPower;

        byte[] data=Conversion.hexStringToBytes( dataStr);
        mDataBuilder.addManufacturerData(0x004C, data);//004c是厂商id,代表apple公司
        AdvertiseData mAdvertiseData = mDataBuilder.build();
        return mAdvertiseData;

    }


通过代码可以看到,其实就是在拼凑 ManufacturerData,

我先把需要广播的数据一个一个都先变成16进制字符串,然后把字符串拼起来,最后把字符串转成byte[] 

上面的Conversion.formatStringLenth(int targetLenth, String src, char leftCharacter)方法作用主要是把字符串变成指定的长度

长度不足的话,左边补齐字符, major,minor,取值范围是0-65535,共占2字节,对应4个字符串长度, 假如major是10的话,使用Integer.toHexString(major)

转成16进制字符串是A,所以需要左边补3个0。 measurePower同样道理,占1个字节,2个字符串长度,也要补齐。


OK,使用另一个手机打开 Beacon扫描工具来扫描试一下是否广播成功:

检测成功。

为了严谨,我使用apple 手机下载了一个 “Locate Beacon”,这个APP是使用 ios 中的iBeacon的api来检测周围的iBeacon设备,如果它可以检测到的话,那我们的手机模拟iBeacon无疑是成功的:


检测成功,完美!

附上模拟iBeacon源码:http://pan.baidu.com/s/1o7XhPnO








本博客所有文章如无特别注明均为原创。作者:AlanWang复制或转载请以超链接形式注明转自 AlanWang的博客-专注android和蓝牙BLE技术分享
原文地址《android BLE 手机模拟iBeacon发出广播

相关推荐

发表评论

路人甲 表情
看不清楚?点图切换 Ctrl+Enter快速提交

网友评论(0)