zoukankan      html  css  js  c++  java
  • [转]Ble蓝牙的使用手册

    本文转自:https://blog.csdn.net/dodan/article/details/52060446

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dodan/article/details/52060446


    如上图所示,使用低功耗蓝牙可以包括多个Profile,一个Profile中有多个Service,一个Service中有多个Characteristic,一个Characteristic中包括一个value和多个Descriptor。


    Android中进行蓝牙开发需要使用到的类的执行过程是:

    1、使用BluetoothAdapter.startLeScan来扫描低功耗蓝牙设备

    2、在扫描到设备的回调函数中会得到BluetoothDevice对象,并使用BluetoothAdapter.stopLeScan停止扫描

    3、使用BluetoothDevice.connectGatt来获取到BluetoothGatt对象

    4、执行BluetoothGatt.discoverServices,这个方法是异步操作,在回调函数onServicesDiscovered中得到status,通过判断status是否等于BluetoothGatt.GATT_SUCCESS来判断查找Service是否成功

    5、如果成功了,则通过BluetoothGatt.getService来获取BluetoothGattService

    6、接着通过BluetoothGattService.getCharacteristic获取BluetoothGattCharacteristic

    7、然后通过BluetoothGattCharacteristic.getDescriptor获取BluetoothGattDescriptor


    使用低功耗蓝牙需要用到的权限:
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>


    下面介绍怎样使用BLE:

    1、准备BLE

    1)获取BluetoothAdapter

    BluetoothAdapter是从系统服务获取到的,全系统就一个。

    // Initializes Bluetooth adapter.
    final BluetoothManager bluetoothManager =
    (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();

    2)检测蓝牙是否打开

    如果蓝牙未打开,系统会自动打开,会弹出系统框展示打开蓝牙。

    private BluetoothAdapter mBluetoothAdapter;
    ...
    // Ensures Bluetooth is available on the device and it is enabled. If not,
    // displays a dialog requesting user permission to enable Bluetooth.
    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }

    2、查找BLE设备

    因为扫描BLE设备是电源密集型操作,浪费电量,因此要保证以下原则:

    1)扫描到需要的设备后,马上停止扫描;

    2)给扫描一个时间限制

    扫描示例代码如下:

    /**
    * Activity for scanning and displaying available BLE devices.
    */
    public class DeviceScanActivity extends ListActivity {

    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler;

    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;
    ...
    private void scanLeDevice(final boolean enable) {
    if (enable) {
    // Stops scanning after a pre-defined scan period.
    mHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
    mScanning = false;
    mBluetoothAdapter.stopLeScan(mLeScanCallback);
    }
    }, SCAN_PERIOD);

    mScanning = true;
    mBluetoothAdapter.startLeScan(mLeScanCallback);
    } else {
    mScanning = false;
    mBluetoothAdapter.stopLeScan(mLeScanCallback);
    }
    ...
    }
    ...
    }
    如果只是要扫描到特定类型的设备,则使用接口 startLeScan(UUID[], BluetoothAdapter.LeScanCallback),通过UUID来查找设备。

    扫描回调的代码如下所示:

    private LeDeviceListAdapter mLeDeviceListAdapter;
    ...
    // Device scan callback.
    private BluetoothAdapter.LeScanCallback mLeScanCallback =
    new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
    byte[] scanRecord) {
    runOnUiThread(new Runnable() {
    @Override
    public void run() {
    mLeDeviceListAdapter.addDevice(device);
    mLeDeviceListAdapter.notifyDataSetChanged();
    }
    });
    }
    };
    注意:我们既可以扫描BLE设备,也可以扫描普通蓝牙设备,也可以同时将BLE设备和普通蓝牙设备一起扫描到。

    3、连接到GATT Server

    获取到BluetoothGatt实例,

    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
    具体实例如下:

    // A service that interacts with the BLE device via the Android BLE API.
    public class BluetoothLeService extends Service {
    private final static String TAG = BluetoothLeService.class.getSimpleName();

    private BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private String mBluetoothDeviceAddress;
    private BluetoothGatt mBluetoothGatt;
    private int mConnectionState = STATE_DISCONNECTED;

    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_CONNECTED = 2;

    public final static String ACTION_GATT_CONNECTED =
    "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED =
    "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED =
    "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_DATA_AVAILABLE =
    "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
    public final static String EXTRA_DATA =
    "com.example.bluetooth.le.EXTRA_DATA";

    public final static UUID UUID_HEART_RATE_MEASUREMENT =
    UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

    // Various callback methods defined by the BLE API.
    private final BluetoothGattCallback mGattCallback =
    new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status,
    int newState) {
    String intentAction;
    if (newState == BluetoothProfile.STATE_CONNECTED) {
    intentAction = ACTION_GATT_CONNECTED;
    mConnectionState = STATE_CONNECTED;
    broadcastUpdate(intentAction);
    Log.i(TAG, "Connected to GATT server.");
    Log.i(TAG, "Attempting to start service discovery:" +
    mBluetoothGatt.discoverServices());

    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
    intentAction = ACTION_GATT_DISCONNECTED;
    mConnectionState = STATE_DISCONNECTED;
    Log.i(TAG, "Disconnected from GATT server.");
    broadcastUpdate(intentAction);
    }
    }

    @Override
    // New services discovered
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
    broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
    } else {
    Log.w(TAG, "onServicesDiscovered received: " + status);
    }
    }

    @Override
    // Result of a characteristic read operation
    public void onCharacteristicRead(BluetoothGatt gatt,
    BluetoothGattCharacteristic characteristic,
    int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
    broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
    }
    }
    ...
    };
    ...
    }
    其中,discoverService方式是异步的,它的回调方法是上面代码中的onServiceDiscovered。

    private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
    }

    private void broadcastUpdate(final String action,
    final BluetoothGattCharacteristic characteristic) {
    final Intent intent = new Intent(action);

    // This is special handling for the Heart Rate Measurement profile. Data
    // parsing is carried out as per profile specifications.
    if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
    int flag = characteristic.getProperties();
    int format = -1;
    if ((flag & 0x01) != 0) {
    format = BluetoothGattCharacteristic.FORMAT_UINT16;
    Log.d(TAG, "Heart rate format UINT16.");
    } else {
    format = BluetoothGattCharacteristic.FORMAT_UINT8;
    Log.d(TAG, "Heart rate format UINT8.");
    }
    final int heartRate = characteristic.getIntValue(format, 1);
    Log.d(TAG, String.format("Received heart rate: %d", heartRate));
    intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
    } else {
    // For all other profiles, writes the data formatted in HEX.
    final byte[] data = characteristic.getValue();
    if (data != null && data.length > 0) {
    final StringBuilder stringBuilder = new StringBuilder(data.length);
    for(byte byteChar : data)
    stringBuilder.append(String.format("%02X ", byteChar));
    intent.putExtra(EXTRA_DATA, new String(data) + " " +
    stringBuilder.toString());
    }
    }
    sendBroadcast(intent);
    }


    // Handles various events fired by the Service.
    // ACTION_GATT_CONNECTED: connected to a GATT server.
    // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
    // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
    // ACTION_DATA_AVAILABLE: received data from the device. This can be a
    // result of read or notification operations.
    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
    final String action = intent.getAction();
    if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
    mConnected = true;
    updateConnectionState(R.string.connected);
    invalidateOptionsMenu();
    } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
    mConnected = false;
    updateConnectionState(R.string.disconnected);
    invalidateOptionsMenu();
    clearUI();
    } else if (BluetoothLeService.
    ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
    // Show all the supported services and characteristics on the
    // user interface.
    displayGattServices(mBluetoothLeService.getSupportedGattServices());
    } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
    displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
    }
    }
    };

    4、读BLE属性

    一旦获取到GATT的Services,就可以读写他们的属性了,实例如下:

    public class DeviceControlActivity extends Activity {
    ...
    // Demonstrates how to iterate through the supported GATT
    // Services/Characteristics.
    // In this sample, we populate the data structure that is bound to the
    // ExpandableListView on the UI.
    private void displayGattServices(List<BluetoothGattService> gattServices) {
    if (gattServices == null) return;
    String uuid = null;
    String unknownServiceString = getResources().
    getString(R.string.unknown_service);
    String unknownCharaString = getResources().
    getString(R.string.unknown_characteristic);
    ArrayList<HashMap<String, String>> gattServiceData =
    new ArrayList<HashMap<String, String>>();
    ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
    = new ArrayList<ArrayList<HashMap<String, String>>>();
    mGattCharacteristics =
    new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

    // Loops through available GATT Services.
    for (BluetoothGattService gattService : gattServices) {
    HashMap<String, String> currentServiceData =
    new HashMap<String, String>();
    uuid = gattService.getUuid().toString();
    currentServiceData.put(
    LIST_NAME, SampleGattAttributes.
    lookup(uuid, unknownServiceString));
    currentServiceData.put(LIST_UUID, uuid);
    gattServiceData.add(currentServiceData);

    ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
    new ArrayList<HashMap<String, String>>();
    List<BluetoothGattCharacteristic> gattCharacteristics =
    gattService.getCharacteristics();
    ArrayList<BluetoothGattCharacteristic> charas =
    new ArrayList<BluetoothGattCharacteristic>();
    // Loops through available Characteristics.
    for (BluetoothGattCharacteristic gattCharacteristic :
    gattCharacteristics) {
    charas.add(gattCharacteristic);
    HashMap<String, String> currentCharaData =
    new HashMap<String, String>();
    uuid = gattCharacteristic.getUuid().toString();
    currentCharaData.put(
    LIST_NAME, SampleGattAttributes.lookup(uuid,
    unknownCharaString));
    currentCharaData.put(LIST_UUID, uuid);
    gattCharacteristicGroupData.add(currentCharaData);
    }
    mGattCharacteristics.add(charas);
    gattCharacteristicData.add(gattCharacteristicGroupData);
    }
    ...
    }
    ...
    }
    在获取Service的时候,每个蓝牙设备都会有两个默认的Service,它们和对应的UUID分别如下:

    Bluetooth Generic Access Profile {00001800-0000-1000-8000-00805f9b34fb}

    Bluetooth Generic Attribute Profile {00001801-0000-1000-8000-00805F9B34FB}

    5、收到GATT通知

    如果设备主动给手机发信息,则可以通过notification的方式,这种方式不用手机去轮询地读设备上的数据。手机可以用如下方式给设备设置notification功能。

    private BluetoothGatt mBluetoothGatt;
    BluetoothGattCharacteristic characteristic;
    boolean enabled;
    ...
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    ...
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
    UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    mBluetoothGatt.writeDescriptor(descriptor);
    如果notificaiton方式对于某个Characteristic是enable的,那么当设备上的这个Characteristic改变时,手机上的onCharacteristicChanged() 回调就会被促发。如下所示:

    @Override
    // Characteristic notification
    public void onCharacteristicChanged(BluetoothGatt gatt,
    BluetoothGattCharacteristic characteristic) {
    broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
    }
    6、关闭客户端蓝牙

    public void close() {
    if (mBluetoothGatt == null) {
    return;
    }
    mBluetoothGatt.close();
    mBluetoothGatt = null;
    }


    在我的项目中的一小部分步骤总结:


    1,开启一个线程去扫描蓝牙设备,将有的蓝牙设备显示在ListView中,点击item触发监听去连接蓝牙设备,将蓝牙设备的地址返回给上一页
    2,得到Intent中的 BluetoothDevice.EXTRA_DEVICE ,开启 BlueToothService服务,将蓝牙的地址存储到sqlite中
    3,在BlueToothService 中bindService UartService 启动UartService
    注册广播接收器:UARTStatusChangeReceiver 用于连接、接收、发送的蓝牙服务
    注册广播接收器:BluetoothStateListener 用于监听蓝牙状态

    4,UartService 用于连接、接收、发送的蓝牙服务
    提供的connect方法:connect(final String address)
    1,判断传递过来的地址是否为空,
    2,判断以前是否连接过,
    3,通过mBluetoothAdapter.getRemoteDevice(address);得到蓝牙设备
    4,device.connectGatt(this, false, mGattCallback
    ---------------------
    作者:非鹭千里
    来源:CSDN
    原文:https://blog.csdn.net/dodan/article/details/52060446
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    flash 的计数器
    如何使用ActionScript来检测用户的语言及屏幕分辨率
    自己整理的MVC框架
    nginx系列(转发代理)
    nginx系列(nginx启动、关闭控制 windows)
    用户体验收录(将样式表放于页面的底部好不好)
    用户体验收录(从下拉菜单设计细节看Amazon对用户体验的把握)(转)
    .NET 4.5 异步IO
    visual studio 2012 update 2中的新功能
    ASP.NET Web API教程(四) 分页查询
  • 原文地址:https://www.cnblogs.com/freeliver54/p/9994975.html
Copyright © 2011-2022 走看看