Android BLE 藍牙編程(三)


上節我們已經可以連接上藍牙設備了。

本節我們就要獲取手環的電池電量和計步啦。

在介紹這個之前我們需要先了解下什么是 服務 什么是 UUID

我們記得上節中我們item監聽事件的回調的返回值是BluetoothGatt 類型的,還記得么?嘿嘿。

返回的bluetoothgatt中包含一個或多個BluetoothGattService(服務)

每個service包含一個或多個characteristic(特征值)

每個特征值包含一個value 和多個 descriptor(注意看啊!是一個value)

這些對於本項目來說某個特征值的value中就包含了記錄的步數。(很遺憾我折騰了很久也沒找到電池電量,不得不舍棄展示電池電量的想法)

先熟悉下下面的圖片吧~~

百度腦圖

在了解下UUID 上圖中每個藍牙設備都有自己的MAC 地址有了這個地址 我們就可以連接這個設備。

連接上設備后會的到設備的服務就是上圖中的Gattservice 這每個service都有個固定的UUID以標識區分

每個service又包含多個的characteristic 這些特征值也有唯一的UUID 這些UUID就是我們編程需要的,

獲取到characteristic對應的UUID就意味着獲取到該characteristic提供的功能或者數據。

特征值對應的UUID所實現的功能是由硬件工程師決定的。

因為小米手環的硬件工程師我們沒有聯系方式,況且人家也肯定不會告訴我所有功能對應的UUID

因此我們只好自己對應數值來找了。

 

簡單說 上圖的每個characteristic 都對應一個UUID 這些UUID對應了設備的不同功能

手環為例:

控制手環震動的 UUID :00002a06-0000-1000-8000-00805f9b34fb   手機向該 UUID 寫入 0x01 或者 0x02 時手環都會震動,01強度弱於 02

計步的     UUID :0000ff06-0000-1000-8000-00805f9b34fb     讀取該UUID下的value數組 第0 個數據就是 步數

 

首先我們來獲取下計步數吧

 經過一番折騰我終於找到了小米手環對應步數的 UUID :0000ff06-0000-1000-8000-00805f9b34fb

gattcallback 回調方法中找到 onServicesDiscovered

添加如下代碼:

 @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            //尋找到服務時
            if (status == bluetoothGatt.GATT_SUCCESS) {
                final List<BluetoothGattService> services = bluetoothGatt.getServices();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //List<String> serlist = new ArrayList<>();
                        for (final BluetoothGattService bluetoothGattService : services) {
                            bluetoothGattServices = bluetoothGattService;

                            Log.i(TAG, "onServicesDiscovered: " + bluetoothGattService.getUuid());

                            List<BluetoothGattCharacteristic> charc = bluetoothGattService.getCharacteristics();

                            for (BluetoothGattCharacteristic charac : charc) {
                                Log.i(TAG, "run: " + charac.getUuid());
                                //找到透傳特征值
                                // 00002a06-0000-1000-8000-00805f9b34fb 小米手環震動特征值 0x01震動 0x02強震
                                if (charac.getUuid().toString().equals("00002a06-0000-1000-8000-00805f9b34fb")) {
                                    //設備 震動特征值
                                    characteristic_zd = charac;

                                } else if (charac.getUuid().toString().equals("0000ff06-0000-1000-8000-00805f9b34fb")) {
                                    //設備 步數
                                    characteristic_jb = charac;
                                    bluetoothGatt.readCharacteristic(characteristic_jb);

                                    Log.i(TAG, "run: 正在嘗試讀取步數");
                                } else if (charac.getUuid().toString().equals("")) {
                                    //設備 電量特征值
                                }
                            }

                            serviceslist.add(bluetoothGattService.getUuid().toString());

                        }
                    }
                });
            }

        }

注釋寫的很清楚

我用 characteristic_zd 記住震動的特征值

 用 characteristic_jb  記住計步的

記住計步特征值后使用 bluetoothGatt 提供的read方法並傳人相應特征值。該方法為異步方法他的返回內容會由

回調中的 onCharacteristicRead 方法接收到

