低功耗蓝牙BLE外围模式(peripheral)-使用BLE作为服务端

Android对外模模式(peripheral)的支持

从Android5.0开始才支持

关键术语和概念

以下是关键BLE术语和概念的摘要:

通用属性简档(GATT) - GATT简档是用于通过BLE链路发送和接收称为“属性”的短数据块的一般规范。 所有当前的低能量应用配置文件都基于GATT。

蓝牙SIG为低能量设备定义了许多配置文件 。 配置文件是设备在特定应用程序中的工作方式的规范。 请注意,设备可以实现多个配置文件。 例如,设备可以包含心率监视器和电池水平检测器。

属性协议(ATT) -GATT建立在属性协议(ATT)之上。 这也称为GATT / ATT。 ATT经过优化,可在BLE设备上运行。 为此,它使用尽可能少的字节。 每个属性由通用唯一标识符(UUID)唯一标识,UUID是用于唯一标识信息的字符串ID的标准化128位格式。 由ATT传送的属性被格式化为特征和服务 。

特性 -A特性包含描述特性值的单个值和0-n个描述符。 一个特性可以被认为是一个类型,类似于类。

描述符 - 描述符是描述特征值的定义属性。 例如,描述符可以指定人类可读的描述,特征值的可接受范围或特征值的特定的测量单位。

服务 - 服务是一个集合的特点。 例如,您可以有一个名为“心率监视器”的服务,其中包括诸如“心率测量”的特征。 您可以在bluetooth.org上找到现有基于GATT的个人资料和服务的列表 。

角色和职责

以下是Android设备与BLE设备互动时适用的角色和职责:

中央与外围。 这适用于BLE连接本身。 处于中心角色的设备扫描,寻找广告,并且外围角色中的设备进行广告。

GATT服务器与GATT客户端。 这决定了两个设备在建立连接后如何相互通信。

BLE权限

首先,需要在manifest中声明使用蓝牙和操作蓝牙的权限

在应用程序清单文件中声明蓝牙权限。 例如:

如果您要声明自己的应用只适用于支持BLE的设备,请在应用清单中包含以下内容:

不过,如果您想让应用程式适用于不支援BLE的装置,您仍应在应用的清单中加入这个元素,但required="false"设为required="false" 。

然后在运行时,您可以通过使用PackageManager.hasSystemFeature()确定BLE可用性:

// 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 6.0 以后,要想获得蓝牙扫描结果,还需要下面的权限

...

...

设置蓝牙

1.Get the BluetoothAdapter

获得蓝牙适配器

private BluetoothAdapter mBluetoothAdapter;

...

// Initializes Bluetooth adapter.

final BluetoothManager bluetoothManager =

(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

mBluetoothAdapter = bluetoothManager.getAdapter();

2.Enable Bluetooth

打开蓝牙

// Ensures Bluetooth is available on the device and it is enabled. If not,

// displays a dialog requesting user permission to enable Bluetooth.

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

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

}

3.初始化BLE蓝牙广播(广告)

(1)广播的设置

(2)设置广播的数据

(3)设置响应的数据

(4)设置连接回调

private void initGATTServer() {

AdvertiseSettings settings = new AdvertiseSettings.Builder()

.setConnectable(true)

.build();

AdvertiseData advertiseData = new AdvertiseData.Builder()

.setIncludeDeviceName(true)

.setIncludeTxPowerLevel(true)

.build();

AdvertiseData scanResponseData = new AdvertiseData.Builder()

.addServiceUuid(new ParcelUuid(UUID_SERVER))

.setIncludeTxPowerLevel(true)

.build();

AdvertiseCallback callback = new AdvertiseCallback() {

@Override

public void onStartSuccess(AdvertiseSettings settingsInEffect) {

Log.d(TAG, "BLE advertisement added successfully");

showText("1. initGATTServer success");

println("1. initGATTServer success");

initServices(getContext());

}

@Override

public void onStartFailure(int errorCode) {

Log.e(TAG, "Failed to add BLE advertisement, reason: " + errorCode);

showText("1. initGATTServer failure");

}

};

BluetoothLeAdvertiser bluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();

bluetoothLeAdvertiser.startAdvertising(settings, advertiseData, scanResponseData, callback);

}

在被BLE设备连接后,将触发 AdvertiseCallback 的 onStartSuccess,我们在这之后,初始化GATT的服务

4.初始化GATT的服务

(1) 通过 mBluetoothManager.openGattServer() 获得 bluetoothGattServer

(2) 添加 服务,特征,描述。这些内容要让客户端知道。

private void initServices(Context context) {

bluetoothGattServer = mBluetoothManager.openGattServer(context, bluetoothGattServerCallback);

BluetoothGattService service = new BluetoothGattService(UUID_SERVER, BluetoothGattService.SERVICE_TYPE_PRIMARY);

//add a read characteristic.

characteristicRead = new BluetoothGattCharacteristic(UUID_CHARREAD, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ);

//add a descriptor

BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UUID_DESCRIPTOR, BluetoothGattCharacteristic.PERMISSION_WRITE);

characteristicRead.addDescriptor(descriptor);

service.addCharacteristic(characteristicRead);

//add a write characteristic.

BluetoothGattCharacteristic characteristicWrite = new BluetoothGattCharacteristic(UUID_CHARWRITE,

BluetoothGattCharacteristic.PROPERTY_WRITE |

BluetoothGattCharacteristic.PROPERTY_READ |

BluetoothGattCharacteristic.PROPERTY_NOTIFY,

BluetoothGattCharacteristic.PERMISSION_WRITE);

service.addCharacteristic(characteristicWrite);

bluetoothGattServer.addService(service);

Log.e(TAG, "2. initServices ok");

showText("2. initServices ok");

}

在 openGattServer 方法中,我们需要传入个回调

bluetoothGattServer = mBluetoothManager.openGattServer(context, bluetoothGattServerCallback);

5.配置数据交互回调

回调时间有:连接状态变化,收发消息,通知消息

/**

* 服务事件的回调

*/

private BluetoothGattServerCallback bluetoothGattServerCallback = new BluetoothGattServerCallback() {

/**

* 1.连接状态发生变化时

* @param device

* @param status

* @param newState

*/

@Override

public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {

Log.e(TAG, String.format("1.onConnectionStateChange:device name = %s, address = %s", device.getName(), device.getAddress()));

Log.e(TAG, String.format("1.onConnectionStateChange:status = %s, newState =%s ", status, newState));

super.onConnectionStateChange(device, status, newState);

}

@Override

public void onServiceAdded(int status, BluetoothGattService service) {

super.onServiceAdded(status, service);

Log.e(TAG, String.format("onServiceAdded:status = %s", status));

}

@Override

public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {

Log.e(TAG, String.format("onCharacteristicReadRequest:device name = %s, address = %s", device.getName(), device.getAddress()));

Log.e(TAG, String.format("onCharacteristicReadRequest:requestId = %s, offset = %s", requestId, offset));

bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue());

// super.onCharacteristicReadRequest(device, requestId, offset, characteristic);

}

/**

* 3. onCharacteristicWriteRequest,接收具体的字节

* @param device

* @param requestId

* @param characteristic

* @param preparedWrite

* @param responseNeeded

* @param offset

* @param requestBytes

*/

@Override

public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) {

Log.e(TAG, String.format("3.onCharacteristicWriteRequest:device name = %s, address = %s", device.getName(), device.getAddress()));

Log.e(TAG, String.format("3.onCharacteristicWriteRequest:requestId = %s, preparedWrite=%s, responseNeeded=%s, offset=%s, value=%s", requestId, preparedWrite, responseNeeded, offset, OutputStringUtil.toHexString(requestBytes)));

bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, requestBytes);

//4.处理响应内容

onResponseToClient(requestBytes, device, requestId, characteristic);

}

/**

* 2.描述被写入时,在这里执行 bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS... 收,触发 onCharacteristicWriteRequest

* @param device

* @param requestId

* @param descriptor

* @param preparedWrite

* @param responseNeeded

* @param offset

* @param value

*/

@Override

public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {

Log.e(TAG, String.format("2.onDescriptorWriteRequest:device name = %s, address = %s", device.getName(), device.getAddress()));

Log.e(TAG, String.format("2.onDescriptorWriteRequest:requestId = %s, preparedWrite = %s, responseNeeded = %s, offset = %s, value = %s,", requestId, preparedWrite, responseNeeded, offset, OutputStringUtil.toHexString(value)));

// now tell the connected device that this was all successfull

bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);

}

/**

* 5.特征被读取。当回复响应成功后,客户端会读取然后触发本方法

* @param device

* @param requestId

* @param offset

* @param descriptor

*/

@Override

public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {

Log.e(TAG, String.format("onDescriptorReadRequest:device name = %s, address = %s", device.getName(), device.getAddress()));

Log.e(TAG, String.format("onDescriptorReadRequest:requestId = %s", requestId));

// super.onDescriptorReadRequest(device, requestId, offset, descriptor);

bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);

}

@Override

public void onNotificationSent(BluetoothDevice device, int status) {

super.onNotificationSent(device, status);

Log.e(TAG, String.format("5.onNotificationSent:device name = %s, address = %s", device.getName(), device.getAddress()));

Log.e(TAG, String.format("5.onNotificationSent:status = %s", status));

}

@Override

public void onMtuChanged(BluetoothDevice device, int mtu) {

super.onMtuChanged(device, mtu);

Log.e(TAG, String.format("onMtuChanged:mtu = %s", mtu));

}

@Override

public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {

super.onExecuteWrite(device, requestId, execute);

Log.e(TAG, String.format("onExecuteWrite:requestId = %s", requestId));

}

};

6.处理来自客户端发来的数据和发送回复数据:

调用 bluetoothGattServer.notifyCharacteristicChanged 方法,通知数据改变。

/**

* 4.处理响应内容

*

* @param reqeustBytes

* @param device

* @param requestId

* @param characteristic

*/

private void onResponseToClient(byte[] reqeustBytes, BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic) {

Log.e(TAG, String.format("4.onResponseToClient:device name = %s, address = %s", device.getName(), device.getAddress()));

Log.e(TAG, String.format("4.onResponseToClient:requestId = %s", requestId));

String msg = OutputStringUtil.transferForPrint(reqeustBytes);

println("4.收到:" + msg);

showText("4.收到:" + msg);

String str = new String(reqeustBytes) + " hello>";

characteristicRead.setValue(str.getBytes());

bluetoothGattServer.notifyCharacteristicChanged(device, characteristicRead, false);

println("4.响应:" + str);

showText("4.响应:" + str);

}

交互流程:

(1) 当客户端开始写入数据时: 触发回调方法 onDescriptorWriteRequest

(2) 在 onDescriptorWriteRequest 方法中,执行下面的方法表示 写入成功 BluetoothGatt.GATT_SUCCESS

bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);

执行 sendResponse后,会触发回调方法 onCharacteristicWriteRequest

(3) 在 onCharacteristicWriteRequest方法中

