[智能硬件] 1、三分鍾看懂智能硬件原理——藍牙防丟器制作教程(包括手機應用)
1 什么是智能藍牙防丟器
所謂智能藍牙(Smart Bluetooth)防丟器,是采用藍牙技術專門為智能手機設計的防丟器。其工作原理主要是通過距離變化來判斷物品是否還控制在你的安全范圍。主要適用於手機、錢包、鑰匙、行李等貴重物品的防丟,也可用於防止兒童或寵物的走失 。[請看正版請百度:beautifulzzzz(看樓主博客園官方博客,享高質量生活)嘻嘻!!!]
圖 1-1 藍牙防丟器應用領域
2 藍牙防丟器的主要構造
目前比較成熟的產品一般是采用藍牙4.0技術,具有低功耗、雙向防丟、自動報警等優點。雖然市場上該類產品種類繁多、層出不窮,但其核心構成一般包括:藍牙4.0芯片、藍牙芯片輔助電路、藍牙天線、蜂鳴器、開關、電源等。
圖 2-1 藍牙防丟器構成
3 藍牙模塊的選擇
由於這是第一個智能硬件DIY篇,樓主可不想一下子把大家給嚇倒了。所以這里我們先用一個相對簡單但常用的藍牙模塊HC-05/HC-06進行DIY 。如下圖該模塊把天線、濾波電路、Soc、晶振都集成在了一起,當我們用的時候只要關注1、2、12、13、24、26這幾個引腳就能實現比較完整的藍牙通信功能,這樣就為我們制作藍牙防丟器節省了很多挑選元件、設計電路、焊接制板的功夫,是不是超贊呀?
圖 3-1 藍牙模塊1
其實還有更贊的呢!由於考慮到很多讀者在硬件方面還都是新手,初次拿到郵票邊緣式引腳的模塊會焊接不好,於是樓主又找到了一款封裝更好的藍牙模塊(其實就是把上面的模塊加一個托,然后將VCC\GND\TXD\RXD四個關鍵的引腳引出)。當我們只是想把藍牙模塊作為標簽時,只要在VCC和GND之間給它加上相應的電壓就行了;當想用它進行無線數據傳輸時,這時TXD和RXD兩個引腳就起作用了。
圖 3-2 藍牙模塊2
4 開始制作一個簡易的藍牙防丟器
上面說了這么多了,那么我們的藍牙防丟器的設計方案到底是什么樣的呢?簡單起見,咱么僅僅實現通過距離變化來判斷物品是否還控制在你的安全范圍內的具有核心功能的防丟器,對於節能功能、雙向報警功能甚至是自拍功能咱們就先不考慮了。哈哈,可能說到這里大家還是對咱們要做的防丟器一點想法都沒有,其實通過上面的鋪墊樓主有信心大家可以在一分鍾之內知道怎么完成它!
圖 4-1 簡易藍牙防丟器
相信很多看完上面圖片同學會恍然大悟——不是嘛,只要將藍牙模塊接上相應的電源模塊就能夠做成一個簡單的可以發出藍牙信號的防丟器了!對的,由於我們沒有加入復雜的通信功能,所以我們僅僅把藍牙模塊通上電做成一個藍牙標簽就可以了。但是大家不要高興太早,雖然是第一個DIY,樓主也不會這么水吧~(沒發現上面圖片中手機屏幕里的應用還是一片空白嗎?哈哈)。
5 如何找到並學習要用到的API
上面制作藍牙防丟器的硬件部分讓大家覺得沒什么挑戰性,那么接下來的東西可就有一定難度了!記得樓主當時學安卓藍牙開發的時候費了好大力氣的。這里樓主強烈建議大家着手了解安卓某個功能的應用時最好去安卓開發者社區,但是隨着Google被屏Android Developer也不能被訪問了。雖然樓主在MIT網站上又找到了一個類似的網頁,但是訪問速度不是那么流暢,為了方便,LZ挑出了和本節相關的知識幫助大家理解和運用。
圖 5-1 Android Developer頁面
6 安卓藍牙編程小知識
安卓平台支持藍牙協議棧,允許一台設備和其他設備進行無線數據交換,當大家想使用藍牙時可以通過調用相應的API實現功能。這些API提供的主要功能有:
△ 掃描搜索其他藍牙設備(Scan for other Bluetooth devices) △ 查詢本地藍牙適配器配對的藍牙設備(Query the local Bluetooth adapter for paired Bluetooth devices) △ 建立RFCOMM通道(Establish RFCOMM channels) △ 連接其他設備(Connect to other devices through service discovery) △ 與其他設備進行數據交換(Transfer data to and from other devices) △ 管理多組連接(Manage multiple connections)
當准備在應用程序中使用藍牙時,首先需要在manifest文件中包含BLUETOOTH和BLUETOOTH_ADMIN權限:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH" />
接着涉及藍牙的操作主要有:1、開啟藍牙 2、關閉藍牙 3、能被搜到 4、獲取配對設備 5、傳輸數據。由於本節只涉及到搜索周邊設備,所以配對並傳輸數據暫時不介紹。
7 安卓藍牙編程主要操作
要想通過編程操作藍牙設備,首先我們得了解一下有可供我們使用的相關類及其成員函數有哪些,如下圖:藍牙設備包括本地設備和遠程設備,本地設備對應的類為BluetoothAdapter,遠程設備對應的類為BluetoothDevice,兩者的成員函數基本相同,樓主將在接下來詳細講解。
圖 7-1 藍牙設備相關函數
7_1 打開本地藍牙設備
我們要做帶有藍牙的應用,首先要保證用戶的本地藍牙設備是打開的,否則什么都不管直接使用搜索、連接、通信等函數肯定會得到和預期不一樣的效果。但是有時候用戶為了節省電池電量會把藍牙關閉,那我們該怎么辦呢?
其實,遇到這種情況大家也不用擔心!請看下面的解決方案:其中第1行調用BluetoothAdapter的getDefaultAdapter()方法獲得本地藍牙設備,接着調用isEnabled()判斷本地藍牙設備是否被打開,如果被打開了就執行接下來的操作,否則可以發送Intent消息,請求打開本地藍牙設備,接着待用戶打開藍牙后再進入接下來的操作。
1 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 2 if (!mBluetoothAdapter.isEnabled()) { 3 Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 4 startActivityForResult(enableIntent, REQUEST_ENABLE_BT); 5 } else { 6 nextOperation(); 7 }
這時候有些同學可能要吐槽樓主了“你上面發送、請求、接着、然后說的挺輕巧,我怎么知道我發送完Intent消息后系統到底干了什么?用戶又如何打開藍牙的?應用程序又在哪里等待用戶完成打開藍牙事件,然后在哪里執行nextOperation()函數的?”哈哈,樓主知道大家動手心切啦!下面將給大家詳細介紹這一過程。
想解答大家的這些問題還得看上面代碼:其中第4行startActIvityForResult會啟動一個系統Preference Activity並將ACTION_REQUEST_ENABLE靜態常量作為其動作字符串,得到的Preference Activity如下圖:
該Activity提醒用戶是否授予權限打開本地藍牙設備,當用戶點擊“是”或者“否”的時候,該Activity將會關閉。我們可以使用onActivityResult處理程序中返回的結果代碼參數來確定是否操作成功。
正如上面介紹,當用戶點擊“是”授予藍牙權限請求后,確認請求的Activity會關閉,在下面的函數中將會收到此操作的消息,這樣我們就能很瀟灑的在第4行安全的進入接下來的操作了。
1 protected void onActivityResult(int requestCode,int resultCode,Intent data){ 2 if(requestCode==ENABLE_BLUETOOTH){ 3 if(resultCode==RESULT_OK){ 4 nextOperation(); 5 } 6 } 7 }
7_2 搜索周邊藍牙設備
上面解決了藍牙設備打開問題,接下來我們就要嘗試調用相關函數搜索周邊的藍牙設備,這樣我們就能知道我們制作的藍牙防丟器是否在我們周邊了(是不是很興奮呢?)。
這里我們要用到BluetoothAdapter的成員函數startDiscovery,該方法可以執行一個異步方式獲得周邊藍牙設備,因為是一個異步的方法所以我們不需要考慮線程被阻塞問題,整個過程大約需要12秒時間。這時我們可以注冊一個BroadcastReceiver 對象來接收查找到的藍牙設備信息,我們通過Filter來過濾ACTION_FOUND這個Intent動作以獲取每個遠程設備的詳細信息,過濾ACTION_DISCOVERY_FINISHED這個Intent動作以獲取整個藍牙搜索過程結束的信息。
1 // Register for broadcasts when a device is discovered 2 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 3 this.registerReceiver(mReceiver, filter); 4 // Register for broadcasts when discovery has finished 5 filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 6 this.registerReceiver(mReceiver, filter);
上面講的可能有點專業,下面俺用一種簡單非專業的方式向大家介紹一下:每次我們想編程搜索周邊的藍牙設備時,只要簡單調用BluetoothAdapter的成員函數startDiscovery就可以了。所謂的異步方法大家可以這樣理解——start方法就好像一個總司令告訴情報搜查員去搜查情報,然后自己繼續做自己的事,情報員去收集各種情報;這里的filter就好像總司令告訴情報搜查員,我只要ACTION_FOUND和ACTION_DISCOVERY_FINISHED信息;那么這里的mReceiver就是總司令指定的情報搜查員了。
接下來就讓咱們神奇的情報搜查員登場!如下,相信大家一看就明白了,咱們的情報搜查員會一絲不苟地將總司令下達的任務執行。這樣當他收到FOUND動作信息時就用第6~8行的方法將每個發現的藍牙設備的名字和地址存儲進一個Vector中,這樣等自己完成任務時,就能告訴總司令任務完成,所有周邊藍牙情報都在xxx向量中(嘻嘻,多么讓領導喜歡的員工呀!)。
1 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 2 @Override 3 public void onReceive(Context context, Intent intent) { 4 String action = intent.getAction(); 5 if (BluetoothDevice.ACTION_FOUND.equals(action)) { 6 BluetoothDevice device = 7 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 8 mDevicesVector.add(device.getName() + "\n" + device.getAddress()); 9 } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { 10 //... 11 } 12 } 13 };
7_3 獲取藍牙設備信號強度
到目前想必很多人已經能夠一氣呵成自己的簡單藍牙防丟器了,因為大家已經掌握了硬件部分的設計方法、擁有軟件藍牙開發打開和搜索周邊藍牙的編程技巧。但是如果心急的同學只用前面介紹的一點小知識的話,肯定設計的小東西很不盡人意(自己都不好意思給自己及格)。是不是出現了藍牙防丟器放到很遠很遠時應用程序才會報告東西已丟?如果是這樣請耐心地看完下面的介紹(也許能給你更多的啟發(⊙o⊙)哦)。
想必大家也想到了問題所在——距離沒控制好!如果我們能夠知道藍牙防丟器距離我們手機的距離那就好了,這樣我們就能設定一個范圍值,當它超出這個范圍時手機就發出丟失警告,這樣就不會出現防丟器要放很遠才能被手機察覺已丟失的問題了。要解決這個問題就要向大家介紹一下無線傳感器網絡中的RSSI了!
RSSI全名Received Signal Strength Indication,翻譯過來是接收的信號強度指示。在我們的經驗中一般離得越近信號越強,所以通過這個RSSI能夠大致估計接收點與發射點之間的距離。而在無線傳感器網絡中經常會設置多個固定的發射源(也是所謂的標簽),然后根據一定的算法來確定移動目標的空間位置信息。例如下圖左分別在A、B、C三點放置三個發射功率相同的信號源(標簽),這樣移動點D通過收集A、B、C三點的RSSI並估計距離rA、rB、rC,由於這個距離並不是完全精准,所以三個圓並不一定交於一點,大多數情況是下面交於一個公共區域的情況,而移動點D的當前位置很有可能在該區域。
圖 7-2 藍牙標簽定位
我們這里只要簡單地用到通過RSSI估計手機和防丟器之間的距離就行了,上面的定位技術相信大家也能舉一反三輕松搞定(\(^o^)/~其實沒那么簡單,哈哈)。那么在安卓編程時如何獲得RSSI呢?其實並不難,我們可以利用和獲取周邊藍牙設備的名稱和地址類似的方法獲取RSSI:
1 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 2 mDevicesVector.add(device.getName() + "\n" + device.getAddress()); 3 short rssi = intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI); 4 mRSSIVector.add(rssi);
8 着手開發我們的APP
1) 使用Eclipse創建一個安卓項目,命名為first_test,並將Activity Name命名為UI_Main,Layout Name命名為ui_main(如圖8-1所示)。
圖8-1 新建安卓項目
2) 展開res文件夾下的layout文件夾,雙擊ui_main.xml文件,選擇xml編輯模式,並將其代碼改為:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical" > 6 7 <SurfaceView 8 android:id="@+id/surfaceView" 9 android:layout_width="match_parent" 10 android:layout_height="wrap_content" 11 android:layout_weight="0.75" /> 12 13 <Button 14 android:id="@+id/button_add" 15 android:layout_width="match_parent" 16 android:layout_height="wrap_content" 17 android:text="添加藍牙防丟器" /> 18 19 <Button 20 android:id="@+id/button_start" 21 android:layout_width="match_parent" 22 android:layout_height="wrap_content" 23 android:text="開始防丟" /> 24 25 </LinearLayout>
操作說明:這里修改的ui_main.xml為應用的主界面(如圖8-2所示)。該頁面采用LinearLayout布局模式,從上到下依次為用於動態顯示藍牙信號強弱的SurfaceView控件、用於添加防丟設備的按鈕和用於開始防丟控制的按鈕。
圖 8-2 ui_main.xml效果
3) 右擊layout,依次選擇New|Android XML File新建安卓XML文件。
4) 將新建的Android XML File命名為ui_list,點擊Finish按鈕。接着同第二步將新建的ui_list.xml修改為:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical" > 6 7 <ListView 8 android:id="@+id/listView1" 9 android:layout_width="match_parent" 10 android:layout_height="wrap_content" 11 android:layout_weight="1" > 12 </ListView> 13 14 <Button 15 android:id="@+id/button_search" 16 android:layout_width="match_parent" 17 android:layout_height="wrap_content" 18 android:text="search" /> 19 20 <Button 21 android:id="@+id/button_ok" 22 android:layout_width="match_parent" 23 android:layout_height="wrap_content" 24 android:text="OK" /> 25 26 </LinearLayout>
操作說明:這里新建的ui_list.xml為搜索並添加藍牙防丟器界面。該界面同樣采用LinearLayout布局模式,包含一個用於開始搜索的按鈕、一個用於列出搜索結果的list和一個用於確認返回的OK按鈕。
5) 右擊src文件夾下的包名,依次選擇New|Class命令(如圖8-3所示)
圖 8-3 新建類
6) 新建類文件命名為My_BTS,點擊Finish按鈕。
7) 將My_BTS.java文件修改為:
1 package com.example.first_test; 2 3 import java.util.Vector; 4 5 public class My_BTS { 6 public String mName; 7 public String mAddr; 8 public Vector<Short> mRSSIVector; 9 10 public My_BTS() { 11 mName = new String(); 12 mAddr = new String(); 13 mRSSIVector = new Vector<Short>(); 14 } 15 16 public My_BTS(String name, String addr) { 17 mName = name; 18 mAddr = addr; 19 mRSSIVector = new Vector<Short>(); 20 } 21 }
操作說明:該類表示藍牙防丟器。其中mName和mAddr分別表示藍牙防丟器的名字和地址;mSSIVector用來存放一段時間檢測到該藍牙防丟器的RSSI值(之所以保留多組數據,是方便今后大家擴展)。
8) 采用同樣的方法新建一個Func_Draw.java文件,並將文件修改為:
1 package com.example.first_test; 2 3 import java.util.Vector; 4 5 import android.graphics.Canvas; 6 import android.graphics.Color; 7 import android.graphics.Paint; 8 import android.graphics.Paint.Style; 9 import android.view.SurfaceHolder; 10 11 public class Func_Draw { 12 private static Vector<Paint> mPaint = new Vector<Paint>(); 13 public static Integer times = 0;// 防丟搜索次數 14 public static float Bei = 200;// 繪制圖形時放大倍數 15 16 public static void initPaint() { 17 Paint paint0 = new Paint(); 18 paint0.setAntiAlias(true); 19 paint0.setStyle(Style.STROKE); 20 paint0.setColor(Color.RED); 21 mPaint.add(paint0); 22 Paint paint1 = new Paint(); 23 paint1.setAntiAlias(true); 24 paint1.setStyle(Style.STROKE); 25 paint1.setColor(Color.GREEN); 26 mPaint.add(paint1); 27 Paint paint2 = new Paint(); 28 paint2.setAntiAlias(true); 29 paint2.setStyle(Style.STROKE); 30 paint2.setColor(Color.BLUE); 31 mPaint.add(paint2); 32 Paint paint3 = new Paint(); 33 paint3.setAntiAlias(true); 34 paint3.setStyle(Style.STROKE); 35 paint3.setColor(Color.YELLOW); 36 mPaint.add(paint3); 37 Paint paint4 = new Paint(); 38 paint4.setAntiAlias(true); 39 paint4.setStyle(Style.STROKE); 40 paint4.setColor(Color.WHITE); 41 mPaint.add(paint4); 42 Paint paint5 = new Paint(); 43 paint5.setAntiAlias(true); 44 paint5.setStyle(Style.STROKE); 45 paint5.setColor(Color.LTGRAY); 46 mPaint.add(paint5); 47 Paint paint6 = new Paint(); 48 paint6.setAntiAlias(true); 49 paint6.setStyle(Style.STROKE); 50 paint6.setColor(Color.CYAN); 51 mPaint.add(paint6); 52 } 53 54 public static void draw(SurfaceHolder mHolder) { 55 Canvas canvas = mHolder.lockCanvas(); 56 canvas.drawRGB(0, 0, 0); 57 for (int i = 0; i < UI_Main.mBTSArrayList.size(); i++) { 58 boolean find = false; 59 short rssi = 0; 60 for (int j = 0; j < UI_Main.mFuncBT.mAddrVector.size(); j++) { 61 if (UI_Main.mBTSArrayList.get(i).mAddr 62 .equals(UI_Main.mFuncBT.mAddrVector.get(j))) { 63 find = true; 64 rssi = UI_Main.mFuncBT.mRSSIVector.get(j); 65 } 66 } 67 if (find == false) { 68 canvas.drawText( 69 times + ": NOT_FIND " 70 + UI_Main.mBTSArrayList.get(i).mName, 5, 71 i * 10 + 12, mPaint.get(i)); 72 } else { 73 float power = (float) ((Math.abs(rssi) - 59) / (10 * 2.0)); 74 float dis = (float) Math.pow(10, power); 75 76 canvas.drawText( 77 times + ": FIND " + UI_Main.mBTSArrayList.get(i).mName 78 + " dis: " + new Float(dis).toString() 79 + " rssi: " + rssi, 5, i * 10 + 12, 80 mPaint.get(i)); 81 canvas.drawCircle(canvas.getWidth() / 2, 82 canvas.getHeight() / 2, Bei * dis, mPaint.get(i));//畫圓圈 83 } 84 } 85 times++; 86 mHolder.unlockCanvasAndPost(canvas);// 更新屏幕顯示內容 87 UI_Main.mFuncBT.mRSSIVector.clear(); 88 UI_Main.mFuncBT.mNameVector.clear(); 89 UI_Main.mFuncBT.mAddrVector.clear(); 90 } 91 }
操作說明:該類提供在SurfaceView上繪制功能。其中靜態方法initPaint對畫筆進行初始化,draw函數負責繪制。
draw函數的核心在於canvas繪圖,canvas繪圖的過程和我們在白紙上繪繪圖的過程很像,如:
l 第55行鎖定canvas相當於得到一張紙; l 第56行用RGB為0的顏色來刷新canvas相當於用橡皮擦把紙上原來的東西擦掉; l 第68和76行drawText相當於在紙的相應位置寫文字; l 第81行drawCircle相當於在紙的相應位置繪制一個指定的圓; l 第86行的nlockCanvasAndPost相當於你把繪制好的作品拿出來展示給別人看;
正是因為canvas的加鎖和解鎖這一機制,才保證了繪制過程中屏幕正確地顯示。
接着再來理解這里draw函數的功能:mBTSArrayList是一個My_BTS類型的數組,保存我們想要防丟的藍牙防丟器設備的名稱、地址等信息;mFuncBT是一個可以實時搜索周邊藍牙設備的一個對象,其靜態變量mNameVector、mAddrVector、mRSSIVector保存着實時搜索結果;這樣核心部分的功能便是通過兩層循環遍歷待防丟設備是否在本次搜索中,如果不在則顯示“NOT_FIND”,如果在則由RSSI計算距離。(效果如圖8-4)
圖 8-4 找到藍牙設備圖
9) 新建一個Func_BT.java文件,並修改為:
1 package com.example.first_test; 2 3 import java.util.Vector; 4 5 import android.app.Activity; 6 import android.bluetooth.BluetoothAdapter; 7 import android.bluetooth.BluetoothDevice; 8 import android.content.BroadcastReceiver; 9 import android.content.Context; 10 import android.content.Intent; 11 import android.content.IntentFilter; 12 import android.os.Bundle; 13 import android.os.Handler; 14 import android.os.Message; 15 16 public class Func_BT { 17 private BluetoothAdapter mBtAdapter;// 藍牙適配器 18 private static final int ENABLE_BLUETOOTH = 1; 19 // 分別用於存儲設備名地址名稱和RSSI的向量 20 public Vector<String> mNameVector; 21 public Vector<String> mAddrVector; 22 public Vector<Short> mRSSIVector; 23 24 private Handler myHandler; 25 private Activity activity; 26 27 public Func_BT(Activity activity, Handler myHandler) { 28 this.myHandler = myHandler; 29 this.activity = activity; 30 31 mNameVector = new Vector<String>();// 向量 32 mAddrVector = new Vector<String>(); 33 mRSSIVector = new Vector<Short>(); 34 35 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 36 activity.registerReceiver(mReceiver, filter); 37 filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 38 activity.registerReceiver(mReceiver, filter); 39 activity.registerReceiver(mReceiver, filter); 40 41 mBtAdapter = BluetoothAdapter.getDefaultAdapter(); 42 } 43 44 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 45 @Override 46 public void onReceive(Context context, Intent intent) { 47 String action = intent.getAction(); 48 if (BluetoothDevice.ACTION_FOUND.equals(action)) { 49 BluetoothDevice device = intent 50 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 51 short rssi = intent.getExtras().getShort( 52 BluetoothDevice.EXTRA_RSSI); 53 mNameVector.add(device.getName()); 54 mAddrVector.add(device.getAddress()); 55 mRSSIVector.add(rssi); 56 } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED 57 .equals(action)) { 58 /*if (mNameVector.size() != 0) { 59 Message msg = new Message();// 消息 60 msg.what = 0x01;// 消息類別 61 myHandler.sendMessage(msg); 62 }*/ 63 } 64 } 65 }; 66 67 public void doDiscovery() { 68 if (mBtAdapter.isDiscovering()) { 69 mBtAdapter.cancelDiscovery(); 70 } 71 mBtAdapter.startDiscovery(); 72 new TimeLimitThread().start(); 73 } 74 75 public void openBT() { 76 // 如果沒有打開則打開 77 if (!mBtAdapter.isEnabled()) { 78 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 79 activity.startActivityForResult(intent, ENABLE_BLUETOOTH); 80 } else { 81 doDiscovery(); 82 } 83 } 84 85 protected void onActivityResult(int requestCode, int resultCode, Intent data){ 86 if (requestCode == ENABLE_BLUETOOTH) { 87 if (resultCode == Activity.RESULT_OK) { 88 doDiscovery(); 89 } 90 } 91 } 92 93 public void setHandler(Handler myHandler) { 94 this.myHandler = myHandler; 95 } 96 97 public