Android开发:IBeacon系列——安卓蓝牙4.0(BLE)开发之检测IBeacon热点初步

 

                                        检测ibeacon热点信号

 

 

软硬件要求:Android4.3及以上中支持BLE技术,同时蓝牙需要满足Bluetooth4.0及以上。

iBeacon的工作原理是基于Bluetooth Low Energy(BLE)低功耗蓝牙传输技术,iBeacon基站不断向四周发送蓝牙信号,当智能设备进入设定区域时,就能够收到信号。只要满足iBeacon技术标准的都可以使用,所以Android也能够支持iBeacon。Google在Android4.3中支持BLE技术,同时蓝牙需要满足Bluetooth4.0及以上。

定位一直是非常关键的功能。通过iBeacon基站的部署能够实现室内导航,同时通过蓝牙推送信息,iBeacon在商场零售或者一些公共服务领域如体育馆、博物馆能提供非常棒的体验。尤其是蓝牙不错传输距离、低功耗、以及信号加密使得iBeacon在移动支付领域也非常有前景。总之,iBeacon的潜力似乎是无穷大,也受到了越来越多的关注。

要了解iBeacon是如何工作首先我们要了解BLE。BLE(也称为Bluetooth Smart)最早追溯到Nokia于2006年提出的Wibree,后来融合进了蓝牙标准,成为Bluetooth4.0的一部分。目前我们经常能看到3种蓝牙设备:

  • Bluetooth:只支持传统模式的蓝牙设备
  • Bluetooth Smart Ready:支持传统和低功耗两种模式设备
  • Bluetooth Smart:只支持低功耗蓝牙设备

 

 

BLE与传统的蓝牙相比最大的优势是功耗降低90%,同时传输距离增大(超过100米)、安全和稳定性提高(支持AES加密和CRC验证)。iBeacon同时有一些自己的特点:

  • 无需配对,一般蓝牙设备印象中都需要配对工作。iBeacon无需配对,因为它是采用蓝牙的广播频道传送信号。
  • 程序可以后台唤醒,iBeacon的信息推送需要App支持。但是我们接收iBeacon信号无需打开App,只要保证安装了,同时手机蓝牙打开。

iBeacon是如何工作呢?实际上iBeacon基站通过蓝牙的广播频道不断向外发送位置信息,发送频率越快越耗电。也就是说iBeacon并不推送消息,而只是用于定位,推送消息的功能必须由App来完成。苹果定义了iBeacon 其中32位广播的数据格式。

 

 

  • UUID:厂商识别号
     
  • Major:相当于群组号,同一个组里Beacon有相同的Major
  • Minor:相当于识别群组里单个的Beacon
  • TX Power:用于测量设备离Beacon的距离

 

UUID+Major+Minor就构成了一个Beacon的识别号,有点类似于网络中的IP地址。TX Power用于测距,iBeacon目前只定义了大概的3个粗略级别:

  • 非常近(Immediate): 大概10厘米内
  • 近(Near):1米内
  • 远(Far):1米外

 下面分步骤来实现检测IBeacon热点

一、在你的主清单中AndroidManifest.xml中添加权限:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />//此权限必须要添加,否则不能正常使用此功能

 
二、检测手机是否支持蓝牙,并获取mBluetoothAdapter 对象

if (!getPackageManager().hasSystemFeature(
        PackageManager.FEATURE_BLUETOOTH_LE))
    {
      Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT)
          .show();
      finish();
    }
    final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();

    if (mBluetoothAdapter == null)
    {
      Toast.makeText(this, R.string.error_bluetooth_not_supported,
          Toast.LENGTH_SHORT).show();
      finish();
      return;
    }


三、实现LeScanCallback回调接口 

设备每次检测到一个蓝牙设备,就会回调这个接口中的onLeScan()方法,并且传入扫描到的device,rssi,scanRecord等参数。

private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback()
  {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
        byte[] scanRecord)
    {
      //在这里处理扫描到的参数
      //判断是不是IBeacon设备,做相应的处理。
    }
  };

 

四、处理扫描到的参数的方法

public class iBeaconClass
{

  static public class iBeacon
  {
    public String name;
    public int major;
    public int minor;
    public String proximityUuid;
    public String bluetoothAddress;
    public int txPower;
    public int rssi;
  }


