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

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

图片 1

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

不久前一个门类用为此到低功耗蓝牙的支出,由于之前从未蓝牙开发的阅历,发现网上关于蓝牙开发之素材不多,不是凭描述一下即使是就不合时宜的,在这个整治一首低功耗蓝牙的入门资料,能够不辱使命使用蓝牙的受和殡葬数据。

1、前言

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

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

图片 2

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。

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相应的服务一旦下图:

图片 3

我们挑选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

图片 4

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>

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

基本概念

先期来打听部分有关低功耗蓝牙的基本概念:

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

这些概念并非深入去探索,有必然了解开发之时段不至于一无所知就算够用了,想使实际了解低功耗蓝牙这里有首对的文章。

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.

图片 5

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

图片 6

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设置监听才能够接收到蓝牙之数。

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/

3.开蓝牙

于开头扫描发现蓝牙设备之前用保证手机的碧蓝牙功能打开。

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

然后在 onActivityResultI办法被判断用户是否同意开启蓝牙功能。

官相关的出指南:
藏蓝牙
低功耗蓝牙
低功耗蓝牙使用实例项目

低功耗蓝牙开步骤

2.初始化蓝牙适配器
private BluetoothAdapter mBluetoothAdapter;
...
// Initializes Bluetooth adapter.
final BluetoothManager bluetoothManager =
        (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();

低功耗蓝牙 (BLE,Bluetooth Low Energy的简称) 从Android 4.3
开始支持,如今越发多外设都是采取低功耗蓝牙来传输数据的,与经典蓝牙本质上从来不最好多的分别,有那么些相似之处,工作流程都是:发现设备
–> 配对/绑定设备 –> 连接装置 –>
数据传。但是,低功耗蓝牙在安卓支出中之动与经典蓝牙是截然不同的,如果按事先那个熟稔的藏蓝牙开考虑来开,说不定还会踩坑。。。

发表评论

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

网站地图xml地图