去年七月在一個講座上第一次聽到 ibeacon 這個名詞,那時候還不是這麼熱門 google 到的第一個就是 estimote 這間公司的產品,不過因為訂購過海關還會被 NCC 攔截...所以後面就沒再繼續追蹤。不過今年跟朋友借到 estimote 的 Beacon,終於可以來測試一下
iBeacon
iBeacon 是 Apple 公司提出的一套可用於室內定位系統的協議,可以使一個智慧型手機或其他裝置在一個 iBeacon 基站的感應範圍內執行相應的命令。
使用 BLE ( Bluetooth Low Energy ) 技術,具有低功耗、低延遲、高射頻等特性
使用 BLE ( Bluetooth Low Energy ) 技術,具有低功耗、低延遲、高射頻等特性
iBeacon ID 長度是 20 bytes,包含以下三個內容:
- UUID (16 bytes)
- major number (2 bytes)
- minor number (2 bytes)
依距離可分為:
- 最近:幾公分
- 中距:幾公尺
- 遠距:大於 10 公尺
兼容設備:
- 藍牙 4.0 的 IOS 設備( iPhone4s 及以上,iPad 第三代及以上,iPad mini 第一代及以上,iPod Touch 第五代 )
- Android 4.3 及以上( 如:三星 Galaxy S3 / S4 / S4 Mini, 三星 Galaxy Note 2 / 3, HTC One, Google / LG Nexus 7 2013 version / Nexus 4 / Nexus 5, HTC Butterfly, OnePlus One )
estimote Beacon 規格
- 32-bit ARM® Cortex M0 CPU 包含加速規、溫度感測器
- 藍芽 4.0
- 最遠射程 70 公尺 ( 無障礙、干擾情況下,使用建議抓 40~50 公尺 )
測試範例一
使用官方釋出 estimote-sdk-preview.jar 進行偵測,官方範例可以點此下載
建立一個 BeaconManager 物件,並註冊 Listener
建立一個 BeaconManager 物件,並註冊 Listener
public static void Create(NotificationManager notificationMngr,
Context context, final Intent i) {
try {
currentContext = context;
// Create a beacon manager
beaconManager = new BeaconManager(currentContext);
// 設定搜尋間格時間
beaconManager.setBackgroundScanPeriod(TimeUnit.SECONDS.toMillis(1),
0);
// 偵測 beacon 進入
beaconManager.setMonitoringListener(new MonitoringListener() {
// ... close to us.
@Override
public void onEnteredRegion(Region region, List beacons) {
Log.e(ConfigUtil.TAG, "onEnteredRegion");
Log.e(ConfigUtil.TAG, "beacons: " + beacons);
if(beacons!=null && beacons.size()>0){
if(beacons.get(0).getProximityUUID().equals(ESTIMOTE_PROXIMITY_UUID)){
Log.i(ConfigUtil.TAG, "same beacon");
return;
}
ESTIMOTE_PROXIMITY_UUID = beacons.get(0).getProximityUUID();
Log.i(ConfigUtil.TAG, "ESTIMOTE_PROXIMITY_UUID:"+ESTIMOTE_PROXIMITY_UUID);
sendUpdateProximityUUID(ESTIMOTE_PROXIMITY_UUID);
}
}
// ... far away from us.
@Override
public void onExitedRegion(Region region) {
}
});
// 在 beacon 範圍中
beaconManager.setRangingListener(new BeaconManager.RangingListener() {
@Override public void onBeaconsDiscovered(Region region, final List beacons) {
Log.e(ConfigUtil.TAG, "setRangingListener");
Log.e(ConfigUtil.TAG, "Ranged beacons: " + beacons);
if(beacons!=null && beacons.size()>0){
if(beacons.get(0).getProximityUUID().equals(ESTIMOTE_PROXIMITY_UUID)){
Log.i(ConfigUtil.TAG, "same beacon");
return;
}
ESTIMOTE_PROXIMITY_UUID = beacons.get(0).getProximityUUID();
Log.i(ConfigUtil.TAG, "ESTIMOTE_PROXIMITY_UUID:"+ESTIMOTE_PROXIMITY_UUID);
sendUpdateProximityUUID(ESTIMOTE_PROXIMITY_UUID);
}
}
});
// 建立連線
beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
@Override
public void onServiceReady() {
try {
// 開使偵測
beaconManager.startRanging(ALL_ESTIMOTE_BEACONS);
beaconManager.startMonitoring(ALL_ESTIMOTE_BEACONS);
} catch (Exception e) {
Log.e(ConfigUtil.TAG, "Exception:"+e);
}
}
});
} catch (Exception e) {
Log.e(ConfigUtil.TAG, "Exception:"+e);
}
}
結束時,記得停止偵測與關閉連線
public static void stop() {
try {
beaconManager.stopRanging(ALL_ESTIMOTE_BEACONS);
beaconManager.stopMonitoring(ALL_ESTIMOTE_BEACONS);
beaconManager.disconnect();
} catch (Exception e) {
Log.e(ConfigUtil.TAG, "Exception:"+e);
}
}
藍芽關閉時,也要記得停止偵測與關閉連線,不然你會看到 XXX 已停止...
public class EstimoteReceiver extends BroadcastReceiver {
private Intent estimoteServiceIntent;
// Method called when bluetooth is turned on or off.
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR);
switch (state) {
case BluetoothAdapter.STATE_TURNING_OFF:
// When bluetooth is turning off, lets stop estimotes ranging
if (estimoteServiceIntent != null) {
//這裡會呼叫上面的 stop()
context.stopService(estimoteServiceIntent);
estimoteServiceIntent = null;
}
break;
case BluetoothAdapter.STATE_ON:
// When bluethooth is turned on, let's start estimotes monitoring
if (estimoteServiceIntent == null) {
estimoteServiceIntent = new Intent(context,
EstimoteService.class);
context.startService(estimoteServiceIntent);
}
break;
}
}
}
}
結論:使用官方釋出 estimote-sdk-preview.jar,只能用 Listener 等待接收事件,測試會出現偵測速度忽快忽慢的問題...
測試範例二
因為官方的 JAR 偵測速度忽快忽慢,所以後面又自己上網找了個 lib 取代官方的 JAR。
使用 Bluetooth LE Library,它已提供 beacon 與一般藍芽設備的辨別方式,可以省掉一些麻煩
使用 Bluetooth LE Library,它已提供 beacon 與一般藍芽設備的辨別方式,可以省掉一些麻煩
一開始先檢查一下藍芽是否開啟
private void checkBluetooth() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
// 如果裝置不支援藍芽
Toast.makeText(this, "Device doesn't support bluetooth", Toast.LENGTH_SHORT).show();
return;
}
// 如果藍芽沒有開啟
if (!mBluetoothAdapter.isEnabled()) {
// 發出一個intent去開啟藍芽,
Intent mIntentOpenBT = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(mIntentOpenBT, REQUEST_ENABLE_BT);
}
}
init
private void initServerData() {
//管理所有藍芽裝置
mDeviceStore = new BluetoothLeDeviceStore();
//管理 BluetoothManager/BluetoothManager
mBluetoothUtils = new BluetoothUtils(this);
mScanner = new BluetoothLeScanner(mLeScanCallback, mBluetoothUtils);
}
偵測到裝置後的 callback
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
synchronized(lockObj){
final BluetoothLeDevice deviceLe = new BluetoothLeDevice(device, rssi, scanRecord, System.currentTimeMillis());
mDeviceStore.addDevice(deviceLe);
final EasyObjectCursor c = mDeviceStore.getDeviceCursor();
for(int i=0; i<=1){
//是否偵測到同一個beacon
if(currentBeaconUUID.equals(iBeacon.getUUID())){
Log.d(ConfigUtil.TAG, "same beacon");
}else{
if(tv_main_msg!=null){
tv_main_msg.setText(iBeacon.getUUID());
}
break;
}
}else{
//不是距離最近的beacon
}
}else{
//not beacon device
}
}
}
}
};
開始搜尋裝置
private void startScan(){
final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn();
final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported();
mDeviceStore.clear();
mBluetoothUtils.askUserToEnableBluetoothIfNeeded();
if(mIsBluetoothOn && mIsBluetoothLePresent){
mScanner.scanLeDevice(-1, true);
}
}
關閉 app 或偵測到藍芽關閉時,記得停止掃描
private void stopScan(){
mScanner.scanLeDevice(-1, false);
}
結論:使用 Bluetooth LE Library 真的比官方釋出的 JAR 快很多,但離開 beacon 範圍再返回後 update time 跟 distance 更新有時會很慢,或要靠近 beacon 一點...
Android 5.0 使用 estimote 官方釋出 estimote-sdk-preview.jar 藍芽問題
之前測試 android 4.X 的時候沒問題,但到了 Android 5.0 不知道為什麼就出現以下問題... 不過還好換個 thread 就解決了
05-16 09:43:32.181: E/AndroidRuntime(32313): FATAL EXCEPTION: main
05-16 09:43:32.181: E/AndroidRuntime(32313): Process: com.estimote.notificationstest, PID: 32313
05-16 09:43:32.181: E/AndroidRuntime(32313): java.lang.IllegalArgumentException: This cannot be run on UI thread, starting BLE scan can be expensive
05-16 09:43:32.181: E/AndroidRuntime(32313): at com.estimote.sdk.internal.Preconditions.checkArgument(Preconditions.java:65)
05-16 09:43:32.181: E/AndroidRuntime(32313): at com.estimote.sdk.service.BeaconService.checkNotOnUiThread(BeaconService.java:502)
05-16 09:43:32.181: E/AndroidRuntime(32313): at com.estimote.sdk.service.BeaconService.access$600(BeaconService.java:61)
05-16 09:43:32.181: E/AndroidRuntime(32313): at com.estimote.sdk.service.BeaconService$InternalLeScanCallback.onLeScan(BeaconService.java:490)
05-16 09:43:32.181: E/AndroidRuntime(32313): at android.bluetooth.BluetoothAdapter$2.onScanResult(BluetoothAdapter.java:1892)
05-16 09:43:32.181: E/AndroidRuntime(32313): at android.bluetooth.le.BluetoothLeScanner$BleScanCallbackWrapper$1.run(BluetoothLeScanner.java:330)
05-16 09:43:32.181: E/AndroidRuntime(32313): at android.os.Handler.handleCallback(Handler.java:739)
05-16 09:43:32.181: E/AndroidRuntime(32313): at android.os.Handler.dispatchMessage(Handler.java:95)
05-16 09:43:32.181: E/AndroidRuntime(32313): at android.os.Looper.loop(Looper.java:211)
05-16 09:43:32.181: E/AndroidRuntime(32313): at android.app.ActivityThread.main(ActivityThread.java:5321)
05-16 09:43:32.181: E/AndroidRuntime(32313): at java.lang.reflect.Method.invoke(Native Method)
05-16 09:43:32.181: E/AndroidRuntime(32313): at java.lang.reflect.Method.invoke(Method.java:372)
05-16 09:43:32.181: E/AndroidRuntime(32313): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1016)
05-16 09:43:32.181: E/AndroidRuntime(32313): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:811)
References
=======================================================================2016/08/23 補充
※ 目前已知 "Bluetooth-LE-Library---Android" 可能會出現 Beacon UUID 缺少位數的問題,這部分 "Bluetooth-LE-Library---Android" 已經在 2015/7/15 v1.0.0 版 fix 了,舊版的可以到 Bluetooth-LE-Library---Android 下載更新,或是下載附件 IBeaconManufacturerData.java 並覆蓋 "Bluetooth LE Library\src\uk\co\alt236\bluetoothlelib\device\mfdata" 下 IBeaconManufacturerData.javaPS. 範例二中 IBeaconManufacturerData.java 已更新,可直接使用
沒有留言:
張貼留言