1、在建立BLE设备进行通信时注意3点

1.1、蓝牙位置权限必须动态申请(Android 6.0以上),如果没有动态申请会出现下面的问题
Need BLUETOOTH permission: Neither user 10063 nor current process has android.permission.BLUETOOTH.
1.2、尽管设置了BluetoothGatt中的setCharacteristicNotification(),但无法触发BluetoothGattCallback中的onCharacteristicChanged()方法
需要重新写setCharacteristicNotification()方法
1.3、读写和设置通知都是单步操作,必须执行完一个才能执行第二个,否则会操作失败

2、BLE通信基本流程

作者认为BLE连接与通信关键点是两个回调。
2.1、蓝牙扫描回调(BluetoothAdapter.LeScanCallback、ScanCallback(此方法使用不在赘述,参见作者的蓝牙扫描(简单)))
如果蓝牙扫描成功会执行onLeScan方法,可以获得设备的基本信息,例如:设备名字、设备地址

2.2、蓝牙连接回调(BluetoothGattCallback)
这个回调方法比较多需要细看。这个回调中常用的有以下几个方法:
onConnectionStateChange:当回调执行、状态发生变化时执行,在此方法中常用来搜索服务(BluetoothGatt#discoverServices())
onServicesDiscovered:当搜索服务执行时执行,在此方法中可以获得蓝牙设备服务的相关信息和特征的信息,例如:服务的UUID、特征的UUID、特征的属性等。
onCharacteristicWrite:当向蓝牙设备发送写命令(BluetoothGatt#writeCharacteristic())时执行,在此方法中可以获得蓝牙设备发送命令的执行状态。
onCharacteristicChanged:当向蓝牙设备发送写命令成功,并且提醒移动设备接收蓝牙设备返回的数据(BluetoothGatt#setCharacteristicNotification())时执行,此方法用来处理蓝牙返回的数据。
onCharacteristicRead:当向蓝牙设备发送读命令(BluetoothGatt#readCharacteristic())执行。

2.3、建立通信的过程
动态申请蓝牙位置权限;
获得蓝牙适配器、判断蓝牙是否支持、蓝牙是否打开;
扫描蓝牙(调用BluetoothAdapter.LeScanCallback、或者调用ScanCallback),在onLeScan(或者调用onScanResult)中处理蓝牙回调的蓝牙设备信息;
与蓝牙设备建立连接(调用BluetoothGattCallback),首先在onConnectionStateChange()方法中搜索服务,然后在onServicesDiscovered()方法中获得服务的UUID、特征的UUID和目标特征,根据服务的UUID、特征的UUID和目标特征与蓝牙设备建立连接,并向设备发送写命令,同时提醒移动设备接收蓝牙数据;
在onCharacteristicChanged方法中接收蓝牙返回的数据。

2.4、蓝牙通用唯一识别码(UUID,Universally Unique Identifier)的理解
一个设备中有多个服务(多个服务的UUID),一个服务中有多个特征(多个特征的UUID),多个设备的UUID可以都相同。
例如:
设备1:dev-01{server1(ser_UUID-01):{characteristic1_1(char1_UUID-01),characteristic1_2(char1_UUID-02),characteristic1_3(char1_UUID-03)},
server2(ser_UUID-02):{characteristic2_1(char2_UUID-01),characteristic2_2(char2_UUID-02),characteristic2_3,(char2_UUID-03)}}

设备2:dev-02{server1(ser_UUID-01):{characteristic1_1(char1_UUID-01),characteristic1_2(char1_UUID-02),characteristic1_3(char1_UUID-03)},
server2(ser_UUID-02):{characteristic2_1(char2_UUID-01),characteristic2_2(char2_UUID-02),characteristic2_3,(char2_UUID-03)}}

3、权限

<uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="true" />

4、xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <Button
        android:id="@+id/search"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="搜索"/>

<Button
        android:id="@+id/link"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="连接"/>

<Button
        android:id="@+id/getData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="接收数据"/>

<TextView
        android:id="@+id/result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="数据字节长度"/>
    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </ListView>
</LinearLayout>

5、java代码

public class MainActivity extends AppCompatActivity {

private ArrayList<String> data = null;
    private ListView listView = null;
    private BluetoothAdapter bta = null;
    private BluetoothManager btm = null;
    private Button button = null;
    private Button link = null;
    private Button getDataButton = null;
    private TextView result = null;

ArrayAdapter<String> adapter = null;

private  BluetoothGatt btg = null;
    private BluetoothDevice mDevice=null;
    private Handler handler = new Handler();

private boolean isScanning = true;
    private boolean isLinking = false;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

// 获得数据
        this.data = new ArrayList<>();
        // 初始化控件
        this.initView();
        this.checkBlePermission();
    }

// 初始化控件
    private void initView() {
        this.result = findViewById(R.id.result);

// 获得button
        this.button = findViewById(R.id.search);
        this.button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 扫描蓝牙
                scanBle();
            }
        });

this.link = findViewById(R.id.link);
        this.link.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(!isScanning){
                    linkBlue();
                }else {
                    Toast.makeText(MainActivity.this, "没有扫描到蓝牙设备",Toast.LENGTH_SHORT).show();
                }
            }
        });