找到該方法添加如下代碼:

if (status == bluetoothGatt.GATT_SUCCESS) {
                final int sum = characteristic.getValue()[0];

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        jibu.setText("走了" + sum + "步");
                    }
                });

                Log.e(TAG, "onCharacteristicRead: " + characteristic.getValue()[0]);

            }

方法的參數中status表示狀態,為 bluetoothGatt.GATT_SUCCESS 時表示成功獲取返回

因為在異步方法中,想要操作UI線程,於是就用了 runOnUiThread 方法。顯示出來步數即可。

好了下面貼出完整主Activity代碼:

MainActivity.java:

package com.wbnq.shouhuan;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button saomiao, duanzhen, changzhen, buting, tingxia;
    private TextView jibu, dianliang, lianjiezhuangtai;
    private ListView list;

    public static String TAG = "shouhuan-MainActivity";

    BluetoothAdapter bluetoothAdapter;
    BluetoothGatt bluetoothGatt;
    List<BluetoothDevice> deviceList = new ArrayList<>();
    List<String> serviceslist = new ArrayList<String>();
    BluetoothDevice bluetoothDevice;
    BluetoothGattService bluetoothGattServices;
    BluetoothGattCharacteristic characteristic_zd, characteristic_jb;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();

        //藍牙管理,這是系統服務可以通過getSystemService(BLUETOOTH_SERVICE)的方法獲取實例
        BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
        //通過藍牙管理實例獲取適配器,然后通過掃描方法(scan)獲取設備(device)
        bluetoothAdapter = bluetoothManager.getAdapter();


    }

    private void initView() {
        saomiao = (Button) findViewById(R.id.saomiao);
        duanzhen = (Button) findViewById(R.id.zhendong);
        changzhen = (Button) findViewById(R.id.changzhen);
        buting = (Button) findViewById(R.id.buting);
        tingxia = (Button) findViewById(R.id.tingxia);
        list = (ListView) findViewById(R.id.list);

        jibu = (TextView) findViewById(R.id.jibu);
        dianliang = (TextView) findViewById(R.id.dianliang);
        lianjiezhuangtai = (TextView) findViewById(R.id.lianjiezhuangtai);

        saomiao.setOnClickListener(this);
        duanzhen.setOnClickListener(this);
        changzhen.setOnClickListener(this);
        buting.setOnClickListener(this);
        tingxia.setOnClickListener(this);

        //item 監聽事件
        list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                bluetoothDevice = deviceList.get(i);
                //連接設備的方法,返回值為bluetoothgatt類型
                bluetoothGatt = bluetoothDevice.connectGatt(MainActivity.this, false, gattcallback);
                lianjiezhuangtai.setText("連接" + bluetoothDevice.getName() + "中...");
            }
        });

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.saomiao:
                //開始掃描前開啟藍牙
                Intent turn_on = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(turn_on, 0);
                Toast.makeText(MainActivity.this, "藍牙已經開啟", Toast.LENGTH_SHORT).show();

                Thread scanThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Log.i("TAG", "run: saomiao ...");
                        saomiao();
                    }
                });
                scanThread.start();
                lianjiezhuangtai.setText("正在掃描");

                break;
            case R.id.zhendong:

                break;
            case R.id.changzhen:

                break;
            case R.id.buting:

                break;
            case R.id.tingxia:

                break;
            case R.id.list:

                break;

        }
    }

    public void saomiao() {
        deviceList.clear();
        bluetoothAdapter.startLeScan(callback);
    }

    //掃描回調
    public BluetoothAdapter.LeScanCallback callback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
            Log.i("TAG", "onLeScan: " + bluetoothDevice.getName() + "/t" + bluetoothDevice.getAddress() + "/t" + bluetoothDevice.getBondState());

            //重復過濾方法,列表中包含不該設備才加入列表中,並刷新列表
            if (!deviceList.contains(bluetoothDevice)) {
                //將設備加入列表數據中
                deviceList.add(bluetoothDevice);

                list.setAdapter(new MyAdapter(MainActivity.this, deviceList));
            }

        }
    };

    private BluetoothGattCallback gattcallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, final int newState) {
            super.onConnectionStateChange(gatt, status, newState);

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    String status;
                    switch (newState) {
                        //已經連接
                        case BluetoothGatt.STATE_CONNECTED:
                            lianjiezhuangtai.setText("已連接");
                            bluetoothAdapter.stopLeScan(callback);
                            //該方法用於獲取設備的服務,尋找服務
                            bluetoothGatt.discoverServices();
                            break;
                        //正在連接
                        case BluetoothGatt.STATE_CONNECTING:
                            lianjiezhuangtai.setText("正在連接");
                            break;
                        //連接斷開
                        case BluetoothGatt.STATE_DISCONNECTED:
                            lianjiezhuangtai.setText("已斷開");
                            break;
                        //正在斷開
                        case BluetoothGatt.STATE_DISCONNECTING:
                            lianjiezhuangtai.setText("斷開中");
                            break;
                    }
                    //pd.dismiss();
                }
            });
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            //尋找到服務時
            if (status == bluetoothGatt.GATT_SUCCESS) {
                final List<BluetoothGattService> services = bluetoothGatt.getServices();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //List<String> serlist = new ArrayList<>();
                        for (final BluetoothGattService bluetoothGattService : services) {
                            bluetoothGattServices = bluetoothGattService;

                            Log.i(TAG, "onServicesDiscovered: " + bluetoothGattService.getUuid());

                            List<BluetoothGattCharacteristic> charc = bluetoothGattService.getCharacteristics();

                            for (BluetoothGattCharacteristic charac : charc) {
                                Log.i(TAG, "run: " + charac.getUuid());
                                //找到透傳特征值
                                // 00002a06-0000-1000-8000-00805f9b34fb 小米手環震動特征值 0x01震動 0x02強震
                                if (charac.getUuid().toString().equals("00002a06-0000-1000-8000-00805f9b34fb")) {
                                    //設備 震動特征值
                                    characteristic_zd = charac;

                                } else if (charac.getUuid().toString().equals("0000ff06-0000-1000-8000-00805f9b34fb")) {
                                    //設備 步數
                                    characteristic_jb = charac;
                                    bluetoothGatt.readCharacteristic(characteristic_jb);

                                    Log.i(TAG, "run: 正在嘗試讀取步數");
                                } else if (charac.getUuid().toString().equals("")) {
                                    //設備 電量特征值
                                }
                            }


                            serviceslist.add(bluetoothGattService.getUuid().toString());

                        }
//                        ArrayAdapter<String> adapter = new ArrayAdapter<String>(
//                                MainActivity.this, android.R.layout.simple_expandable_list_item_1, serviceslist);
                        //list.setAdapter(adapter);
                    }
                });
            }

        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);

            if (status == bluetoothGatt.GATT_SUCCESS) {
                final int sum = characteristic.getValue()[0];

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        jibu.setText("走了" + sum + "步");
                    }
                });

                Log.e(TAG, "onCharacteristicRead: " + characteristic.getValue()[0]);

            }

        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
        }


        //獲取返回 數據
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);

            Log.i(TAG, "onCharacteristicChanged: 獲取回調方法");

            Log.e("", "命令:" + HexUtil.encodeHexStr(characteristic.getValue()));

            final byte[] values = characteristic.getValue();

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, HexUtil.encodeHexStr(values), Toast.LENGTH_SHORT).show();
                }
            });


        }

    };

    private boolean enableNotification(boolean enable, BluetoothGattCharacteristic characteristic) {
        if (bluetoothGatt == null || characteristic == null)
            return false;
        if (!bluetoothGatt.setCharacteristicNotification(characteristic, enable))
            return false;
        BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
        if (clientConfig == null)
            return false;

        if (enable) {
            clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        } else {
            clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        }
        return bluetoothGatt.writeDescriptor(clientConfig);
    }
}
View Code

今天就寫這些,下次要實現震動方法咯!是不是很激動呢~~

嘿嘿嘿~

大家加油啦~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM