zoukankan      html  css  js  c++  java
  • android BLE Peripheral 模拟 ibeacon 发出ble 广播

    Android对外模模式(peripheral)的支持:

    从Android 5.0+开始才支持。 api level >= 21

    所以5.0 之前设备,是不能向外发送广播的。

    Android中心设备(central)的支持:

    从Android 4.3+ 。 api level  >= 18

    1、初始化蓝牙

    2、检查ble是否可用

    3、开启广播

    4、扫描响应数据

    5、创建iBeacon 广播数据

    6、广播设置

    7、开启广播后的回调

    (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     }

    5、创建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     }

    6、创建广播设置:模式,是否可连接,功率

     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     }

    7、开始广播后的回调。提示广播开启是否成功。

     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

  • 相关阅读:
    java.sql.SQLException: The server time zone value is unrecognized or represents more than one time zone
    MySQL
    C# 简单软件有效期注册的实现【原】
    【转】Flask and PostgreSQL on Heroku
    5个提问,详细介绍北极星指标的策略框架 | 人人都是产品经理
    DingDing CRM
    Mariadb & MySQL :: MySQL 5.7 Reference Manual :: 8.2.1.17 LIMIT Query Optimization
    IT从之“CRM”与“OA”
    maven
    中国网络安全企业100强报告
  • 原文地址:https://www.cnblogs.com/CharlesGrant/p/7155211.html
Copyright © 2011-2022 走看看