this.getDataButton = findViewById(R.id.getData);
        this.getDataButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(isLinking){
                    getData();
                }else {
                    Toast.makeText(MainActivity.this, "蓝牙设备没建立连接",Toast.LENGTH_SHORT).show();
                }
            }
        });

// 初始化ListView
        this.adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, this.data);
        this.listView = findViewById(R.id.lv);
        this.listView.setAdapter(adapter);
    }

/*----- 搜索蓝牙设备 -----*/
    // 扫描蓝牙
    private void scanBle() {
        btm = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        // 初始化蓝牙适配器
        if(btm == null){
            return;
        }
        bta = btm.getAdapter();
        if(bta == null){
            this.result.setText("The bluetooth not support");
            this.result.setVisibility(View.VISIBLE);
            return;
        }

// 打开蓝牙
        if (!bta.isEnabled()) {
            Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enable, 1);
        } else {
            bta.startLeScan(oldBtsc);
            // 设置扫描时间
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    bta.stopLeScan(oldBtsc);
                    Log.d("------------","停止扫描");
                }
            }, 8000);
        }
    }
    private BluetoothAdapter.LeScanCallback oldBtsc = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            if(device.getName().equals("ECG-XA01")){
                mDevice=device;

data.add(device.getAddress() + ""+ device.getName());
                Log.d("------------", device.getAddress());

adapter.notifyDataSetChanged();
                bta.stopLeScan(oldBtsc);

isScanning = false;
            }

}
    };

/*----- 搜索蓝牙服务和特征 -----*/
    // 连接蓝牙
    private void linkBlue(){
        btg = mDevice.connectGatt(MainActivity.this, true, btgcb);
    }

// 获得数据
    private void getData(){
        BluetoothGattService service = btg.getService(UUID.fromString("0000180F-0000-1000-8000-00805F9B34FB"));
        BluetoothGattCharacteristic characteristic1= service.getCharacteristic(UUID.fromString("11111102-5544-7766-9988-AABBCCDDEEFF"));
        setCharacteristicNotification(characteristic1, true);
        if(btg.writeCharacteristic(characteristic1)){
            Log.d("------------","writeCharacteristic");
        }
    }
    private BluetoothGattCallback btgcb = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            if(newState == BluetoothGatt.STATE_CONNECTED){
                Log.d("------------","ConnectionStateChange");
                // 搜索服务
                btg.discoverServices();
            }
        }

@Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            Log.d("------------","ServicesDiscovered");

