菜单

安卓低功耗蓝牙的运用

2018年11月16日 - LINUX

星期三, 05. 九月 2018 02:03上午 – beautifulzzzz

近来一个档要用到低功耗蓝牙的付出,由于前从未蓝牙开发之经历,发现网上关于蓝牙开发的材料不多,不是管描述一下不怕是已过时的,在是整治一首低功耗蓝牙的入门资料,能够成功使用蓝牙的领与殡葬数据。

图片 1

低功耗蓝牙 (BLE,Bluetooth Low Energy的简称) 从Android 4.3
开始支持,如今尤为多外设都是应用低功耗蓝牙来传输数据的,与经蓝牙本质上未曾最多之分,有许多一般之处在,工作流程都是:发现设备
–> 配对/绑定设备 –> 连接装置 –>
数据传。但是,低功耗蓝牙在安卓付出被的行使以及藏蓝牙是全两样之,如果按照之前特别熟悉的经典蓝牙开合计来举行,说不定还见面踩坑。。。

1、前言

直达同首讲话了哪编译安装BlueZ-5,本篇主要在于玩BlueZ,用命令行去操作BLE设备:

图片 2

官方相关的开支指南:
经文蓝牙
低功耗蓝牙
低功耗蓝牙使用实例项目

2、gatttool —— 老工具趟坑

无独有偶起跟着 Get Started with Bluetooth Low Energy on
Linux
操作gatttool,发现坑太多(主要缘由是工具老矣):

