Android藍芽串口通訊
閑著無聊玩起了Android藍芽模組與單片機藍芽模組的通訊,簡單思路就是要手機通過藍芽發送控制指令給單片機,並作簡單的控制應用。單片機的藍芽模組串連與程式暫且略過,此文主要描述Android手機藍芽用戶端遇到的那點破事。進入正題:
串連藍牙裝置——藍芽用戶端:
Android手機一般以用戶端的角色主動串連SPP協議裝置(接上藍芽模組的數字感應器),用戶端串連流程是:
1.使用registerReceiver註冊BroadcastReceiver來擷取藍芽狀態、搜尋裝置等訊息;
privateBroadcastReceiversearchDevices= newBroadcastReceiver() {
publicvoidonReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle b = intent.getExtras();
Object[] lstName = b.keySet().toArray();
//顯示所有收到的訊息及其細節
for(inti =0; i < lstName.length;i++) {
String keyName = lstName[i].toString();
Log.e(keyName,String.valueOf(b.get(keyName)));
}
//搜尋裝置時,取得裝置的MAC地址
if(BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String str= device.getName() +"|"+device.getAddress();
if(lstDevices.indexOf(str) == -1)//防止重複添加
lstDevices.add(str);//擷取裝置名稱和mac地址
adtDevices.notifyDataSetChanged();
}
}
};
2.使用BlueAdatper的搜尋:
btAdapt.startDiscovery();
3.在BroadcastReceiver的onReceive()裡取得搜尋所得的藍牙裝置資訊(如名稱,MAC,RSSI);
4.通過裝置的MAC地址來建立一個BluetoothDevice對象;
5.由BluetoothDevice衍生出BluetoothSocket,準備SOCKET來讀寫裝置;
6.通過BluetoothSocket的createRfcommSocketToServiceRecord()方法來選擇串連的協議/服務,這裡用的 是SPP(UUID:00001101-0000-1000-8000-00805F9B34FB);
try{
btSocket=btDev.createRfcommSocketToServiceRecord(uuid);
}catch(IOException e) {
//TODOAuto-generated catch block
Log.e(TAG,"Low: Connection failed.",e);
}
成功後進行串連:
try{
btSocket.connect();
Log.e(TAG," BT connection established, data transfer linkopen.");
mangeConnectedSocket(btSocket);//自訂函數進行藍芽通訊處理
}catch(IOException e) {
Log.e(TAG," Connection failed.", e);
setTitle("串連失敗..");
}
7.Connect之後(如果還沒配對則系統自動提示),使用
BluetoothSocket的getInputStream()和getOutputStream()來讀寫藍牙裝置。
讀寫可以歸到一個獨立線程去實現~注意:讀時必須一直迴圈讀取串口緩衝區,寫可以不需要。
按以上7步逐次走過後,你就會發現Android藍芽模組是多麼的坑爹了。
出現問題:
在第6步一般初學者都會報錯:執行.connect()發生異常Connection refused
此時執行不下去咯,怎麼辦怎麼辦呢?
於是邊debug邊網上找攻略,總算在Google出老外的一些做法,嘗試了下,貌似還可行。也即把
btSocket的建立方法採用另一種方法替代,這裡都使用連接埠1
Method m;
try{
m =btDev.getClass().getMethod("createRfcommSocket",newClass[] {int.class});
btSocket=(BluetoothSocket) m.invoke(btDev,Integer.valueOf(1));
}catch(SecurityException e1) {
//TODOAuto-generated catch block
e1.printStackTrace();
}catch(NoSuchMethodException e1) {
//TODOAuto-generated catch block
e1.printStackTrace();
}catch(IllegalArgumentException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}catch(IllegalAccessException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}catch(InvocationTargetException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}
至此,這個問題貌似倒也解決了,程式繼續往下跑。
但這裡請記住之前的異常,先別急著拋開~人家不一定一直都是異常哦
接下來的任務是,讓手機通過藍芽跟單片機的藍芽模組通訊,並發送資料,通過電腦串口調試助手顯示出來。具體實現,在mangeConnectedSocket(btSocket)方法中實現,裡面通過啟動另一個Activity實現。不是重點,略過。
直到這裡,我們都只是把手機藍芽模組充當用戶端來使用,那什麼時候會用到服務端呢?其實,之前手機藍芽與單片機藍芽模組的通訊,單片機藍芽模組就充當了服務端(處於監聽狀態,被手機藍芽串連)。為了更好地搞清楚Android藍芽通訊,我們接下來使用2個手機的藍芽進行通訊。簡單地說,就是做一個“手機藍芽扣扣”,⊙﹏⊙b汗
一開始就想天真地把之前的程式同時燒到2部手機中,發現只有一部手機能正常建立socket串連(主動串連的那台),而另一部卻遲遲沒有響應。原因很簡單,服務端的程式還沒有編寫!
於是,開始服務端程式:開闢一個新的線程實現
串連藍牙裝置——藍芽服務端:
classAcceptThreadextendsThread {
privatefinalBluetoothServerSocketserverSocket;
publicAcceptThread() {
// Use a temporary object that is later assignedto mmServerSocket,
// because mmServerSocket isfinal
BluetoothServerSocket tmp=null;
try{
//tmp =btAdapt.listenUsingRfcommWithServiceRecord("MyBluetoothApp",uuid);
Log.e(TAG,"++BluetoothServerSocketestablished!++");
Method listenMethod = btAdapt.getClass().getMethod("listenUsingRfcommOn",
new Class[]{int.class});
tmp= ( BluetoothServerSocket) listenMethod.invoke(btAdapt, Integer.valueOf( 1));
}catch(SecurityException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}catch(IllegalArgumentException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}catch(NoSuchMethodException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}catch(IllegalAccessException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}catch(InvocationTargetException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}
serverSocket=tmp;
}
publicvoidrun() {
// Keep listening until exception occurs or asocket is returned
//mState!=STATE_CONNECTED
while(true){//這裡是一直迴圈監聽,也可以設定mState來判斷
try{
socket=serverSocket.accept();
Log.e(TAG,"++BluetoothSocket established! DataLinkopen.++");
}catch(IOException e) {
break;
}
// If a connection was accepted
if(socket!=null){
// Do work to manage the connection (in a separatethread)
manageConnectedSocket();
try{
serverSocket.close();
}catch(IOException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}
break;
}
}
}
publicvoidcancel() {
try{
serverSocket.close();
}catch(IOException e) { }
}
}
安裝測試:當2部手機都裝上並開啟同樣的程式後,通過藍芽檢索並串連,經測試可以成功串連上,雙雙進入“聊天介面”,嘿嘿
注意,這時候重新拾回之前那個異常,把socket串連建立的方法重新改為
btSocket=btDev.createRfcommSocketToServiceRecord(uuid);//用戶端
對應的服務端程式:
tmp=btAdapt.listenUsingRfcommWithServiceRecord("MyBluetoothApp",uuid);//服務端
這樣繼續重新運行安裝測試,在2部手機上運行發現之前那個bug消失了~2部手機又雙雙進入聊天介面。
神奇~
存在bug:
任一一部手機都只能成功啟動一次作為用戶端的主動串連,當退出聊天介面回到主介面時(服務端的AcceptThread還在繼續運行著),可再次主動串連另一部手機時就又報異常Connection refused。也就是說用戶端的藍芽通訊端2次串連時出錯~哎(注意我的用戶端藍芽串連程式是沒有放到一個獨立線程,而是放到一個按鈕監聽事件中)
又折騰了好久,沒發現個所以然來,看來連完一次退出再連時就只好重啟程式咯。有哪位大神知道為什麼的麻煩告知下哈!
若需要代碼,code下載