if (status == BluetoothGatt.GATT_SUCCESS) {
                List<BluetoothGattService> supportedGattServices = btg.getServices();
                for(int i=0;i<supportedGattServices.size();i++){
                    Log.d("------------","1:BluetoothGattService UUID=:"+supportedGattServices.get(i).getUuid());
                    List<BluetoothGattCharacteristic> listGattCharacteristic=supportedGattServices.get(i).getCharacteristics();
                    for(int j=0;j<listGattCharacteristic.size();j++){
                        Log.d("------------","2:BluetoothGattCharacteristic UUID=:"+listGattCharacteristic.get(j).getUuid());
                    }
                }

BluetoothGattService service1 = btg.getService(UUID.fromString("0000300F-0000-1000-8000-00805F9B34FB"));
                final BluetoothGattCharacteristic characteristic1= service1.getCharacteristic(UUID.fromString("11111101-5544-7766-9988-AABBCCDDEEFF"));

characteristic1.setValue(new byte[]{0x01, 0x01});
                btg.writeCharacteristic(characteristic1);
                btg.setCharacteristicNotification(characteristic1, true);
                Log.d("------------","write success ");
            } else {
                Log.d("------------", "onservicesdiscovered收到: " + status);
            }
        }
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);

}
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            Log.d("------------", "CharacteristicWrite" + status);
            if(status == BluetoothGatt.GATT_SUCCESS){
                // 连接成功
                isLinking = true;
            }

}
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            Log.d("------------", "CharacteristicChanged");
            // 查看字节长度
            Log.d("------------", characteristic.getValue().length+" ");
            result.setText("获得字节长度:"+characteristic.getValue().length);
        }
    };

// 如果setCharacteristicNotification()不能触发onCharacteristicChanged()方法时,调用此方法
    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
        if (bta == null || btg == null) {
            Log.w("------------", "BluetoothAdapter not initialized");
            return;
        }
        btg.setCharacteristicNotification(characteristic, enabled);
        List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
        for (BluetoothGattDescriptor dp : descriptors) {
            dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            btg.writeDescriptor(dp);
        }
    }

// 位置权限动态申请(ACCESS_COARSE_LOCATION)
    public void checkBlePermission() {
        if (ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            // 调用系统的方法
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},1);
        } else {
            Log.i("tag", "已申请位置权限");
        }
    }
    // 位置权限动态申请(ACCESS_COARSE_LOCATION)
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            // 此处的1与上文的checkBlePermission()方法中ActivityCompat.requestPermissions的1(requestCode)对应
            case 1: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.i("tag", "同意位置申请");
                } else {
                    Log.i("tag", "拒绝位置申请");
                }
                return;
            }
        }
    }

}

