Android 藍牙串口調試程序開發

前言:本次項目需要爲智能設備開發一個 App 於是就開始學習 Android 的藍牙串口通信方面的知識,現在 App 已經寫完了,當初學的時候走了不少彎路和嘗試,現作爲一名初學者和大家分享一下經驗,以及總結我該部分的學習。

〇、藍牙串口開發的流程

Created with Raphaël 2.2.0開始獲取權限檢測設備打開藍牙連接設備收發數據單片機串口藍牙結束yesnoyesno

一、添加需要用到的權限

在 Android 項目中,打開 AndroidManif.xml 文件 添加對藍牙操作的權限。

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

二、檢測和打開藍牙設備

1.檢測藍牙設備

如果要通過藍牙進行串口通信,必須得保證藍牙設備正常工作,首先要檢測藍牙設備是否正常。通常情況下這一步也可以省略,或在打開藍牙設備的時候檢測。

//獲取默認藍牙設備
mADAPTER_BLUETOOTH = BluetoothAdapter.getDefaultAdapter();
//判斷是否有藍牙設備
if (mADAPTER_BLUETOOTH == null){
	Toast.makeText(MainActivity.this, "未找到藍牙功能", LENGTH_SHORT).show();
}else {
	Toast.makeText(MainActivity.this, "藍牙功能正常", LENGTH_SHORT).show();

通過 BluetoothAdapter 來獲取默認藍牙設備,值爲 null 說明藍牙功能缺失,反之則反。

2.打開藍牙設備

打開藍牙設備的方式一共有兩種,一種是直接通過使能藍牙打開,另一種是彈出對話框讓用戶確認打開。一般不使用第一種,而使用有交互的第二種。因爲如果我們打開藍牙,不打開藍牙可見的話,別人就發現不了我們的設備,對我們的項目沒有任何意義,所以在打開藍牙的同時會打開可見。

  • 直接使能藍牙的方式
mADAPTER_BLUETOOTH = BluetoothAdapter.getDefaultAdapter();
mADAPTER_BLUETOOTH.enable();
  • 具有人機交互的方式

    構建一個打開藍牙的方法,可以把對藍牙操作的方法,寫在一個類裏面,方便調用。

//打開藍牙的方法
public void TurnOnBlueTooth(){
    //獲取默認藍牙設備
    mADAPTER_BLUETOOTH = BluetoothAdapter.getDefaultAdapter();
    //判斷是否有藍牙設備
    if (mADAPTER_BLUETOOTH == null){
        Toast.makeText(MainActivity.this, "未找到藍牙功能", LENGTH_SHORT).show();
    }else {
        Toast.makeText(MainActivity.this, "藍牙功能正常", LENGTH_SHORT).show();
        if (!mADAPTER_BLUETOOTH.isEnabled()){
            //如果藍牙設備正常 藍牙沒打開 請求打開並可見
            Intent TurnOn = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(TurnOn,1);
        }else {
            Toast.makeText(MainActivity.this, "藍牙已打開 請勿重複打開", LENGTH_SHORT).show();
        }
    }
}
  • 藍牙狀態的廣播接收者

    通過註冊一個藍牙狀態的廣播接收者,來接收藍牙狀態改變,給用戶提供一個跟友好的人機交互。

    註冊廣播接收者

//藍牙狀態改變的廣播 //註冊和綁定廣播接收者和廣播
IntentFilter mBROADCAST_BLUETOOTH_STATE = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(RECEIVER_BLUETOOTH_STATE,mBROADCAST_BLUETOOTH_STATE);

寫藍牙狀態改變的廣播接收者

//藍牙打開狀態的廣播接收
private BroadcastReceiver RECEIVER_BLUETOOTH_STATE = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,-1);
        switch (state){
            case BluetoothAdapter.STATE_OFF:
                Toast.makeText(MainActivity.this, "藍牙已關閉", LENGTH_SHORT).show();
                break;
            case BluetoothAdapter.STATE_ON:
                Toast.makeText(MainActivity.this, "藍牙已打開", LENGTH_SHORT).show();
                break;
            case BluetoothAdapter.STATE_TURNING_OFF:
                Toast.makeText(MainActivity.this, "藍牙正在關閉", LENGTH_SHORT).show();
                break;
            case BluetoothAdapter.STATE_TURNING_ON:
                Toast.makeText(MainActivity.this, "藍牙正在打開", LENGTH_SHORT).show();
                break;
        }
    }
};