  /**
   * 将扫描到的信息传入这个方法
   * 该方法会判断扫描到的设备是不是IBeacon
   * 如果是就返回一个IBeacon对象
   * 如果不是就返回null
   * @param device
   * @param rssi
   * @param scanData
   * @return
   */
  public static iBeacon fromScanData(BluetoothDevice device, int rssi,
      byte[] scanData)
  {

    int startByte = 2;
    boolean patternFound = false;
    while (startByte <= 5)
    {
      if (((int) scanData[startByte + 2] & 0xff) == 0x02
          && ((int) scanData[startByte + 3] & 0xff) == 0x15)
      {
        // yes! This is an iBeacon
        patternFound = true;
        break;
      } else if (((int) scanData[startByte] & 0xff) == 0x2d
          && ((int) scanData[startByte + 1] & 0xff) == 0x24
          && ((int) scanData[startByte + 2] & 0xff) == 0xbf
          && ((int) scanData[startByte + 3] & 0xff) == 0x16)
      {
        iBeacon iBeacon = new iBeacon();
        iBeacon.major = 0;
        iBeacon.minor = 0;
        iBeacon.proximityUuid = "00000000-0000-0000-0000-000000000000";
        iBeacon.txPower = -55;
        return iBeacon;
      } else if (((int) scanData[startByte] & 0xff) == 0xad
          && ((int) scanData[startByte + 1] & 0xff) == 0x77
          && ((int) scanData[startByte + 2] & 0xff) == 0x00
          && ((int) scanData[startByte + 3] & 0xff) == 0xc6)
      {

        iBeacon iBeacon = new iBeacon();
        iBeacon.major = 0;
        iBeacon.minor = 0;
        iBeacon.proximityUuid = "00000000-0000-0000-0000-000000000000";
        iBeacon.txPower = -55;
        return iBeacon;
      }
      startByte++;
    }

    if (patternFound == false)
    {
      // This is not an iBeacon
      return null;
    }

    iBeacon iBeacon = new iBeacon();

    iBeacon.major = (scanData[startByte + 20] & 0xff) * 0x100
        + (scanData[startByte + 21] & 0xff);
    iBeacon.minor = (scanData[startByte + 22] & 0xff) * 0x100
        + (scanData[startByte + 23] & 0xff);
    iBeacon.txPower = (int) scanData[startByte + 24]; // this one is signed
    iBeacon.rssi = rssi;

    // AirLocate:
    // 02 01 1a 1a ff 4c 00 02 15 # Apple's fixed iBeacon advertising prefix
    // e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 # iBeacon profile
    // uuid
    // 00 00 # major
    // 00 00 # minor
    // c5 # The 2's complement of the calibrated Tx Power

    // Estimote:
    // 02 01 1a 11 07 2d 24 bf 16
    // 394b31ba3f486415ab376e5c0f09457374696d6f7465426561636f6e00000000000000000000000000000000000000000000000000

    byte[] proximityUuidBytes = new byte[16];
    System.arraycopy(scanData, startByte + 4, proximityUuidBytes, 0, 16);
    String hexString = bytesToHexString(proximityUuidBytes);
    StringBuilder sb = new StringBuilder();
    sb.append(hexString.substring(0, 8));
    sb.append("-");
    sb.append(hexString.substring(8, 12));
    sb.append("-");
    sb.append(hexString.substring(12, 16));
    sb.append("-");
    sb.append(hexString.substring(16, 20));
    sb.append("-");
    sb.append(hexString.substring(20, 32));
    iBeacon.proximityUuid = sb.toString();

    if (device != null)
    {
      iBeacon.bluetoothAddress = device.getAddress();
      iBeacon.name = device.getName();
    }

    return iBeacon;
  }

  private static String bytesToHexString(byte[] src)
  {
    StringBuilder stringBuilder = new StringBuilder("");
    if (src == null || src.length <= 0)
    {
      return null;
    }
    for (int i = 0; i < src.length; i++)
    {
      int v = src[i] & 0xFF;
      String hv = Integer.toHexString(v);
      if (hv.length() < 2)
      {
        stringBuilder.append(0);
      }
      stringBuilder.append(hv);
    }
    return stringBuilder.toString();
  }
}

 

五、开启蓝牙

mBluetoothAdapter.enable();

 

六、开始扫描

mBluetoothAdapter.startLeScan(mLeScanCallback);

 

七、根据场强计算距离

