[BlueZ] 2、使用bluetoothctl搜索、连接、配对、读写、使能notify蓝牙低功耗设备

发布时间:2018-11-15  栏目:LINUX  评论:0 Comments

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

前不久一个品种需要因此到低功耗蓝牙的开,由于事先未曾蓝牙开发的更,发现网上有关蓝牙开发之资料不多,不是凭描述一下虽是都不合时宜的,在此整治一首低功耗蓝牙的入门资料,能够形成使用蓝牙的承受以及殡葬数据。

图片 1

低功耗蓝牙 (BLE,Bluetooth Low Energy的简称) 从Android 4.3
开始支持,如今进一步多外设都是采取低功耗蓝牙来传输数据的,与经蓝牙本质上从不最多的区分,有为数不少相似之处在,工作流程都是:发现设备
–> 配对/绑定设备 –> 连接装置 –>
数据传。但是,低功耗蓝牙在安卓开中的施用及经蓝牙是一点一滴两样之,如果以之前十分熟悉的经文蓝牙开考虑来举行,说不定还见面踩坑。。。

1、前言

及一样篇讲话了争编译安装BlueZ-5,本篇主要在玩BlueZ,用命令行去操作BLE设备:

  • [BlueZ] 1、Download install and use the BlueZ and hcitool on PI
    3B+

图片 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

基本概念

先行来了解部分关于低功耗蓝牙的基本概念:

  • Generic Attribute Profile
    (GATT)
    ——全称叫做通用属性配置文件,GATT按照层级定义了三只概念,服务(Service)、特征(Characteristic)和讲述(Descriptor)。一个
    Service 包含多个 Characteristic,一个 Characteristic 包含几单
    Descriptor。
  • Characteristic——可以解吧一个看似,包含了一个 value
    和零至基本上个对该 value 的描述。
  • Descriptor——对 Characteristic 的叙述,例如克与计量单位等。
  • Service——Characteristic的集合。

这些概念并非深入去探索,有肯定了解开发的时段不至于一无所知就够了,想使实际了解低功耗蓝牙这里出篇是的文章。

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地图