通過接受藍牙狀態改變的廣播,來給用戶相應的提示,形成良好的人機交互。

三、搜索並連接藍牙設備

1.搜索藍牙設備

搜索藍牙設備操作直接調用 BluetoothAdapter 的 startDiscovery 方法即可。同樣的,我們可以寫一個搜索藍牙的方法,把這個方法寫在 對藍牙操作的 類裏面,方便我們去調用。

public void DiscoveryBlueTooth() {
    //獲取默認的藍牙設備
    mADAPTER_BLUETOOTH = BluetoothAdapter.getDefaultAdapter();
    //判斷是否在搜索
    if (mADAPTER_BLUETOOTH.isDiscovering()) {
        mADAPTER_BLUETOOTH.cancelDiscovery();
    }
    //開始搜索
    mADAPTER_BLUETOOTH.startDiscovery();
    Toast.makeText(MainActivity.this, "藍牙設備正在搜索中", LENGTH_SHORT).show();
}

這只是開啓搜索,我們搜索的結果該怎麼反饋給我們呢?我們需要一個容器來存放我們剛剛搜索到的設備。

  • ListView 和 ListAdapter
    我們在佈局文件中,加入一個Listview控件,來顯示我們搜索到的藍牙設備。寫好佈局後,給 Listview 的 item 寫一個佈局樣式。
<TextView
    android:id="@+id/TV_BLUETOOTH_DEVICE"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:textSize="13sp"
    android:textColor="@color/colorWhite"
    android:background="@drawable/button_selector"/>

我這裏只用了一個 Textview
寫好了 Listview 我們需要一個東西,把搜索到的設備信息和 Listview 的內容綁定起來。這裏我用的是 ArrayList ,通過 ArrayAdapter 綁定設備信息和 Listview的內容。

//ArrayAdapter聲明
private ArrayAdapter mADAPTER_ARRAY;
//ArrayList定義:存放藍牙名稱和地址
private List<String> BLUETOOTH_DEViCE = new ArrayList<>();
/*
*通過 ArrayAdapter 綁定 ListView控件和ArrayList裏的數據
*/
//通過ArrayAdapter綁定數據
mADAPTER_ARRAY = new ArrayAdapter<String>(this,R.layout.list_view_item,R.id.TV_BLUETOOTH_DEVICE,BLUETOOTH_DEViCE);
//給ListView使用這個 adapter
mLV_DEVICE.setAdapter(mADAPTER_ARRAY);

到這裏,我們的準備工作就完成了,接下來就是把搜索到的設備信息添加進去。

  • 接收搜索結果的廣播
    在接受搜索設備的廣播之前,先註冊搜索設備的廣播,將接收者和廣播綁定。
//藍牙狀態改變的廣播 //註冊和綁定廣播接收者和廣播
IntentFilter mBROADCAST_BLUETOOTH_STATE = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(RECEIVER_BLUETOOTH_STATE,mBROADCAST_BLUETOOTH_STATE);

然後寫搜索藍牙設備的廣播接收者

private BroadcastReceiver RECEIVER_BLUETOOTH_FOUND = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                BLUETOOTH_DEViCE.add("  NAME:"+device.getName()+"(待連接)"+"\n"+"  *MAC:"+device.getAddress());
                mADAPTER_ARRAY.notifyDataSetChanged();
            }
        }
    };

將搜索到的設備添加到 ArrayList 裏,然後就會在 Listview 上顯示。

2.連接藍牙設備

在完成了搜索設備之後,我們就需要與藍牙設備進行連接。與單片機上藍牙串口連接,我們需要藍牙串口服務的 UUID 00001101-0000-1000-8000-00805F9B34FB
需要一個 Sokect 來連接藍牙串口。

//設定本機的UUID->通用唯一識別碼 //串口UUID
private final UUID mUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
//定義BluetoothSocket
public static BluetoothSocket mBLUETOOTH_SOCKET;

我們給 Listview 的 item 設立一個點擊事件,好讓我們點擊一個設備的時候,可以連接該設備。在點擊事件中,我們通過 ArrayAdapter 的 getItem 方法,來獲取保存在 Array List裏面的設備信息。然後通過 “*” 定位我們需要的地址的起始位置,來獲取地址。通過 BluetoothDevice 的 GetRemoteDevice 方法,獲取該地址的的設備。用我們寫的 ConnectThread 的方法進行連接設備。

