小弟我去年底回以前念研究所的LAB走一遭,發現學弟們在玩的iBeacons還蠻酷的說,主要是
能夠透過藍芽4.0的技術來收到該Beacon上發出的訊號 (一組UUID及Major version、Minor
version等辨識碼),藉此該Beacon就能夠當做一個識別器。因此之後也自行至網路上購買了
一組 Estimote Beacons,這是一間在波蘭的公司,三個一組是美金$100元左右,再加上運費
的話大概台幣快$4000元(老實說也蠻不便宜的說orz...)。
一、具備開發環境
由於您要跑得設備必須具備BLE的支援,因此一般的模擬器是沒辦法執行該應用的(聽說連
一般的藍芽都不支援),因此我是在Nexus 7 one平板上直接跑應用的,所以您的平板必須開啟
USB debug模式。
在這邊實在是花了很多心力,因為我的平板是one,也就是當時還沒有支援藍芽4.0,因此就
利用一APP開啟root權限,再安裝Bluetooth Low Energy Enabler APP並使用後,才有支援
到BLE(因為小弟我沒有預算再換第二版的Nexus)。但好消息的是,現今很多手機或平板已
經有支援囉!!
二、應用程式說明
在這邊的APP應用,如題只是能夠去掃出範圍內的Beacons,在這邊我也為這三個Beacons綁
定了三個身份XD。好比依據官網對於Beacons的介紹,假設您走到一間商店,您可以利用
商場開發的APP來提醒您該商店有哪些優惠商品。而小弟在這邊做的也只是幫我的Beacons
賦予他意義囉!
另外,在Android開發環境下,記得載入相關的library - estimote-sdk-preview.jar
主頁面
在這邊,主要是建立一個ListView的頁面呈現方式,而這ListView上面所建立的adapter會
賦予Beacons所代表的定義!!
而我們要搜尋這個區域內有哪些Beacons必須利用BeaconManager來操作,而BeaconManager
需要載入Region所定義的實體
參數1 => 該區域的名稱設定
參數2 => UUID代碼
參數3 => Major Version
參數4 => Minor Version
當參數2 ~ 參數4都設定為null時,表示會偵測當下所有的Beacons
因此在這邊也有定義BeaconManager會執行事件,即時偵測是否有哪些特定搜尋的Beacons
在範圍內,並且不定時的更新距離位置(據說可以偵測10m內的)
adapter.replaceWith(beacons); //定時更換為當下被偵測到的beacons實體物件
在這邊要提到這個上面的method,即當onStart內確認該設備有支援BLE及藍芽有開啟之後,
才會呼叫該method進行BeaconManager的工作!!
接下來說明一下ListADInforsAdapter改寫的地方,即增加定義Beacons的資訊改寫
bind method由getView所呼叫,在這邊可以動態的改變ListView下的item資訊
setItemInitials method是在discover event時執行的,此時會由Map beacon2item來建立beacon所
對應的ItemADInfos物件(自行定義),而怎麼針對哪個beacon給予不同的意義,則由major及
minor來區隔(因為這一組的UUID是相同的),在這邊目前是以寫死的資訊來定義。
最後,APP demo的畫面如下:
能夠透過藍芽4.0的技術來收到該Beacon上發出的訊號 (一組UUID及Major version、Minor
version等辨識碼),藉此該Beacon就能夠當做一個識別器。因此之後也自行至網路上購買了
一組 Estimote Beacons,這是一間在波蘭的公司,三個一組是美金$100元左右,再加上運費
的話大概台幣快$4000元(老實說也蠻不便宜的說orz...)。
Estimote Beacons
Estimote Beacons官網有提供開發者們利用iOS或Android等語言可自行開發一個App來識
別Beacons,如基本的即為如何得知目前的範圍內有哪些Beacons或App可偵測是否有進入
或離開特定Beacon的範圍,並且還有可能做到室內定位等等。
除了基本的範例外,也有相關的API可以參考
由於小弟只會寫基本的Android,因此在這邊就不會提到iOS的部分(因為我不會XD,也沒有
設備)
另外,當您購買了產品後,在登入官網可以看到相關您購買的Beacon資訊及設定,如下
基本的產品介紹完之後,接下來就是進入如何撰寫程式來建立與Beacon的連結應用了!
而在這邊小弟我不會提到BLE(Bluetooth low energy)的相關技術介紹(藍芽4.0的一部分),會
針對的是開發一個小小應用的分享。
官網上面的論壇也找到有人提供相關的Android版本開發的基本範例
在此,小弟就利用了裡面的範例來進行改寫(遜掉了....)
一、具備開發環境
由於您要跑得設備必須具備BLE的支援,因此一般的模擬器是沒辦法執行該應用的(聽說連
一般的藍芽都不支援),因此我是在Nexus 7 one平板上直接跑應用的,所以您的平板必須開啟
USB debug模式。
在這邊實在是花了很多心力,因為我的平板是one,也就是當時還沒有支援藍芽4.0,因此就
利用一APP開啟root權限,再安裝Bluetooth Low Energy Enabler APP並使用後,才有支援
到BLE(因為小弟我沒有預算再換第二版的Nexus)。但好消息的是,現今很多手機或平板已
經有支援囉!!
二、應用程式說明
在這邊的APP應用,如題只是能夠去掃出範圍內的Beacons,在這邊我也為這三個Beacons綁
定了三個身份XD。好比依據官網對於Beacons的介紹,假設您走到一間商店,您可以利用
商場開發的APP來提醒您該商店有哪些優惠商品。而小弟在這邊做的也只是幫我的Beacons
賦予他意義囉!
另外,在Android開發環境下,記得載入相關的library - estimote-sdk-preview.jar
主頁面
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getActionBar().setDisplayHomeAsUpEnabled(true);
//Configure device list.
adapter = new ListADInforsAdapter(this);
ListView list = (ListView) findViewById(R.id.device_list);
list.setAdapter(adapter);
list.setOnItemClickListener(createOnItemClickListener());
// Configure verbose debug logging.
L.enableDebugLogging(true);
// Configure BeaconManager.
beaconManager = new BeaconManager(this);
beaconManager.setRangingListener(new BeaconManager.RangingListener() {
//傾聽目前有哪些beacons在所定義的範圍內
@Override
public void onBeaconsDiscovered(Region region, final List<beacon> beacons) {
// Note that results are not delivered on UI thread.
runOnUiThread(new Runnable() {
@Override
public void run() {
// Note that beacons reported here are already sorted by estimated
// distance between device and beacon.
getActionBar().setSubtitle("找到" + beacons.size()+"樣優惠.");
adapter.replaceWith(beacons);
adapter.setItemInitials(beacons);
}
});
}
});
}
在這邊,主要是建立一個ListView的頁面呈現方式,而這ListView上面所建立的adapter會
賦予Beacons所代表的定義!!
而我們要搜尋這個區域內有哪些Beacons必須利用BeaconManager來操作,而BeaconManager
需要載入Region所定義的實體
private static final Region ALL_ESTIMOTE_BEACONS_REGION = new Region("rid", UUID, null, null);
參數1 => 該區域的名稱設定
參數2 => UUID代碼
參數3 => Major Version
參數4 => Minor Version
當參數2 ~ 參數4都設定為null時,表示會偵測當下所有的Beacons
因此在這邊也有定義BeaconManager會執行事件,即時偵測是否有哪些特定搜尋的Beacons
在範圍內,並且不定時的更新距離位置(據說可以偵測10m內的)
adapter.replaceWith(beacons); //定時更換為當下被偵測到的beacons實體物件
adapter.setItemInitials(beacons); //針對beacons賦予意義的初始化設定
而該頁面的Android生命週期的其它部分,如onStart()主要是會偵測該硬體是否有支援BLE
或者該設備的藍芽是否有開啟等等,這個部分都還是沿用範例原本設計的。
另外,點選item後所觸發的method,在這邊還沒有撰寫接續動作,因此就沒有另外說明。
另外,點選item後所觸發的method,在這邊還沒有撰寫接續動作,因此就沒有另外說明。
private void connectToService() {
getActionBar().setSubtitle("Scanning...");
adapter.replaceWith(Collections.<beacon>emptyList()); //初始化丟入空的beacons list
beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
@Override
public void onServiceReady() {
try {
beaconManager.startRanging(ALL_ESTIMOTE_BEACONS_REGION);
} catch (RemoteException e) {
Toast.makeText(MainActivity.this, "Cannot start ranging, something terrible happened",
Toast.LENGTH_LONG).show();
Log.e(TAG, "Cannot start ranging", e);
}
}
});
}
在這邊要提到這個上面的method,即當onStart內確認該設備有支援BLE及藍芽有開啟之後,
才會呼叫該method進行BeaconManager的工作!!
接下來說明一下ListADInforsAdapter改寫的地方,即增加定義Beacons的資訊改寫
static class ItemsView {
final TextView iNameTextView;
//final TextView iDescripeTextView;
final TextView iPriceTextView;
final TextView iDateTextView;
final ImageView iPicImageView;
public ItemsView(View view) {
iNameTextView = (TextView) view.findViewWithTag("item_name");
//iDescripeTextView = (TextView) view.findViewWithTag("item_description");
iPriceTextView = (TextView) view.findViewWithTag("item_price");
iDateTextView = (TextView) view.findViewWithTag("item_date");
iPicImageView = (ImageView) view.findViewWithTag("item_image");
}
}
private void bind(Beacon beacon, View view) {
ItemsView holder = (ItemsView) view.getTag();
ItemADInfos items = beacon2item.get(beacon);
holder.iNameTextView.setText(String.format("商品: %s (距離:%.2fm)", items.getAdname(), Utils.computeAccuracy(beacon)));
//holder.iDescripeTextView.setText(items.getAddescription());
holder.iPriceTextView.setText("特價: $"+items.getAdprice());
holder.iDateTextView.setText("活動日期:"+items.getDate());
holder.iPicImageView.setImageDrawable(rsys.getDrawable(items.getAdimageresource()));
}
public void setItemInitials(List<beacon> beacons){
for(Beacon b : beacons){
//Log.i(ListADInforsAdapter.class.getSimpleName(), ">>>>>>>>>>>>>>>>>");
ItemADInfos itemObj = null;
if(!beacon2item.containsKey(b)){
if(b.getMajor() == 37254 && b.getMinor() == 43541){ //ice
itemObj = new ItemADInfos("ASUS 14吋 X450JN (750G) 筆電", "X450...", 22900, "04/06 ~ 04/13", R.drawable.asus);
}else if(b.getMajor() == 15160 && b.getMinor() == 40811){ //mint
itemObj = new ItemADInfos("ACER Iconia B1-750 7吋四核平板16G/wifi-黑/白", "搭載Intel...", 3490, "04/06 ~ 04/13", R.drawable.acer);
}else if(b.getMajor() == 298 && b.getMinor() == 24861){ //blueberry
itemObj = new ItemADInfos("EPSON L360高速三合一原廠連續供墨印表", "機器壽命...", 4990, "04/06 ~ 04/13", R.drawable.epson);
}
beacon2item.put(b, itemObj);
}
}
}
bind method由getView所呼叫,在這邊可以動態的改變ListView下的item資訊
setItemInitials method是在discover event時執行的,此時會由Map beacon2item來建立beacon所
對應的ItemADInfos物件(自行定義),而怎麼針對哪個beacon給予不同的意義,則由major及
minor來區隔(因為這一組的UUID是相同的),在這邊目前是以寫死的資訊來定義。
最後,APP demo的畫面如下:
以下的商品圖示僅供demo使用,來自yahoo奇摩購物中心
在這邊的item會即時的更換距離的數字部分,而當您離開了某個beacon的範圍之後當下的
特定項目也會消失,直到進入可偵測的範圍內。
總結
這個範例很簡單的將原本的範例進行小小改寫,怎麼將該beacon賦予不同的意義,如利用
某個beacon安置在賣場的筆電館,則當您持著裝有APP的裝置接近筆電館時會偵測到該
beacon的訊號而得知該beacon的識別碼,此時在APP內可以以此識別碼做資料的查詢(如查
資料庫或寫死在APP的程式內)來得到訊息,也就是由beacon的識別碼配合來觸發!!
PS. 以上只是舉範例內重點或新加的method進行說明,相關完整list beacons的範例可以自行
下載github所提供的囉!!
謝謝分享
回覆刪除很棒的分享
回覆刪除但還是有許多不理解的地方
請問能提供example檔或是有詳細的解說嗎
感謝
你好~ 是否請你提供email,我再mail給你
刪除你好,很感謝你的分享,但可以提供範例或者更詳細的解說嗎?因為目前還是有很多地方想要了解更多,謝謝你。
回覆刪除已提供範例下載於文末囉
刪除真是非常感謝!!
刪除今後也會繼續關注您的分享的
: )
非常感謝你的分享!
刪除你好,想請問一下區域名稱是否會影響到接收Beacon數量?因為我設定了UUID、Major 、Minor但我無法抓到資料,想請問一下是否是這個問題。
回覆刪除自己自訂的區域名稱要影響到應該不至於!我想問題出在UUID、Major、Minor的正確性!
刪除印象中,API那邊是說自訂區域名稱需為唯一,而UUID可在官網login後設定頁面取得,Major、Minor我都設為null。這樣子的初始化,能夠偵測到的將是我自己的三個Beacons。而程式之中你再各別以Beacon的物件get Major、Minor來做區隔!
請問我買的是無錫谷雨開發套件組但我設定了自己的UUID,Major、Minor都是設定null可是沒辦法抓到耶?
回覆刪除是否從官方提供的範例下手?! 小弟我也是從example、官方API得到一點資訊
刪除不好意思我想請問在搜尋部分的問題
回覆刪除目前我的專題研究是使用apple裝置
在搜尋時 必須指定UUID才能搜尋裝置
但在您的文章中似乎表示安卓可以不指定UUID去搜尋Beacon
是指在UUID設定為null的情況下
可以同時搜尋到兩個不同UUID的裝置嗎?
參考官網API
刪除new Region(java.lang.String identifier, java.util.UUID proximityUUID, java.lang.Integer major, java.lang.Integer minor)
針對new Region的後三個參數設定為null,則在同一個identifier之下的距離範圍內可以搜尋出所有的Beacons;依序若再設第二個參數的UUID,則會找出相同UUID的裝置(印象中,我三個裝置UUID都一樣,差別在Major version、Minor version各不同)