蓝牙BLE设备连接与通信相关推荐

  1. 低功耗蓝牙BLE之连接事件、连接参数和更新方法(程序解读)

    关注公众号"迈微电子研发社",选择"星标★" 低功耗蓝牙BLE之连接参数de更新方法 0. 蓝牙的状态以及基本连接过程 0.1 蓝牙的状态: 0.3 蓝牙的连接 ...

  2. 安卓读取蓝牙BLE设备信息

    安卓读取蓝牙BLE设备信息 简介 轮询方式代码实现 监听广播方式代码实现 简介 目前,许多项目都会涉及与BLE设备进行交互的功能,接下来说一下读取BLE设备信息的具体实现流程.安卓BLE相关接口介绍详 ...

  3. 蓝牙BLE设备主机重启回连流程分析

    本文出自:<蓝牙BLE设备主机重启回连流程分析> 如果一个BLE设备已经与蓝牙中心设备连接上,那么当中心设备的断电重启,其依然会和配对过的BLE设备连接上,而不需要重新走配对的流程,这个过 ...

  4. android 多个蓝牙连接电脑,Android BLE蓝牙多设备连接

    多设备连接的问题很典型,一方面实际应用中存在同时和多个设备通信的场景,另一方面蓝牙连接较耗时,如果能尽可能保持连接,则可省去不少时间,用户体验更好. 然而多设备连接也有一些问题要注意,有以下几点: 一 ...

  5. Win10 平台C#与低功耗蓝牙BLE设备通信案例

    前几天接了个单,客户要在win10电脑上做个工具软件,跟蓝牙锁设备相互通信.一开始以为是普通的蓝牙设备呢,收到客户寄来的测试设备,才发现是低功耗BLE蓝牙设备. PS:当时我研发用的台式机是没有蓝牙设 ...

  6. 低功耗蓝牙BLE之连接事件、连接参数和更新方法

    连接事件 在一个连接当中,主设备会在每个连接事件里向从设备发送数据包.一个连接事件是指主设备和从设备之间相互发送数据包的过程.连接事件的进行始终位于一个频率,每个数据包会在上个数据包发完之后等待 15 ...

  7. 【低功耗蓝牙BLE】连接事件和相关参数

    低功耗蓝牙连接相关的知识 1.连接事件 在一个连接当中,主设备会在每个连接事件里向从设备发送数据包.一个连接事件是指主设备和从设备之间相互发送数据包的过程.连接事件的进行始终位于一个频率,每个数据包会 ...

  8. 蓝牙BLE的连接过程,自动连接过程

    BLE连接,再次连接 BLE的第一次连接 1 蓝牙连接过程中的角色 2 连接过程中两个角色对应的状态 ble蓝牙的连接过程 二. 再次自动连接 1 二次连接的特点: BLE的第一次连接 所谓连接,分为 ...

  9. 此蓝牙设备或计算机无法处理该类型文件,蓝牙允许设备进行连接用不了_win10蓝牙允许设备连接灰色怎么解决...

    2020-11-12 15:20:52 浏览量:4578 在如今的智能设备中像手机.电脑等设备中都集成了蓝牙功能,我们经常会使用到这一功能,不过近日有用户在使用时,有一疑问,那就是手机可以连接多个蓝牙 ...

最新文章

  1. hoj 1067 Rails //poj1363 Rails 栈的简单应用
  2. 收到邮件乱码html,为什么我发的html格式邮件收到的是乱码呢?(100分)
  3. 京瓷打印机几个常见密码
  4. 为什么C语言exe能直接打开,怎么让c语言生成的exe双击就能运行呢?
  5. saleor的测试用账户地址This value is not valid for the address
  6. ubuntu列出所有磁盘_列出Ubuntu上的磁盘空间使用情况
  7. linux 编译安装python,linux下编译安装python2.7.6
  8. ZZULIOJ 1116: 删除元素
  9. python之路——面向对象进阶
  10. 7 centos lvs nat配置_centos中lvs安装配置方法详解
  11. 安卓开发 实现文字渐变效果_AI教程!用网格工具做渐变字效
  12. oracle中求商函数,三个方便实用的Oracle分析函数
  13. 正负数据如何归一化_数据归一化方法大全
  14. 万能免费信息采集软件-免费网站信息内容数据采集软件
  15. gmsk仿真matlab,GMSK系统的建模与仿真MATLAB程序
  16. 全国计算机二级vf成绩查询,2017全国计算机二级《VF》考点习题
  17. 苹果Macbook电脑无法进入系统
  18. 网络割接方案模板(范文)
  19. 单细胞测序数据挖掘与课题设计
  20. mac 全局安装 Cz 工具【踩坑】

热门文章

  1. 多想别人学习。增强编程能力。
  2. word文档太大怎么压缩到最小
  3. MDC实现日志链路追踪
  4. Hypervisor---虚拟化技术简易说明
  5. VS Nuget的使用
  6. 为什么工具类App,都要做一个社区?
  7. LTE-A载波聚合技术(14)---CQI的TDM和CDM
  8. ssd测试软件和实际 速度,固态硬盘实际速度比拼_三星 300E5K-Y05_笔记本评测-中关村在线...
  9. android手机8g内存够用嘛,安卓旗舰机8GB运行内存到底够不够用?有必要上12GB吗?...
  10. Debian安装谷歌浏览器(Google Chrome)