//LV_DEVICE 的 item 被點擊:
mLV_DEVICE.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        //BluetoothDevice mBTD =
        //獲取item的內容 //解析出內容裏的地址
        String s = (String) mADAPTER_ARRAY.getItem(position);
        if (mADAPTER_BLUETOOTH.isDiscovering())
        {
            mADAPTER_BLUETOOTH.cancelDiscovery();
        }
        if (s != null){
        //在這裏我們通過*來定位地址的起始字符然後保存在 address裏
            String address = s.substring(s.indexOf("*")+5).trim();
            Toast.makeText(MainActivity.this,"MAC:"+address,Toast.LENGTH_LONG).show();
            BluetoothDevice device = mADAPTER_BLUETOOTH.getRemoteDevice(address);
            ConnectThread(device);
        }


    }
});

ConnectThread 方法
進行連接後,我們就開始數據接收服務

public void ConnectThread(BluetoothDevice device) {
    try {
        mBLUETOOTH_SOCKET = device.createRfcommSocketToServiceRecord(mUUID);
        mADAPTER_BLUETOOTH.cancelDiscovery();
        mBLUETOOTH_SOCKET.connect();
        Toast.makeText(MainActivity.this, "連接成功", Toast.LENGTH_LONG).show();
        // 接收數據進程
        ReceiveData receiveThread = new ReceiveData();
        // 連接成功後開啓接收數據服務
        receiveThread.start();
    } catch (IOException e) {
        Toast.makeText(MainActivity.this, "連接失敗", Toast.LENGTH_SHORT)
                .show();
        try {
            mBLUETOOTH_SOCKET.close();
        } catch (IOException e2) {
            e.printStackTrace();
        }
    }
}

既然要連接我們就要有一個取消連接的方法。關閉 Socket 來釋放資源。

    // 取消鏈接的方法
    public void CancelConnect() {
        try {
            mBLUETOOTH_SOCKET.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

在以上過程中,我們可以自己設立一個 Boolean 型的變量,來表示與設備的連接狀態,通過判斷該變量的值來做出相應的人機交互。

四、發送並接受串口數據

到這一步,我們的項目基本成型了,只剩下最後也是最關鍵的與 串口藍牙進行通信。我這裏採用的是發送 字符數據 和 接受 字符數據 進行交互。在發送的時候,會將字符數據轉化成字節流的形式發送。

1.發送數據部分

發送數據數據和接受數據,我們需要 輸入流 和 輸出流。

    //定義輸出流//定義輸入流
    public static OutputStream OS;
    public static InputStream  IS;

然後寫一個 write 的方法來調用 OutputStream 的 write 方法來寫一個字節。

    //發送數據的方法
    public static void write(String str) {
        if (CONNECT_STATUS) {
            try {
                OS = mBLUETOOTH_SOCKET.getOutputStream();
                if (OS != null){
                    OS.write(str.getBytes("UTF-8"));
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

我們可以將 String 來源於 Edit Text 控件的內容,通過一個按鈕來執行發送的操作。

2.接收數據部分

接受數據的服務我們已經在連接那一步打開了,裏面用到的我們寫的一個接收數據的方法 ReceiveData 。並且在調用前,我們開啓了一個新的線程來做這件事情。

    // 讀數據線程
    private class ReceiveData extends Thread {
        InputStream mmInStream;

        private ReceiveData() {
            try {
                mmInStream = mBLUETOOTH_SOCKET.getInputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {

            int bytes = 0;
            byte[] buffer = new byte[256];
            while (true) {

                // 接收數據
                try {
                    bytes = mmInStream.read(buffer);
                    final String readString = new String(buffer, 0, bytes);

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mTV_ACCEPT.append(readString + " ");
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

我們可以用一個 Textview 用來顯示我們接收到的數據。

五、總結學習

本次項目的學習主要難點在搜索添加設備和,讀寫數據上,很多內容還需要多家理解,對該部分的知識還不夠深刻,需要系統性的學習一下。

以下是 App 的實際效果。
App開始界面
App主界面
切換到調試模式
用戶登錄界面
用戶信息輸入界面

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章