采用sudo gatttool -b 4D:69:98:0E:91:5E -I去连接
意识会报错:Error: connect error: Connection refused (111)
最终参考LINK-11发现欲加random选项([\#1](https://stackoverflow.com/questions/32947807/cannot-connect-to-ble-device-on-raspberry-pi))

➜  ~  sudo gatttool -b 4D:69:98:0E:91:5E -I
[4D:69:98:0E:91:5E][LE]> connect
Attempting to connect to 4D:69:98:0E:91:5E
Error: connect error: Connection refused (111)
[4D:69:98:0E:91:5E][LE]> exit
➜  ~  sudo gatttool  -t random  -b 4D:69:98:0E:91:5E -I
[4D:69:98:0E:91:5E][LE]> connect
Attempting to connect to 4D:69:98:0E:91:5E
Connection successful
[4D:69:98:0E:91:5E][LE]> 
(gatttool:3104): GLib-WARNING **: Invalid file descriptor.

过一样回会10S自行断开,网上说之家伙老了,不建议用了([\#2](https://www.spinics.net/lists/linux-bluetooth/msg67617.html)):

There are new tools to use with GATT, bluetoothctl/bluetoothd is the preferred since with that you have GAP, etc, 
but if want to use a stand alone tool then I suggest you use btgatt-client.

图片 3

基本概念

先期来询问一些关于低功耗蓝牙的基本概念:

这些概念并非深入去追,有自然了解开发之时候不至于一无所知即足够了,想要切实了解低功耗蓝牙这里来首对的文章。

3、bluetoothctl——NB的新工具

一声令下行进入bluetoothctl操作环境([\#6](https://mcuoneclipse.com/2016/12/19/tutorial-ble-pairing-the-raspberry-pi-3-model-b-with-hexiwear/))

bluetoothctl

自身当手机上用lightblue模拟一个BLE设备ty_prod,之后对那service进行改动,调用scan
on进行搜索还是一直的,
末段发现如先行用remove移除之前的装备,之后还scan就见面冒出[NEW] Device 72:3B:E1:81:4E:4F ty_prod设备
注: 用lightblue模拟的设备的MAC不是原则性的
注:
我意识以lightblue中任怎么学BLE设备,一旦受连上查找到的service都是IPone的

[bluetooth]# devices
Device 28:ED:6A:A0:26:B7 ty_prod
Device 58:71:33:00:00:24 Bluetooth Keyboard
Device 00:1A:7D:DA:71:0A SHEN-PC
Device 94:87:E0:B3:AC:6F Mi Phone
[bluetooth]# remove 28:ED:6A:A0:26:B7 
...
[bluetooth]# scan on
Discovery started
[NEW] Device 72:3B:E1:81:4E:4F ty_prod
[bluetooth]# scan off
...
Discovery stopped
[bluetooth]# connect 72:3B:E1:81:4E:4F
Attempting to connect to 72:3B:E1:81:4E:4F
[CHG] Device 72:3B:E1:81:4E:4F Connected: yes
Connection successful
[ty_prod]

简直就就此IPhone自带的劳动做测试了~

[ty_prod]# info
Device 28:ED:6A:A0:26:B7 (public)
    Name: tuya_mdev_test
    Alias: tuya_mdev_test
    Appearance: 0x0040
    Icon: phone
    Paired: yes
    Trusted: no
    Blocked: no
    Connected: yes
    LegacyPairing: no
    UUID: Fax                       (00001111-0000-1000-8000-00805f9b34fb)
    UUID: Generic Access Profile    (00001800-0000-1000-8000-00805f9b34fb)
    UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
    UUID: Current Time Service      (00001805-0000-1000-8000-00805f9b34fb)
    UUID: Device Information        (0000180a-0000-1000-8000-00805f9b34fb)
    UUID: Battery Service           (0000180f-0000-1000-8000-00805f9b34fb)
    UUID: Vendor specific           (7905f431-b5ce-4e99-a40f-4b1e122d00d0)
    UUID: Vendor specific           (89d3502b-0f36-433a-8ef4-c502ad55f8dc)
    UUID: Vendor specific           (9fa480e0-4967-4542-9390-d343dc5d04ae)
    UUID: Vendor specific           (d0611e78-bbb4-4591-a5f8-487910ae4366)
[CHG] Device 28:ED:6A:A0:26:B7 ServicesResolved: no
[CHG] Device 28:ED:6A:A0:26:B7 Connected: no

俺们因此Current Time Service,列有装有attributes操作如下:

[tuya_mdev_test]# menu gatt
[tuya_mdev_test]# list-attributes 28:ED:6A:A0:26:B7
...
Primary Service
    /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0041
    00001805-0000-1000-8000-00805f9b34fb
    Current Time Service
Characteristic
    /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0041/char0045
    00002a0f-0000-1000-8000-00805f9b34fb
    Local Time Information
Characteristic
    /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0041/char0042
    00002a2b-0000-1000-8000-00805f9b34fb
    Current Time
Descriptor
    /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0041/char0042/desc0044
    00002902-0000-1000-8000-00805f9b34fb
    Client Characteristic Configuration
...

上面Current Time Service对应之服务要下图:

图片 4

咱们捎Current Time进行操作UUID:0x2A2B

[ty_prod]# select-attribute /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0041/char0042
[tuya_mdev_test:/service0041/char0042]# read
Attempting to read /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0041/char0042
[CHG] Attribute /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0041/char0042 Value:
  e2 07 09 05 01 24 11 03 f1 02                    .....$....      
  e2 07 09 05 01 24 11 03 f1 02                    .....$.... 
[tuya_mdev_test:/service0041/char0042]# attribute-info
Characteristic - Current Time
    UUID: 00002a2b-0000-1000-8000-00805f9b34fb
    Service: /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0041
    Value:
  e2 07 09 05 01 2e 01 03 f5 02                    ..........      
    Notifying: yes
    Flags: read
    Flags: notify

念来结果大致意思应该是:2018-9/5-1:36:17 周三

读取一下0x180A的Device Information:

[tuya_mdev_test:/service0006/char0007]# select-attribute /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0047/char004a
[tuya_mdev_test:/service0047/char004a]# attribute-info
Characteristic - Model Number String
    UUID: 00002a24-0000-1000-8000-00805f9b34fb
    Service: /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0047
    Flags: read
[tuya_mdev_test:/service0047/char004a]# read
Attempting to read /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0047/char004a
[CHG] Attribute /org/bluez/hci0/dev_47_B1_26_C1_81_18/service0047/char004a Value:
  69 50 68 6f 6e 65 36 2c 32                       iPhone6,2       
  69 50 68 6f 6e 65 36 2c 32                       iPhone6,2    

自写、使能notify也颇粗略,看help即可。最后断开连接、并脱离!!!

[tuya_mdev_test:/service0047/char004a]# disconnect 28:ED:6A:A0:26:B7
Attempting to disconnect from 28:ED:6A:A0:26:B7
[CHG] Device 28:ED:6A:A0:26:B7 ServicesResolved: no
Successful disconnected
[CHG] Device 28:ED:6A:A0:26:B7 Connected: no
[bluetooth]# quit

图片 5

低功耗蓝牙开步骤

LINKS

[1].Cannot connect to BLE device on Raspberry
Pi
[2].Invalid file descriptor gatttool of bluez
5.32
[3].Get Started with Bluetooth Low Energy on
Linux
[4].Reverse Engineering a Bluetooth Low Energy Light
Bulb
[5].Doing Bluetooth Low Energy on
Linux
[6].Tutorial: BLE Pairing the Raspberry Pi 3 Model B with
Hexiwear

图片 6

@beautifulzzzz
智能硬件、物联网,热爱技术,关注产品
博客:http://blog.beautifulzzzz.com
园友交流群:414948975
1.声称权限

使用蓝牙功能首先要声明相关的权,比如:

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

以,也不怕可以通过蓝牙特点配置来界定支持蓝牙功能的配备用APP:

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

还是通过在代码中判断,不过本为主无呀手机不支持蓝牙功能了咔嚓。

// Use this check to determine whether BLE 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();
}

急需留意的是,官方说明 Android 5.0
及以上设备以蓝牙时还亟需稳定权限,需要注意的凡开发之早晚要是于
Android 6.0
及以上设备的内需动态获取一定权限,否则蓝牙功能为是力不从心采取的。

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> 
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<manifest ... >
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
    <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
    <uses-feature android:name="android.hardware.location.gps" />
    ...
</manifest>
2.初始化蓝牙适配器
private BluetoothAdapter mBluetoothAdapter;
...
// Initializes Bluetooth adapter.
final BluetoothManager bluetoothManager =
        (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
3.开蓝牙

以开班扫描发现蓝牙设备之前要确保手机的蓝牙功能打开。

if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    // 申请打开蓝牙
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

然后在 onActivityResultI道吃判断用户是否允许开启蓝牙功能。

4.意识设备
private static final long SCAN_PERIOD = 10000;
private void scanLeDevice(final boolean 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);
}

/**
* 发现设备的回调
*/
private BluetoothAdapter.LeScanCallback mLeScanCallback =
        new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
            byte[] scanRecord) {

    }
};
5.连续装置
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

一连装置的措施要传入三个参数,第一单凡是 context,第二单凡是
boolean,表示是否自动连续,第三独凡是接连的回调接口,其中有几个老重大的办法。

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                mConnectionState = STATE_CONNECTED;
                // 开始查找服务,只有找到服务才算是真的连接上
                mBluetoothGatt.discoverServices();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {

            } else {

            }
        }

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

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
              byte[] data = characteristic.getValue();
        }
    };