/**
 * 根据场强计算距离
 * <p>
 * 计算公式:
 * d = 10^((abs(RSSI) - A) / (10 * n))
 * 其中:
 * d - 计算所得距离
 * RSSI - 接收信号强度(负值)
 * A - 发射端和接收端相隔1米时的信号强度
 * n - 环境衰减因子
 * <p>
 * 计算公式的代码实现:
 * - (float)calcDistByRSSI:(int)rssi
 * {
 * int iRssi = abs(rssi);
 * float power = (iRssi-59)/(10*2.0);
 * return pow(10, power);
 * }
 * <p>
 * 传入RSSI值,返回距离(单位:米)。其中,A参数赋了59,n赋了2.0。
 *
 * @param rssi 场强
 * @return double
 */
public static double calcDistByRSSI(int rssi) {
    double iRssi = abs(rssi);
    double power = ((iRssi - 59) / (10 * 2.0));
    return Math.pow(10, power);
}

步骤到此结束!

接下来,关键代码展示一下!

public class MainActivity extends Activity {

    private BluetoothAdapter mBluetoothAdapter;

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

 

        BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

        mBluetoothAdapter = bluetoothManager.getAdapter();

        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {

            Intent enableBluetooth = new Intent(

                    BluetoothAdapter.ACTION_REQUEST_ENABLE);

            startActivityForResult(enableBluetooth, 1);

        }

        mBluetoothAdapter.startLeScan(mLeScanCallback);

 

    }

 

    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

        @Override

        public void onLeScan(final BluetoothDevice device, final int rssi,

                final byte[] scanRecord) {

            int startByte = 2;

            boolean patternFound = false;

            // 寻找ibeacon

            while (startByte <= 5) {

                if (((int) scanRecord[startByte + 2] & 0xff) == 0x02 && // Identifies

                                                                        // an

                                                                        // iBeacon

                        ((int) scanRecord[startByte + 3] & 0xff) == 0x15) { // Identifies

                                                                            // correct

                                                                            // data

                                                                            // length

                    patternFound = true;

                    break;

                }

                startByte++;

            }

            // 如果找到了的话

            if (patternFound) {

                // 转换为16进制

                byte[] uuidBytes = new byte[16];

                System.arraycopy(scanRecord, startByte + 4, uuidBytes, 0, 16);

                String hexString = bytesToHex(uuidBytes);

 

                // ibeacon的UUID值

                String uuid = hexString.substring(0, 8) + "-"

                        + hexString.substring(8, 12) + "-"

                        + hexString.substring(12, 16) + "-"

                        + hexString.substring(16, 20) + "-"

                        + hexString.substring(20, 32);

 

                // ibeacon的Major值

                int major = (scanRecord[startByte + 20] & 0xff) * 0x100

                        + (scanRecord[startByte + 21] & 0xff);

 

                // ibeacon的Minor值

                int minor = (scanRecord[startByte + 22] & 0xff) * 0x100

                        + (scanRecord[startByte + 23] & 0xff);

 

                String ibeaconName = device.getName();

                String mac = device.getAddress();

                int txPower = (scanRecord[startByte + 24]);

                Log.d("BLE",bytesToHex(scanRecord));

                Log.d("BLE", "Name:" + ibeaconName + "\nMac:" + mac

                        + " \nUUID:" + uuid + "\nMajor:" + major + "\nMinor:"

                        + minor + "\nTxPower:" + txPower + "\nrssi:" + rssi);

 

                Log.d("BLE","distance:"+calculateAccuracy(txPower,rssi));

            }

        }

    };

    static final char[] hexArray = "0123456789ABCDEF".toCharArray();

 

    private static String bytesToHex(byte[] bytes) {

        char[] hexChars = new char[bytes.length * 2];

        for (int j = 0; j < bytes.length; j++) {

            int v = bytes[j] & 0xFF;

            hexChars[j * 2] = hexArray[v >>> 4];

            hexChars[j * 2 + 1] = hexArray[v & 0x0F];

        }

        return new String(hexChars);

    }

 

    protected static double calculateAccuracy(int txPower, double rssi) {

        if (rssi == 0) {

            return -1.0; // if we cannot determine accuracy, return -1.

        }

 

        double ratio = rssi * 1.0 / txPower;

        if (ratio < 1.0) {

            return Math.pow(ratio, 10);

        } else {

            double accuracy = (0.89976) * Math.pow(ratio, 7.7095) + 0.111;

            return accuracy;

        }

    }

}

 

 

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值