Android BLE编程指南

Bluetooth Low Energy

In this document

1.    Key Terms and Concepts

1.    Roles and Responsibilities

2.    BLE Permissions

3.    Setting Up BLE

4.    Finding BLE Devices

5.    Connecting to a GATT Server

6.    Reading BLE Attributes

7.    Receiving GATT Notifications

8.    Closing the Client App

Key classes

1.    BluetoothGatt

2.    BluetoothGattCallback

3.    BluetoothGattCharacteristic

4.    BluetoothGattService

Related samples

1.    Bluetooth LE sample

See Also

1.    Best Practices for Bluetooth Development (video)

VIDEO

DevBytes: Bluetooth Low Energy API

 

Android 4.3 (API Level 18)introduces built-in platform support for Bluetooth Low Energy in the central role and provides APIs that appscan use to discover devices, query for services, and read/writecharacteristics. In contrast to Classic Bluetooth, Bluetooth Low Energy (BLE) is designed to providesignificantly lower power consumption. This allows Android apps to communicatewith BLE devices that have low power requirements, such as proximity sensors,heart rate monitors, fitness devices, and so on.

Key Terms and Concepts


Here is a summary of key BLEterms and concepts:

·        Generic Attribute Profile(GATT)—The GATTprofile is a general specification for sending and receiving short pieces ofdata known as "attributes" over a BLE link. All current Low Energyapplication profiles are based on GATT.

o   The Bluetooth SIG defines many profiles for LowEnergy devices. A profile is a specification for how a device works in a particularapplication. Note that a device can implement more than one profile. Forexample, a device could contain a heart rate monitor and a battery leveldetector.

·        Attribute Protocol (ATT)—GATT is built on top of theAttribute Protocol (ATT). This is also referred to as GATT/ATT. ATT isoptimized to run on BLE devices. To this end, it uses as few bytes as possible.Each attribute is uniquely identified by a Universally Unique Identifier(UUID), which is a standardized 128-bit format for a string ID used to uniquelyidentify information. The attributestransported by ATT areformatted as characteristics and services.

·        Characteristic—A characteristic contains asingle value and 0-n descriptors that describe the characteristic's value. Acharacteristic can be thought of as a type, analogous to a class. 

·        Descriptor—Descriptors are definedattributes that describe a characteristic value. For example, a descriptormight specify a human-readable description, an acceptable range for acharacteristic's value, or a unit of measure that is specific to acharacteristic's value.

·        Service—A service is a collection ofcharacteristics. For example, you could have a service called "Heart RateMonitor" that includes characteristics such as "heart ratemeasurement." You can find a list of existing GATT-based profiles andservices on bluetooth.org.

Roles and Responsibilities

Here are the roles andresponsibilities that apply when an Android device interacts with a BLE device:

·        Central vs. peripheral. This applies to the BLE connection itself. Thedevice in the central role scans, looking for advertisement, and the device inthe peripheral role makes the advertisement.

·        GATT server vs. GATT client. This determines how two devices talk to eachother once they've established the connection.

To understand the distinction,imagine that you have an Android phone and an activity tracker that is a BLEdevice. The phone supports the central role; the activity tracker supports theperipheral role (to establish a BLE connection you need one of each—two thingsthat only support peripheral couldn't talk to each other, nor could two thingsthat only support central).

Once the phone and theactivity tracker have established a connection, they start transferring GATTmetadata to one another. Depending on the kind of data they transfer, one orthe other might act as the server. For example, if the activity tracker wantsto report sensor data to the phone, it might make sense for the activitytracker to act as the server. If the activity tracker wants to receive updatesfrom the phone, then it might make sense for the phone to act as the server.

In the example used in thisdocument, the Android app (running on an Android device) is the GATT client.The app gets data from the GATT server, which is a BLE heart rate monitor thatsupports the Heart Rate Profile. But you could alternatively design your Android appto play the GATT server role. See BluetoothGattServer for more information.

BLE Permissions


In order to use Bluetoothfeatures in your application, you must declare the Bluetooth permission BLUETOOTH. You need this permission toperform any Bluetooth communication, such as requesting a connection, acceptinga connection, and transferring data.

If you want your app toinitiate device discovery or manipulate Bluetooth settings, you must alsodeclare theBLUETOOTH_ADMIN permission. Note: If you use the BLUETOOTH_ADMIN permission, then you must also have theBLUETOOTH permission.

Declare the Bluetoothpermission(s) in your application manifest file. For example:

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

If you want to declare thatyour app is available to BLE-capable devices only, include the following inyour app's manifest:

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

However, if you want to makeyour app available to devices that don't support BLE, you should still includethis element in your app's manifest, but set required="false". Then at run-time you can determine BLE availabilityby using PackageManager.hasSystemFeature():

// Use this check to determine whetherBLE is supported on the device. Then
// you can selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}

Setting Up BLE


Before your application cancommunicate over BLE, you need to verify that BLE is supported on the device,and if so, ensure that it is enabled. Note that this check is only necessary if <uses-feature.../> is set to false.

If BLE is not supported, thenyou should gracefully disable any BLE features. If BLE is supported, butdisabled, then you can request that the user enable Bluetooth without leavingyour application. This setup is accomplished in two steps, using the BluetoothAdapter.

1.    Get the BluetoothAdapter

The BluetoothAdapter is required for any and all Bluetooth activity. The BluetoothAdapter represents the device's own Bluetooth adapter (the Bluetooth radio).There's one Bluetooth adapter for the entire system, and your application caninteract with it using this object. The snippet below shows how to get theadapter. Note that this approach uses getSystemService() to return an instance of BluetoothManager, which is then used to getthe adapter. Android 4.3 (API Level 18) introduces BluetoothManager:

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

2.    Enable Bluetooth

Next, you need to ensure that Bluetooth is enabled.Call isEnabled() to check whether Bluetooth is currently enabled. If this method returnsfalse, then Bluetooth is disabled. The following snippet checks whetherBluetooth is enabled. If it isn't, the snippet displays an error prompting theuser to go to Settings to enable Bluetooth:

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);
}

