4194|1

4

帖子

0

TA的资源

一粒金砂(初级)

楼主
 

Android平台BLE低功耗蓝牙开发 [复制链接]

安卓4.3(API 18)为BLE的核心功能提供平台支持和API,App可以利用它来发现设备、查询服务和读写特性。相比传统的蓝牙,BLE更显著的特点是低功耗。这一优 点使android App可以与具有低功耗要求的BLE设备通信,如近距离传感器、心脏速率监视器、健身设备等。

BLE权限

为了在app中使用蓝牙功能,必须声明蓝牙权限BLUETOOTH。利用这个权限去执行蓝牙通信,例如请求连接、接受连接、和传输数据。

如果想让你的app启动后能够发现或操纵蓝牙设置,必须声明BLUETOOTH_ADMIN权限。注意:如果你使用BLUETOOTH_ADMIN权限,你也必须声明BLUETOOTH权限。

在你的app manifest文件中声明蓝牙权限。

如果想声明你的app只为具有BLE的设备提供,在manifest文件中包括:

但是如果想让你的app提供给那些不支持BLE的设备,需要在manifest中包括上面代码并设置required="false",然后在运行时可以通过使用PackageManager.hasSystemFeature()确定BLE的可用性。

// 使用此检查确定BLE是否支持在设备上,然后你可以有选择性禁用BLE相关的功能

if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {

Toast.makeText(this,R.string.ble_not_supported, Toast.LENGTH_SHORT).show();

    finish();

}

设置BLE

你的app能与BLE通信之前,你需要确认手机是否支持BLE,如果支持,确认已经启用。注意如果设置为false,这个检查才是必需的。

如果不支持BLE,那么你应该适当地禁用部分BLE功能。如果支持BLE但被禁用,你可以无需离开应用程序而要求用户启动蓝牙。使用BluetoothAdapter两步完成该设置。

获取 BluetoothAdapter

所有的蓝牙活动都需要蓝牙适配器。BluetoothAdapter代表手机本身的蓝牙适配器(蓝牙无线)。整个系统只有一个蓝牙适配器,而且你的app使用它与系统交互。下面的代码片段显示了如何得到适配器。注意该方法使用getSystemService()]返回BluetoothManager,然后将其用于获取适配器的一个实例。Android 4.3(API 18)引入BluetoothManager。

// 初始化蓝牙适配器

final BluetoothManager bluetoothManager =

(BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);

mBluetoothAdapter = bluetoothManager.getAdapter();

开启蓝牙

接下来,你需要确认蓝牙是否开启。调用isEnabled())去检测蓝牙当前是否开启。如果该方法返回false,蓝牙被禁用。下面的代码检查蓝牙是否开启,如果没有开启,将显示错误提示用户去设置开启蓝牙。

// 确保蓝牙在手机上可以开启

if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {

   Intent enableBtIntent = newIntent (BluetoothAdapter.ACTION_REQUEST_ENABLE);

   startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

}

发现BLE设备

为了发现BLE设备,使用startLeScan())方法。这个方法需要一个参数BluetoothAdapter.LeScanCallback。你必须实现它的回调函数,那就是返回的扫描结果。因为扫描非常消耗电量,你应当遵守以下准则:

只要找到所需的设备,停止扫描。

不要在循环里扫描,并且对扫描设置时间限制。以前可用的设备可能已经移出范围,继续扫描消耗电池电量。

下面代码显示了如何开始和停止一个扫描:

/**

* 扫描和显示可以提供的蓝牙设备.

*/

public class DeviceScanActivity extends ListActivity {

    private BluetoothAdapter mBluetoothAdapter;

    private boolean mScanning;

    private Handler mHandler;

    // 10秒后停止寻找.

    private static final long SCAN_PERIOD = 10000;

    ...

    private void scanLeDevice(final boolean enable) {

        if (enable) {

// 经过预定扫描期后停止扫描

            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)),需要提供你的app支持的GATT Services的UUID对象数组。

作为BLE扫描结果的接口,下面是BluetoothAdapter.LeScanCallback的实现。

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

           }

       });

   }

};

连接到GATT服务端

与一个BLE设备交互的第一步就是连接它——更具体的,连接到BLE设备上的GATT服务端。为了连接到BLE设备上的GATT 服务端,需要使用connectGatt( )方法。这个方法需要三个参数:一个Context对象,自动连接(boolean值,表示只要BLE设备可用是否自动连接到它),和 BluetoothGattCallback调用。

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

连接到GATT服务端时,由BLE设备做主机,并返回一个BluetoothGatt实例,然后你可以使用这个实例来进行 GATT客户端操作。请求方(Android app)是GATT客户端。BluetoothGattCallback用于传递结果给用户,例如连接状态,以及任何进一步GATT客户端操作。

在这个例子中,这个BLE APP提供了一个activity(DeviceControlActivity)来连接,显示数据,显示该设备支持的GATT Services和Characteristics。根据用户的输入,这个activity与BluetoothLeService通信,通过 Android BLE API实现与BLE设备交互。

//通过BLE API服务端与BLE设备交互

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

    //通过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

        // 发现新服务端

        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

        // 读写特性

        public void onCharacteristicRead(BluetoothGatt gatt,

                BluetoothGattCharacteristic Characteristic,

                int status) {

            if (status == BluetoothGatt.GATT_SUCCESS) {

                broadcastUpdate(ACTION_DATA_AVAILABLE, Characteristic);

            }

        }

     ...

    };

...

}

当一个特定的回调被触发的时候,它会调用相应的broadcastUpdate()辅助方法并且传递给它一个action。注意在该部分中的数据解析按照蓝牙心率测量配置文件规格进行。

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

    // 这是心率测量配置文件。

    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 {

        // 对于所有其他的配置文件,用十六进制格式写数据

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

}

返回DeviceControlActivity, 这些事件由一个BroadcastReceiver来处理:

// 通过服务控制不同的事件

// ACTION_GATT_CONNECTED: 连接到GATT服务端

// ACTION_GATT_DISCONNECTED: 未连接GATT服务端.

// ACTION_GATT_SERVICES_DISCOVERED: 未发现GATT服务.

// ACTION_DATA_AVAILABLE: 接受来自设备的数据,可以通过读或通知操作获得。

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

            // 在用户接口上展示所有的Services and Characteristics

            displayGattServices(mBluetoothLeService.getSupportedGattServices());

        } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {

            displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));

        }

    }

};

关闭客户端App

当你的app完成BLE设备的使用后,应该调用close( ),系统可以合理释放占用资源。

public void close() {

    if (mBluetoothGatt == null) {

        return;

    }

    mBluetoothGatt.close();

    mBluetoothGatt = null;

}




此帖出自无线连接论坛

最新回复

mark了。谢谢楼主  详情 回复 发表于 2016-1-15 10:25
点赞 关注
 

回复
举报

2452

帖子

19

TA的资源

五彩晶圆(中级)

沙发
 
mark了。谢谢楼主
此帖出自无线连接论坛
个人签名    懒得很
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
立即报名 | 2025 瑞萨电子工业以太网技术日即将开启!
3月-4月 深圳、广州、北京、苏州、西安、上海 走进全国6城
2025瑞萨电子工业以太网技术巡回沙龙聚焦工业4.0核心需求,为工程师与企业决策者提供实时通信技术最佳解决方案。
预报从速,好礼等您拿~

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表