public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) {

这个里可以获得 来自客户端发来的数据 requestBytes

(4) 处理响应内容,我写了这个方法:

onResponseToClient(requestBytes, device, requestId, characteristic);

在这个方法中,通过 bluetoothGattServer.notifyCharacteristicChanged()方法 回复数据

通过日志,我们看看事件触发的顺序

1.onConnectionStateChange:device name = null, address = 74:32:DE:49:3C:28

1.onConnectionStateChange:status = 0, newState =2

2.onDescriptorWriteRequest:device name = null, address = 74:32:DE:49:3C:28

2.onDescriptorWriteRequest:requestId = 1, preparedWrite = false, responseNeeded = true, offset = 0, value = [01,00,],

3.onCharacteristicWriteRequest:device name = null, address = 74:32:DE:49:3C:28

3.onCharacteristicWriteRequest:requestId = 2, preparedWrite=false, responseNeeded=false, offset=0, value=[41,54,45,30,0D,]

4.onResponseToClient:device name = null, address = 74:32:DE:49:3C:28

4.onResponseToClient:requestId = 2

4.收到:ATE0

4.响应:ATE0 hello>

5.onNotificationSent:device name = null, address = 74:32:DE:49:3C:28

5.onNotificationSent:status = 0

代码托管到github:

android蓝牙服务端设置,低功耗蓝牙BLE外围模式(peripheral)-使用BLE作为服务端相关推荐

  1. 低功耗蓝牙BLE外围模式(peripheral)-使用BLE作为服务端

    低功耗蓝牙BLE外围模式(peripheral)-使用BLE作为服务端 Android对外模模式(peripheral)的支持 从Android5.0开始才支持 关键术语和概念 以下是关键BLE术语和 ...

  2. 怎么查看蓝牙uuid_多设备低功耗蓝牙 Swarm BLE in Android and iOS

    Camellia Café 在这里讲述同时与多个低功耗蓝牙设备的连接及通迅,在Android和iOS中的开发,敬请点击观看视频: 多设备低功耗蓝牙Android和iOShttps://www.zhih ...

  3. Bluetooth 蓝牙介绍(二):低功耗蓝牙BLE协议栈

    文章目录 Physical LAYER Link LAYER 角色 地址 物理信道 Air Interface Packet PDU Advertising physical channel PDU ...

  4. android低功耗蓝牙连接失败_低功耗蓝牙 AoA定位系统为室内定位和资产跟踪 提供亚米级精度位置服务...

    蓝色创源使用Nordic nRF52833 SoC实现兼容智能手机的低功耗位置服务网络. *定位解决方案提供商蓝色创源(北京)科技有限公司选择使用其nRF52833低功耗蓝牙(Bluetooth® L ...

  5. Android 经典蓝牙(Classic Bluetooth)和低功耗蓝牙(BLE)

    从蓝牙4.0开始包含两个蓝牙芯片模块:传统/经典蓝牙模块(Classic Bluetooth,简称BT)和低功耗蓝牙(Bluetooth Low Energy,简称BLE) 经典蓝牙是在之前的蓝牙1. ...

  6. android ble 经典蓝牙,Android 经典蓝牙(Classic Bluetooth)和低功耗蓝牙(BLE)

    [实例简介] 从蓝牙4.0开始包含两个蓝牙芯片模块:传统/经典蓝牙模块(Classic Bluetooth,简称BT)和低功耗蓝牙(Bluetooth Low Energy,简称BLE) 经典蓝牙是在 ...

  7. 蓝牙技术|什么是低功耗蓝牙(BLE)芯片?

    蓝牙是蓝牙技术联盟设计和销售的一种个人局域网络技术,旨在用于医疗保健.运动健身.信标(Beacon).安防.家庭娱乐等领域的新兴应用.相较经典蓝牙,蓝牙低功耗技术旨在保持同等通信范围的同时显著降低功耗 ...

  8. 蓝牙学习笔记(二)——低功耗蓝牙(BLE)的体系结构

    前言 :最近开始学习蓝牙相关技术,做了一些笔记整理.本次阅读参考书籍是<低功耗蓝牙开发权威指南> 低功耗蓝牙的体系结构   低功耗蓝牙体系结构主要分为三个部分:控制器.主机和应用程序.在控 ...

  9. Bluetooth 蓝牙介绍(四):低功耗蓝牙BLE Mesh网络 Ⅰ—— 基础概念

    文章目录 背景 术语 Managed Flooding Models Scenes 架构 Node Features 中继节点 代理节点 友元节点和低功耗节点 示例 BLE Mesh Networki ...

  10. android蓝牙 uuld,BLE4.0低功耗蓝牙协议总结

    一位大牛在蓝牙BLE领域干了十来年的总结,总结得很到位.都准备出书了.非常好.在这里共享给大家! 版权所有 273.扫描态. 46 274.发起态 275.软件设计广播状态流程图.-.-. ::::: ...

最新文章

  1. oa系统登录后几分钟自动退出_2020版OA办公系统正式上线运行!
  2. leetcode算法题--排序链表★
  3. python一行没写完用什么隔离_完全隔离的Python环境
  4. Windows上搭建Python安装包MySQLdb
  5. IBM的SOA方法论之一——五个切入点和八个场景
  6. SAP云平台CloudFoundry环境里新建SAP UI5应用后,自动生成了哪些组件
  7. 3月任务--target
  8. bmp转YUV RGB转YUV HM学习
  9. 《编码规范和测试方法——C/C++版》作业 ·005——设计一组员工类
  10. Ubuntu虚拟机安装gcc运行C程序
  11. 【unity3d】复刻死亡之书自动设置摄像机景深
  12. 协同OA产品要完全符合企业的办公模式吗?
  13. Thrift入门学习
  14. 共筑安全内容分发,知道创宇与华为云签署合作备忘录
  15. MySQL数据库篇---对数据库,数据库中表,数据库中表的记录进行添修删查操作---保姆级教程
  16. SAP-MM知识精解-计划协议-01
  17. 排球分组循环交叉编排_请问一下排球是怎么样编排的啊
  18. mac系统ssh可视化工具zoc的简单使用
  19. matplotlib的imshow在Python shell IDLE环境无法显示图像问题
  20. 秃头大牛一文竟然就把SpringCloudStream(SCS)给讲明白了?

热门文章

  1. linux设备驱动程序-i2c(1):i2c总线的添加与实现
  2. Linux内核部件分析 原子性操作atomic_t
  3. python默认字体_matplotlib默认字体设置
  4. mac编写python_刚到手Mac写Python的一个简单问题
  5. 试除法判定质数、试除法分解质因数(附例题)
  6. 【BFS】迷宫问题c++代码详解(逐句分析)
  7. while循环 dowhile循环 for循环(C++)
  8. mysql如何更改文件所有者sa_Mssql Server2005中更改sa的用户名的多种方法
  9. oracle解析关闭,读书笔记:深入解析oracle-第一章 数据库的启动和关闭
  10. mysql ageval 1 30_通过sqoop eval传递mysql属性