Finding BLE Devices


To find BLE devices, you usethe startLeScan() method. This method takes aBluetoothAdapter.LeScanCallback as a parameter. You must implement this callback, because that is how scanresults are returned. Because scanning is battery-intensive, you should observethe following guidelines:

·        As soon as you find the desired device, stop scanning.

·        Never scan on a loop, and set a time limit on your scan. A device that waspreviously available may have moved out of range, and continuing to scan drainsthe battery.

The following snippet showshow to start and stop a scan:

/**
 * 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) {
            // Stopsscanning 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);
        }
        ...
    }
...
}

If you want to scan for onlyspecific types of peripherals, you can instead call startLeScan(UUID[],BluetoothAdapter.LeScanCallback), providing an array of UUID objects that specify the GATT services your app supports.

Here is an implementation ofthe BluetoothAdapter.LeScanCallback, which is the interface usedto deliver BLE scan results:

private LeDeviceListAdaptermLeDeviceListAdapter;
...
// 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();
           }
       });
   }
};

Note: You can only scan forBluetooth LE devices or scan for Classic Bluetoothdevices, as described inBluetooth. You cannot scan for both Bluetooth LE and classicdevices at the same time.

Connecting to a GATT Server


The first step in interactingwith a BLE device is connecting to it— more specifically, connecting to theGATT server on the device. To connect to a GATT server on a BLE device, you usethe connectGatt() method. This method takes three parameters: a Context object, autoConnect (boolean indicating whether to automatically connect to the BLE device assoon as it becomes available), and a reference to a BluetoothGattCallback:

mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

This connects to the GATTserver hosted by the BLE device, and returns a BluetoothGatt instance, which you can then use to conduct GATT client operations. Thecaller (the Android app) is the GATT client. TheBluetoothGattCallback is used to deliver results to the client, such as connection status, aswell as any further GATT client operations.

In this example, the BLE appprovides an activity (DeviceControlActivity) to connect, display data,and display GATT services and characteristics supported by the device. Based onuser input, this activity communicates with a Service called BluetoothLeService, which interacts with the BLEdevice via the Android BLE API:

// A service that interacts with the BLEdevice 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 StringmBluetoothDeviceAddress;
    private BluetoothGatt mBluetoothGatt;
    private int mConnectionState =STATE_DISCONNECTED;

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

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

    public final static UUIDUUID_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 voidonConnectionStateChange(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, "Connectedto GATT server.");
                Log.i(TAG, "Attemptingto start service discovery:" +
                       mBluetoothGatt.discoverServices());

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

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

        @Override
        // Result of a characteristic readoperation
        public voidonCharacteristicRead(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic,
                int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }
     ...
    };
...
}

When a particular callback istriggered, it calls the appropriate broadcastUpdate() helper method and passes it an action. Note that the data parsing in thissection is performed in accordance with the Bluetooth Heart Rate Measurement profile specifications:

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 RateMeasurement 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 rateformat UINT16.");
        } else {
            format = BluetoothGattCharacteristic.FORMAT_UINT8;
            Log.d(TAG, "Heart rateformat UINT8.");
        }
        final int heartRate = characteristic.getIntValue(format, 1);
        Log.d(TAG, String.format("Receivedheart rate: %d", heartRate));
        intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
    } else {
        // For all other profiles, writes the dataformatted 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) + "\n" +
                   stringBuilder.toString());
        }
    }
    sendBroadcast(intent);
}

Back in DeviceControlActivity, these events are handled bya BroadcastReceiver:

// Handles various events fired by theService.
// 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 thesupported services and characteristics on the
            // userinterface.
            displayGattServices(mBluetoothLeService.getSupportedGattServices());
        } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
            displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
        }
    }
};

Reading BLE Attributes


Once your Android app hasconnected to a GATT server and discovered services, it can read and writeattributes, where supported. For example, this snippet iterates through theserver's services and characteristics and displays them in the UI:

public class DeviceControlActivity extends Activity {
    ...
    // Demonstrates how to iterate through the supported GATT
    // Services/Characteristics.
    // In this sample, we populate the data structure that isbound to the
    // ExpandableListView on the UI.
    private voiddisplayGattServices(List<BluetoothGattService> gattServices) {
        if (gattServices == null) return;
        String uuid = null;
        StringunknownServiceString = getResources().
                getString(R.string.unknown_service);
        StringunknownCharaString = 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 throughavailable Characteristics.
            for (BluetoothGattCharacteristicgattCharacteristic :
                   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);
         }
    ...
    }
...
}

Receiving GATT Notifications


It's common for BLE apps toask to be notified when a particular characteristic changes on the device. Thissnippet shows how to set a notification for a characteristic, using the setCharacteristicNotification()method:

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);

Once notifications are enabledfor a characteristic, an onCharacteristicChanged() callback is triggered if the characteristic changes on the remote device:

@Override
// Characteristic notification
public void onCharacteristicChanged(BluetoothGatt gatt,
        BluetoothGattCharacteristic characteristic) {
    broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}

Closing the Client App


Once your app has finishedusing a BLE device, it should call close() so the system can release resources appropriately:

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值