学习笔记之iBeacon应用


概述

iBeacon技术基于BLE,它的特点是通过广播对外发送消息,目前主要用来做室内定位和营销信息推送(微信摇一摇周边),实际上iBeacon基站通过蓝牙的广播频道不断向外发送位置信息,也就是说iBeacon并不推送消息,而只是用于定位,推送消息的功能必须由App来完成。iBeacon无需配对,因为它是采用蓝牙的广播频道传送信号向周围发送自己特有的ID,接收到该ID的应用软件会根据该ID采取一些行动。


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的UUID和BLE的Service、Characteristic、Descriptor的UUID是没关系,iBeacon的UUID是广播的时候发出,是由Apple自己定义的标准,而Service、Characteristic、Descriptor必须是连上BLE终端后才得到,是BLE标准。

效果图:搜索期间iBeacon设备与手机的远近不断修改Rssi、TxPower、Distance



权限

在经典蓝牙需要的权限基础上再加一个

 <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

这里的true标示手机如果不支持低功耗蓝牙就直接不让安装,但是如果想让你的app提供给那些不支持BLE的设备,需要在manifest中包括上面代码并设置required="false",然后在运行时可以通过使用getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)确定BLE的可用性。

打开蓝牙

打开方法与经典蓝牙打开方式相同,区别在于BLE通过getSystemService(Context.BLUETOOTH_SERVICE)得到BluetoothManager(API level 18),BluetoothManager.getAdapter()得到BluetoothAdapter。

发现/搜索BLE设备

通过调用BluetoothAdapter的startLeScan(BluetoothAdapter.LeScanCallback)搜索BLE设备。调用此方法时需要传入 BluetoothAdapter.LeScanCallback参数。因此你需要实现 BluetoothAdapter.LeScanCallback接口,BLE设备的搜索结果将通过这个callback返回。如果你只需要搜索指定UUID的外设,你可以调用 startLeScan(UUID[], BluetoothAdapter.LeScanCallback)方法。其中UUID数组指定你的应用程序所支持的GATT Services的UUID。stopLeScan(BluetoothAdapter.LeScanCallback)停止搜索BLE设备,由于搜索需要尽量减少功耗,因此在实际使用时需要注意。

onLeScan 方法在Android 5.0以下及Android 5.0及以上所运行的线程不同。

@Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
if (Looper.myLooper() == Looper.getMainLooper()) {// Android 5.0 及以上
addData(device, rssi, scanRecord);
} else {
runOnUiThread(new Runnable() {// Android 5.0 以下
@Override
public void run() {
addData(device, rssi, scanRecord);
}
});
}
}

处理发现的BLE设备,并展示在UI上

private void addData(BluetoothDevice device, int rssi, byte[] scanRecord) {
if (device != null) {
BleBuletoothDeviceBean mdb = IbeaconUtils.fromScanData(new BleBuletoothDeviceBean(), device, rssi, scanRecord);
if (mainList.size() <= 0) {
mainList.add(mdb);
// addTempData(mdb.getDevice());
lvAdapter.notifyDataSetChanged();
return;
}
for (int i = 0; i < mainList.size(); i++) {
if (!mdb.getDevice().getAddress().equals(mainList.get(i).getDevice().getAddress())) {
mainList.add(mdb);
} else {
if (mainList.get(i).getType() == 1) {
if (!mdb.getDistance().equals(mainList.get(i).getDistance())) {
mainList.get(i).setDistance(mdb.getDistance());
}
if (mdb.getRssi() != mainList.get(i).getRssi()) {
mainList.get(i).setRssi(mdb.getRssi());
}
if (mdb.getTxPower() != mainList.get(i).getTxPower()) {
mainList.get(i).setTxPower(mdb.getTxPower());
}
}

}
}
Log.e(TAG, "" + mainList.size());
lvAdapter.notifyDataSetChanged();

}
}
封装的ibeacon实体类

public class BleBuletoothDeviceBean {
private int major;//相当于群组号,同一个组里Beacon有相同的Major
private int minor;//相当于识别群组里单个的Beacon
private String proximityUuid = "";//ibeacon厂商识别号
private int txPower;//测量设备(手机)离iBeacon的距离值
private int rssi;//ibeacon信号强度
private String distance = "";//换算的距离(米)
private BluetoothDevice device;//BLE设备
private int type;
// get、set方法就不贴出来了
}


根据得到的scanData解析是否为ibeacon

public class IbeaconUtils {
/**
*
* @param mdb 封装的实体类(根据需求替换)
* @param device
* @param rssi
* @param scanData
* @return
*/
public static BleBuletoothDeviceBean fromScanData(BleBuletoothDeviceBean mdb,
BluetoothDevice device, int rssi, byte[] scanData) {
mdb.setDevice(device);
int startByte = 2;
boolean patternFound = false;
while (startByte <= 5) {
if (((int) scanData[startByte + 2] & 0xff) == 0x02 && ((int) scanData[startByte + 3] & 0xff) == 0x15) {
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) {

} 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) {
}
startByte++;
}
if (patternFound == false) {
mdb.setType(0);
return mdb;
}
byte[] proximityUuidBytes = new byte[16];
System.arraycopy(scanData, startByte + 4, proximityUuidBytes, 0, 16);
String hexString = bytesToHexString(proximityUuidBytes);
mdb.setProximityUuid(hexString);
mdb.setMajor((scanData[startByte + 20] & 0xff) * 0x100 + (scanData[startByte + 21] & 0xff));
mdb.setMinor((scanData[startByte + 22] & 0xff) * 0x100 + (scanData[startByte + 23] & 0xff));
mdb.setTxPower((int) scanData[startByte + 24]);
mdb.setRssi(rssi);
mdb.setDistance(String.format("%.2f", calculateAccuracy(mdb.getTxPower(), rssi)));
mdb.setType(1);
Log.e("iBeacon", mdb.getProximityUuid() + "," + mdb.getMajor() + "=======" + mdb.getType());
return mdb;
}
/**
* 转换十进制
*
* @param src
* @return
*/
private static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return "";
}
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();
}
/**
* 估算用户设备到ibeacon的距离
*
* @param txPower
* @param rssi
* @return
*/
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;
}
}
}

小结

项目已经上传至GitHub,项目包含经典蓝牙与BLE以及ibeacon,请自行阅读需要的模块。关于iBeacon连接,在学习笔记之低功耗蓝牙开发在中会提到。

想研究经典蓝牙可以到学习笔记之经典蓝牙开发当中阅读



智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告