搜索
查看
编辑修改
首页
UNITY
NODEJS
PYTHON
AI
GIT
PHP
GO
CEF3
JAVA
HTML
CSS
搜索
凡人多烦事01
这个屌丝很懒,什么也没留下!
关注作者
热门标签
jquery
HTML
CSS
PHP
ASP
PYTHON
GO
AI
C
C++
C#
PHOTOSHOP
UNITY
iOS
android
vue
xml
爬虫
SEO
LINUX
WINDOWS
JAVA
MFC
CEF3
CAD
NODEJS
GIT
Pyppeteer
article
热门文章
1
如何在Linux CentOS部署宝塔面板并实现固定公网地址访问内网宝塔_centos 7 安装宝塔
2
NLP学习之bigrams函数"generator object bigrams at 0x000001D32A95A678"问题解决
3
基于阿里云的快照介绍_阿里云 创建极速快照
4
AI推介-大语言模型LLMs论文速览(arXiv方向):2024.03.10-2024.03.15_大语言模型 机器翻译 论文
5
TTS合成技术中的语音合成和人工智能和自然语言生成_语音合成 tts 应用
6
[emerg] the “ssl“ parameter requires ngx_http_ssl_module in /usr/local/nginx/conf/nginx.conf:35_nginx: [emerg] the "ssl" parameter requires ngx_ht
7
时序数据库:TDengine整体架构
8
云服务器8核32G配置报价大全,腾讯云、阿里云和京东云
9
GeoTrust SSL证书有什么优势?
10
软件架构的风格与分类_云架构属于虚拟机风格 仓库风格
当前位置:
article
> 正文
Android BLE蓝牙4.0开发详解_蓝牙lightblue android
作者:凡人多烦事01 | 2024-03-27 19:51:36
赞
踩
蓝牙lightblue android
http://blog.csdn.net/chenliqiang12345678/article/details/50504406
这篇博客主要讲解
蓝牙 BLE
的用法。在讲解之前先讲一些概念性的东西,对于之前没接触过蓝牙开发,现在手上又有个蓝牙BLE项目需要做的人,先看下这些概念还是很重要的。因为我之前就是这样,之前没有接触过蓝牙方面的开发,然后来了个蓝牙的项目,于是就到网上百度了一番,于是有点茫然,产生了几点疑惑:
1:发现蓝牙有传统蓝牙和低功耗蓝牙(ble)之分。那么什么是传统蓝牙,什么又是低功耗蓝牙?
之前又没做过蓝牙开发,我该用哪种方式去开发我这个项目?用最新的 方式的话,传统方式蓝牙开发我是不是该要先了解?
2:蓝牙到底有哪些版本?哪些版本称为传统蓝牙?哪些版本称为低功耗蓝牙?
3:传统蓝牙和低功耗蓝牙有什么区别?为什么低功耗蓝牙的出现使得智能能穿戴越来越流行?
带着这些疑问,我又进行了一番的搜索,先把概念搞清,在研究了几份代码,才渐渐弄明白,再开发项目。
蓝牙我们应该很早就听过,最常见的就是原来我们偶尔通过手机上的蓝牙来传输文件。貌似在低功耗蓝牙出现之前,蓝牙我们使用的并不多,蓝牙的产品貌似也不是很多。2010年6月30号蓝牙技术联盟推出了低功耗蓝牙,经过几年的发展,市场上基于低功耗系列的硬件产品越来越多,开发硬件,软件的厂商,公司越来越多。
蓝牙发展至今经历了8个版本的更新。1.1、1.2、2.0、2.1、3.0、4.0、4.1、4.2。那么在1.x~3.0之间的我们称之为传统蓝牙,4.x开始的蓝牙我们称之为低功耗蓝牙也就是蓝牙ble,当然4.x版本的蓝牙也是向下兼容的。android手机必须系统版本4.3及以上才支持BLE API。低功耗蓝牙较传统蓝牙,
传输速度更快,覆盖范围更广,安全性更高,延迟更短,耗电极低等等优点
。这也是为什么近年来智能穿戴的东西越来越多,越来越火。还有传统蓝牙与低功耗蓝牙通信方式也有所不同,传统的一般通过socket方式,而低功耗蓝牙是通过Gatt协议来实现。若是之前没做过传统蓝牙开发,也是可以直接上手低功耗蓝牙开发的。因为它们在通信协议上都有所改变,关联不大。当然有兴趣的可以去下载些传统蓝牙开发的demo看看,在看看低功耗蓝牙的demo。两者的不同之处自然容易看出来。好了,我们下面开始讲低功耗蓝牙开发吧。低功耗蓝牙也叫BLE,下面都称之为BLE。
BLE分为三部分:Service,Characteristic,Descriptor
。
这三部分都用UUID作为唯一标识符。UUID为这种格式:0000ffe1-0000-1000-8000-00805f9b34fb。比如有3个Service,那么就有三个不同的UUID与Service对应。这些UUID都写在硬件里,我们通过BLE提供的API可以读取到。
一个BLE终端可以包含多个Service, 一个Service可以包含多个
Characteristic,一个Characteristic包含一个value和多个Descriptor,一个Descriptor包含一个Value。Characteristic是比较重要的,是手机与BLE终端交换数据的关键,读取设置数据等操作都是操作Characteristic的相关属性。
比如我有个BLE的硬件,我们可以用android 版本的light blue去连接上这个硬件,没有的话我文章底部提供了下载链接,不过它在android5.0以上跑不起来,大家也可以下载另外两个源码跑起来也可以连接上,不过这个android 版本的light blue最好用了。进入应用,就可以扫描到你的BLE设备,点击就会连接上,然后我们可以看到UUID列表,这里每一行的UUID都代表一个
Service
,
再点击任意一行进去,又可以看到一个UUID列表,这里每一行的UUID都代表一个
Characteristic
,
再点击任意一行
进去,即可以操作这个
Characteristic
,比如写入数据或者读出数据等。
好了,那我们来根据代码讲解下吧。代码有上传,里面有
Bluetooth4_3/BLEDemo/Android_Lightblue.apk
。下面讲解的是根据代码
Bluetooth4_3来讲解的。
1:首先在程序里我们要开启蓝牙权限
在应用程序manifest文件中添加如下代码,声明蓝牙权限。
[java]
view plain
copy
<uses-permission android:name=
"android.permission.BLUETOOTH"
/>
<uses-permission android:name=
"android.permission.BLUETOOTH_ADMIN"
/>
如果你想声明你的应用程序只能在支持BLE的设备上运行,可以将下面声明包含进你的应用程序manifest文件中:
[java]
view plain
copy
<uses-feature android:name=
"android.hardware.bluetooth_le"
android:required=
"true"
></uses-feature>
然而,如果你想让你的应用程序也能够在不支持BLE的设备上运行,你就应该将上面标签中的属性设置为required="false"。然后在运行的过程中使用PackageManager.hasSystemFeature()方法来判断设备是否支持BLE:
[java]
view plain
copy
if
(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(
this
, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
2:判断设备是否支持蓝牙ble
[java]
view plain
copy
// 检查当前手机是否支持ble 蓝牙,如果不支持退出程序
if
(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(
this
, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
3:获取蓝牙适配器BluetoothAdapter
可以通过以下两种方式获取
[java]
view plain
copy
final
BluetoothManager bluetoothManager =(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
[java]
view plain
copy
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
[java]
view plain
copy
//如果mBluetoothAdapter == null,说明设备不支持蓝牙
4:弹出是否启用蓝牙的对话框
[java]
view plain
copy
if
(mBluetoothAdapter ==
null
|| !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent =
new
Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
@Override
protected
void
onActivityResult(
int
requestCode,
int
resultCode, Intent data) {
switch
(requestCode) {
case
REQUEST_ENABLE:
if
(resultCode == Activity.REQUEST_ENABLE_BT) {
Toast.makeText(
this
,
"蓝牙已启用"
, Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(
this
,
"蓝牙未启用"
, Toast.LENGTH_SHORT).show();
}
break
;
}
}
[java]
view plain
copy
//也可以直接调用mBluetoothAdapter.enable()mBluetoothAdapter.disable()来启用禁用蓝牙。不过这种方式不会弹出询问对话框
5:搜索设备
[java]
view plain
copy
private
void
scanLeDevice(
final
boolean
enable) {
if
(enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(
new
Runnable() {
@Override
public
void
run() {
mScanning =
false
;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
//10秒后停止搜索
mScanning =
true
;
mBluetoothAdapter.startLeScan(mLeScanCallback);
//开始搜索
}
else
{
mScanning =
false
;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
//停止搜索
}
}
搜索的操作最好放在Activity的onResume里面或者服务里面,我有发现放在onCreate有时响应不及时
6:搜索到设备会回调LeScanCallback接口
[java]
view plain
copy
private
BluetoothAdapter.LeScanCallback mLeScanCallback =
new
BluetoothAdapter.LeScanCallback() {
@Override
public
void
onLeScan(
final
BluetoothDevice device,
int
rssi,
byte
[] scanRecord) {
runOnUiThread(
new
Runnable() {
@Override
public
void
run() {
//在这里可以把搜索到的设备保存起来
//device.getName();获取蓝牙设备名字
//device.getAddress();获取蓝牙设备mac地址
}
});
}
};
7:选择一个设备进行连接。连接后会返回一个
BluetoothGatt
类型的对象,这里定义为
mBluetoothGatt
。该对象比较重要,后面发现服务读写设备等操作都是通过该对象。
代码里建了一个service,里面封装了连接,读写设备等操作。连接是通过获取到的mac地址去进行连接操作就可以了。
[java]
view plain
copy
public
boolean
connect(
final
String address) {
if
(mBluetoothAdapter ==
null
|| address ==
null
) {
Log.w(TAG,
"BluetoothAdapter not initialized or unspecified address."
);
return
false
;
}
// Previously connected device. Try to reconnect. (先前连接的设备。 尝试重新连接)
if
(mBluetoothDeviceAddress !=
null
&& address.equals(mBluetoothDeviceAddress)&& mBluetoothGatt !=
null
) {
Log.d(TAG,
"Trying to use an existing mBluetoothGatt for connection."
);
if
(mBluetoothGatt.connect()) {
mConnectionState = STATE_CONNECTING;
return
true
;
}
else
{
return
false
;
}
}
final
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if
(device ==
null
) {
Log.w(TAG,
"Device not found. Unable to connect."
);
return
false
;
}
// We want to directly connect to the device, so we are setting the
// autoConnect
// parameter to false.
mBluetoothGatt = device.connectGatt(
this
,
false
, mGattCallback);
//该函数才是真正的去进行连接
Log.d(TAG,
"Trying to create a new connection."
);
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return
true
;
}
8:连接后会回调BluetoothGattCallback接口,包括读取设备,往设备里写数据及设备发出通知等都会回调该接口。其中比较重要的是BluetoothGatt。
[java]
view plain
copy
private
final
BluetoothGattCallback mGattCallback =
new
BluetoothGattCallback() {
@Override
//当连接上设备或者失去连接时会回调该函数
public
void
onConnectionStateChange(BluetoothGatt gatt,
int
status,
int
newState) {
if
(newState == BluetoothProfile.STATE_CONNECTED) {
//连接成功
mBluetoothGatt.discoverServices();
//连接成功后就去找出该设备中的服务 private BluetoothGatt mBluetoothGatt;
}
else
if
(newState == BluetoothProfile.STATE_DISCONNECTED) {
//连接失败
}
}
@Override
//当设备是否找到服务时,会回调该函数
public
void
onServicesDiscovered(BluetoothGatt gatt,
int
status) {
if
(status == BluetoothGatt.GATT_SUCCESS) {
//找到服务了
//在这里可以对服务进行解析,寻找到你需要的服务
}
else
{
Log.w(TAG,
"onServicesDiscovered received: "
+ status);
}
}
@Override
//当读取设备时会回调该函数
public
void
onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,
int
status) {
System.out.println(
"onCharacteristicRead"
);
if
(status == BluetoothGatt.GATT_SUCCESS) {
//读取到的数据存在characteristic当中,可以通过characteristic.getValue();函数取出。然后再进行解析操作。
//int charaProp = characteristic.getProperties();if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0)表示可发出通知。 判断该Characteristic属性
}
}
@Override
//当向设备Descriptor中写数据时,会回调该函数
public
void
onDescriptorWrite(BluetoothGatt gatt,BluetoothGattDescriptor descriptor,
int
status) {
System.out.println(
"onDescriptorWriteonDescriptorWrite = "
+ status +
", descriptor ="
+ descriptor.getUuid().toString());
}
@Override
//设备发出通知时会调用到该接口
public
void
onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {
if
(characteristic.getValue() !=
null
) {
System.out.println(characteristic.getStringValue(
0
));
}
System.out.println(
"--------onCharacteristicChanged-----"
);
}
@Override
public
void
onReadRemoteRssi(BluetoothGatt gatt,
int
rssi,
int
status) {
System.out.println(
"rssi = "
+ rssi);
}
@Override
//当向Characteristic写数据时会回调该函数
public
void
onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,
int
status) {
System.out.println(
"--------write success----- status:"
+ status);
};
}
9
:设备连接成功并回调BluetoothGattCallback接口里面的onConnectionStateChange函数,然后调用mBluetoothGatt.discoverServices();去发现服务。发现服务后会回调BluetoothGattCallback接口里面的 onServicesDiscovered函数,在里面我们可以获取服务列表。
[java]
view plain
copy
public
List<BluetoothGattService> getSupportedGattServices() {
if
(mBluetoothGatt ==
null
)
return
null
;
return
mBluetoothGatt.getServices();
//此处返回获取到的服务列表
}
10:获取到服务列表后自然就是要对服务进行解析。解析出有哪些服务,服务里有哪些Characteristic,哪些Characteristic可读可写可发通知等等。
[java]
view plain
copy
private
void
displayGattServices(List<BluetoothGattService> gattServices) {
if
(gattServices ==
null
)
return
;
for
(BluetoothGattService gattService : gattServices) {
// 遍历出gattServices里面的所有服务
List<BluetoothGattCharacteristic> gattCharacteristics = gattServices.getCharacteristics();
for
(BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
// 遍历每条服务里的所有Characteristic
if
(gattCharacteristic.getUuid().toString().equalsIgnoreCase(需要通信的UUID)) {
// 有哪些UUID,每个UUID有什么属性及作用,一般硬件工程师都会给相应的文档。我们程序也可以读取其属性判断其属性。
// 此处可以可根据UUID的类型对设备进行读操作,写操作,设置notification等操作
// BluetoothGattCharacteristic gattNoticCharacteristic 假设是可设置通知的Characteristic
// BluetoothGattCharacteristic gattWriteCharacteristic 假设是可读的Characteristic
// BluetoothGattCharacteristic gattReadCharacteristic 假设是可写的Characteristic
}
}
}
}
11:可接收通知的UUID,设置其可以接收通知(notification)。下面函数参数为10中的gattNoticCharacteristic
[java]
view plain
copy
public
void
setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean
enabled) {
if
(mBluetoothAdapter ==
null
|| mBluetoothGatt ==
null
) {
Log.w(TAG,
"BluetoothAdapter not initialized"
);
return
;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID
.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
if
(descriptor !=
null
) {
System.out.println(
"write descriptor"
);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
12:可读的UUID。
下面函数参数为10中的gattReadCharacteristic
。
read
Characteristic调用成功会回调步骤8中的onCharacteristicRead函数
[java]
view plain
copy
public
void
readCharacteristic(BluetoothGattCharacteristic characteristic) {
if
(mBluetoothAdapter ==
null
|| mBluetoothGatt ==
null
) {
Log.w(TAG,
"BluetoothAdapter not initialized"
);
return
;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
13:可写的UUID。下面函数参数为10中的gattWriteCharacteristic。writeCharacteristic调用成功会回调步骤8中的onCharacteristicWrite函数
[java]
view plain
copy
public
void
wirteCharacteristic(BluetoothGattCharacteristic characteristic) {
if
(mBluetoothAdapter ==
null
|| mBluetoothGatt ==
null
) {
Log.w(TAG,
"BluetoothAdapter not initialized"
);
return
;
}
mBluetoothGatt.writeCharacteristic(characteristic);
}
14:一般硬件里读出写入的数据为二进制类型,所以要熟悉整型,字符串,二进制,十六进制等它们之间的转换。有时间的话再整理一份进制数据转换的。好了整个BLE的工作过程差不多就这些,剩下的就是看自己怎么去处理获取到的那些数据呈现到界面了。
代码说明:
下面是源码下载。源码里面有
Bluetooth4_3/BLEDemo/Android_Lightblue.apk
三个.前两个是BLE的demo。我们上面讲解的是基于Bluetooth4_3这个demo讲解的。BLEDemo 这个功能较Bluetooth4_3多一些,有兴趣的可以都看下。Android_Lightblue.apk是Android版的lightblue,在进行ble开发的时候用该app作为辅助工具还是不错的,功能较Bluetooth4_3 BLEDemo 这两个demo都强大。不过Android_Lightblue.apk在android5.0以上的版本手机上运行不起来,我也没有该app的源码。看看后面会不会有更新
源码下载
顶
5
踩
0
上一篇
JNI & NDK 实例讲解二 (通过NDK创建JNI项目)
下一篇
源码分析异步消息处理线程机制(Looper MessageQueue Handler Message)
我的同类文章
声明:
本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:
https://www.wpsshop.cn/w/凡人多烦事01/article/detail/326560
推荐阅读
article
android | 解决:Task '
assemble
' not
found
in
root
pro...
解决方案:根目录下的build.gradle的首行加入:
task
assemble
{}_
task
'
assemble
de...
赞
踩
article
Task 'assemblePlug
in
'
not
found
in
root
project
'q...
问题Task 'assemblePlug
in
'
not
found
in
root
project
'qiakr-and...
赞
踩
article
and
r
oid studio 找不到
r
.
java
,And
r
oid
Studio
无法重新生成R.jav...
我从git存储库中提取了一些最近的更改,然后突然, And
r
oid
Studio
抛出R.
java
的令牌未找到错误.我在A...
赞
踩
article
android
studio
root
project
,Android Studio 0.4.3 - ...
Updated to 0.4.3 the other day and try
in
g to start a new pro...
赞
踩
article
android
studio
学习-补充_
android
studio
导入第三方库
project
w...
41、双击shift键进行查找非常实用,可以找到文件,设置等,但是好像不能全局搜索文件内容,如果想要全局搜索文件内容,可...
赞
踩
article
realm
for
android
集成问题
app
:trans
for
mClassesWithRea...
Error:Execution failed
for
task ':
app
:trans
for
mClassesWithRe...
赞
踩
article
解决:
android
studio 运行时
Process
'
comma
nd
'
C:\Program ...
Error:Execution
failed
for
task
'
:app:dexDebug
'
.> com.and...
赞
踩
article
Android
Studio
常见问题_
failed
resolution
of
: l
timber
/l...
Android
stuido是Google官方唯一推荐的
Android
开发IDE,但是在使用过程中,必然会遇到一些一问题...
赞
踩
article
Android
兼容
Java
8
语法
特性
的原理分析_
android
java
8
特性
...
本文主要阐述了Lambda表达式及其底层实现(invokedynamic指令)的原理、
Android
第三方插件Retro...
赞
踩
article
【笔记】关于
java
.exe
finished
with
non-
zero
exit
value 3...
Process '
command
'C:\Program Files\Android\Android Studio\jr...
赞
踩
article
Android
bug
之transformClassesWithDexForDe
bug
_transf...
Error:Execution failed for task ‘:app:transformClassesWithDe...
赞
踩
article
Android
Camera2
教程 · 第四章 ·
拍照
_
android
camera2
拍照
...
相机_
android
camera2
拍照
android
camera2
拍照
上一章《Camera...
赞
踩
article
Android
研究之为基于 x
86
的
Android
*
游戏
选择合适
的
引擎详解_适合
游戏
的
andr...
摘要
游戏
开发人员知道
Android
中蕴藏着巨大
的
机遇。 在 Google Play 商店
的
前 100 款应用中,约一...
赞
踩
article
Android
:如何通过
c++
来调用
java
程序
:接上篇_
studio
用
c++
启动
java
程序
...
Android
:如何通过
c++
来调用
java
程序
:接上篇设备/引擎:Mac(11.6)/cocos开发工具:Androi...
赞
踩
article
Android
:JNI实战,加载
三方
库、编译C/
C++
_
android
引用第
三方
jni
库...
本篇主要从项目架构上剖析一个
Android
App如何通过Jni机制加载
三方
库和C/
C++
文件。_
android
引用第...
赞
踩
article
Android
Studio3.5
JAVA
调用
C++
源码方法总结_
callvoidmethod
...
本文主要讲述如何在
Android
Studio中通过
JAVA
调用
C++
编写的源码_
callvoidmethod
callv...
赞
踩
article
Android
Studio
导入项目
gradle
过慢_
androidstudio
import
g...
列表内容android
Studio
去导入项目的时候会去检测
gradle
-3.3-all.zip 版本 这里以“gr...
赞
踩
article
Android
手机
/平板 连接
局域网
访问
局域网
电脑
Web
服务器
_平板
访问
电脑
局域网
...
应用如何在
手机
上
访问
本地
电脑
上的tomcat
服务器
,设置"localhost"或者"127.0.0.1"是不行的,在模拟...
赞
踩
article
Linux
和
Android
之间的区别_安卓
linux
...
Linux
是由 Linus Torvalds 于 1991 年创建
和
设计的免费开源操作系统。
Linux
是 Unix ...
赞
踩
article
android
连接本地
服务器
Connection
refused
问题_安卓pad访问内网地址,co...
通过
android
模拟器链接本地
服务器
localhost或者127.0.0.1连接
服务器
出现
Connection
ref...
赞
踩
相关标签
android studio 找不到r.java
android studio rootproject
android studio
ide
android
realm
Process 'command
bug
安卓
移动开发
java
c++
游戏开发
jni
cmake