连年装置成功后用在回调方法被发现服务mBluetoothGatt.discoverServices(),发现服务后会见回调onServicesDiscovered方式,发现服务成功才总算真正的连续达蓝牙设备。onCharacteristicWrite主意是摹写操作结果的回调,onCharacteristicChanged术是状态改变之回调,在拖欠措施被可知取得蓝牙发送的数量。不过,在接收数据之前,我们必须对Characteristic设置监听才能够吸收至蓝牙底数码。

6.Characteristic监听设置
public void setNotification() {

    BluetoothGattService service = mBluetoothGatt.getService(SERVICE_UUID);
    if (service == null) {
        L.e("未找到蓝牙中的对应服务");
        return;
    }
    BluetoothGattCharacteristic characteristic= service.getCharacteristic(CharacteristicUUID);
    if (characteristic== null) {
        L.e("未找到蓝牙中的对应特征");
        return;
    }
    //设置true为启用通知,false反之
    mBluetoothGatt.setCharacteristicNotification(characteristic, true);

    //下面为开启蓝牙notify功能,向CCCD中写入值1
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD);
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    mBluetoothGatt.writeDescriptor(descriptor);
}

先经过 UUID
找到我们用展开数据传的service,在找到我们想如果设置监听的Characteristic的
UUID
找到响应的characteristic对象,找到响应的characteristic后调用setCharacteristicNotification计启用通知,则该characteristic状态来变更后就见面回调onCharacteristicChanged方式,而被蓝牙的
notify 功能要为 UUID 为 CCCD 的 descriptor 中描写副值1,其中 CCCD
的价为:

public static final UUID CCCD = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
7.往蓝牙发送数据

往蓝牙发送数据,可以领略也吃蓝牙的characteristic设置数据。

public void writeRXCharacteristic(byte[] value) {
        if (mBluetoothGatt == null) {
            return;
        }
        BluetoothGattService service= mBluetoothGatt.getService(SERVICE_UUID);
        BluetoothGattCharacteristic characteristic= service.getCharacteristic(UUID);
        characteristic.setValue(value);
        mBluetoothGatt.writeCharacteristic(characteristic);
    }

我们可以形容副Stringbyte[]的数据,一般为byte[]。其中我们得跟谁service的characteristic进行数据传可以沟通硬件工程师或者查看蓝牙设备供应商提供的证实获得。我们吧可经过mBluetoothGatt.getServices()mBluetoothGatt.getgetCharacteristics()主意取得蓝牙设备的富有
service 和某 service 中的备 characteristic。

8.数分包处理

低功耗蓝牙一次性只能发送 20 独字节的数码,超过 20
个字节的无法发送,因此需要对发送的多少开展分包处理,在此于有多少分包的一个演示,是在别人
github 中看出底:

//存储待发送的数据队列
    private Queue<byte[]> dataInfoQueue = new LinkedList<>();
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            send();
        }
    };

    /**
     * 向characteristic写数据
     *
     * @param value
     */
    public void writeCharacteristic(BluetoothGattCharacteristic characteristic, byte[] value) {
        this.mCharacteristic = characteristic;
        if (dataInfoQueue != null) {
            dataInfoQueue.clear();
            dataInfoQueue = splitPacketFor20Byte(value);
            handler.post(runnable);
        }
        // characteristic.setValue(value);
        // mBluetoothGatt.writeCharacteristic(characteristic);
    }

    private void send() {
        if (dataInfoQueue != null && !dataInfoQueue.isEmpty()) {
            //检测到发送数据,直接发送
            if (dataInfoQueue.peek() != null) {
                this.mCharacteristic.setValue(dataInfoQueue.poll());//移除并返回队列头部的元素
                mBluetoothGatt.writeCharacteristic(mCharacteristic);
            }
            //检测还有数据,延时后继续发送,一般延时100毫秒左右
            if (dataInfoQueue.peek() != null) {
                handler.postDelayed(runnable, 200);
            }
        }
    }

    //数据分包处理
    private Queue<byte[]> splitPacketFor20Byte(byte[] data) {
        Queue<byte[]> dataInfoQueue = new LinkedList<>();
        if (data != null) {
            int index = 0;
            do {
                byte[] surplusData = new byte[data.length - index];
                byte[] currentData;
                System.arraycopy(data, index, surplusData, 0, data.length - index);
                if (surplusData.length <= 20) {
                    currentData = new byte[surplusData.length];
                    System.arraycopy(surplusData, 0, currentData, 0, surplusData.length);
                    index += surplusData.length;
                } else {
                    currentData = new byte[20];
                    System.arraycopy(data, index, currentData, 0, 20);
                    index += 20;
                }
                dataInfoQueue.offer(currentData);
            } while (index < data.length);
        }
        return dataInfoQueue;
    }

这篇稿子简单介绍了安卓进行低功耗蓝牙开之流程与涉嫌了有注意事项,看罢本文基本就可知进行低功耗蓝牙开,除这之外,强烈推荐去
Github
看一下低功耗蓝牙使用实例项目,例子不难理解,看懂了
Demo 能针对低功耗蓝牙的采用有更老的了解。

正文原文地址:http://electhuang.com/2017/07/10/android-ble/

相关文章

标签:

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图