Android-Ble蓝牙开发Demo示例–扫描,连接,发送和接收数据,分包解包(附源码)

Android-Ble蓝牙开发Demo示例–扫描,连接,发送和接收数据,分包解包(附源码),第1张

万物互联的物联网时代的已经来临,ble蓝牙开发在其中扮演着举重若轻的角色。最近刚好闲一点,抽时间梳理下这块的知识点。

涉及ble蓝牙通讯的客户端(开启、扫描、连接、发送和接收数据、分包解包)和服务端(初始化广播数据、开始广播、配置Services、Server回调 *** 作)整个环节以及一些常见的问题即踩过的一些坑。

比如

1、在Android不同版本或不同手机的适配问题,扫描不到蓝拆拍牙设备

2、如何避免ble蓝牙连接出现133错误?

3、单次写的数据大小有20字节限制,如何发送长数据

蓝牙有传统(经典)蓝牙和低功耗蓝牙BLE(Bluetooth Low Energy)之分,两者的旅岩羡开发的API不一样,本文主讲Ble蓝牙开发,传统蓝牙不展开,有需要的可以自行了解。

相对传统蓝牙,BLE低功耗蓝牙,主要特点是快速搜索,快速连接,超低功耗保持连接和数据传输。

客户端

服务端

Android4.3(API Level 18)开始引入BLE的核心功能并提供了相应的 API。应用程序通过这些 API 扫描蓝牙设备、查询 services、读写设备的 characteristics(属性特征)等 *** 作。

BLE蓝牙协议是GATT协议, BLE相关类不多, 全枣搭都位于android.bluetooth包和android.bluetooth.le包的几个类:

android.bluetooth.

.BluetoothGattService 包含多个Characteristic(属性特征值), 含有唯一的UUID作为标识

.BluetoothGattCharacteristic 包含单个值和多个Descriptor, 含有唯一的UUID作为标识

.BluetoothGattDescriptor 对Characteristic进行描述, 含有唯一的UUID作为标识

.BluetoothGatt 客户端相关

.BluetoothGattCallback 客户端连接回调

.BluetoothGattServer 服务端相关

.BluetoothGattServerCallback 服务端连接回调

android.bluetooth.le.

.AdvertiseCallback 服务端的广播回调

.AdvertiseData 服务端的广播数据

.AdvertiseSettings 服务端的广播设置

.BluetoothLeAdvertiser 服务端的广播

.BluetoothLeScanner 客户端扫描相关(Android5.0新增)

.ScanCallback 客户端扫描回调

.ScanFilter 客户端扫描过滤

.ScanRecord 客户端扫描结果的广播数据

.ScanResult 客户端扫描结果

.ScanSettings 客户端扫描设置

BLE设备分为两种设备: 客户端(也叫主机/中心设备/Central), 服务端(也叫从机/外围设备/peripheral)

客户端的核心类是 BluetoothGatt

服务端的核心类是 BluetoothGattServer 和 BluetoothLeAdvertiser

BLE数据的核心类是 BluetoothGattCharacteristic 和 BluetoothGattDescriptor

下面详细讲解下客户端和服务端的开发步骤流程

安卓手机涉及蓝牙权限问题,蓝牙开发需要在AndroidManifest.xml文件中添加权限声明:

在搜索设备之前需要询问打开手机蓝牙:

注意: BLE设备地址是动态变化(每隔一段时间都会变化),而经典蓝牙设备是出厂就固定不变了!

通过扫描BLE设备,根据设备名称区分出目标设备targetDevice,下一步实现与目标设备的连接,在连接设备之前要停止搜索蓝牙;停止搜索一般需要一定的时间来完成,最好调用停止搜索函数之后加以100ms的延时,保证系统能够完全停止搜索蓝牙设备。停止搜索之后启动连接过程;

BLE蓝牙的连接方法相对简单只需调用connectGatt方法;

参数说明

与设备建立连接之后与设备通信,整个通信过程都是在BluetoothGattCallback的异步回调函数中完成;

BluetoothGattCallback中主要回调函数如下:

上述几个回调函数是BLE开发中不可缺少的;

当调用targetdDevice.connectGatt(context, false, gattCallback)后系统会主动发起与BLE蓝牙设备的连接,若成功连接到设备将回调onConnectionStateChange方法,其处理过程如下:

判断newState == BluetoothGatt.STATE_CONNECTED表明此时已经成功连接到设备;

mBluetoothGatt.discoverServices()

扫描BLE设备服务是安卓系统中关于BLE蓝牙开发的重要一步,一般在设备连接成功后调用,扫描到设备服务后回调onServicesDiscovered()函数,函数原型如下:

BLE蓝牙开发主要有负责通信的BluetoothGattService完成的。当且称为通信服务。通信服务通过硬件工程师提供的UUID获取。获取方式如下:

具体 *** 作方式如下:

开启监听,即建立与设备的通信的首发数据通道,BLE开发中只有当客户端成功开启监听后才能与服务端收发数据。开启监听的方式如下:

BLE单次写的数据量大小是有限制的, 通常是20字节 ,可以尝试通过requestMTU增大,但不保证能成功。分包写是一种解决方案,需要定义分包协议,假设每个包大小20字节,分两种包,数据包和非数据包。对于数据包,头两个字节表示包的序号,剩下的都填充数据。对于非数据包,主要是发送一些控制信息。

监听成功后通过向 writeCharacteristic写入数据实现与服务端的通信。写入方式如下:

其中:value一般为Hex格式指令,其内容由设备通信的蓝牙通信协议规定;

若写入指令成功则回调BluetoothGattCallback中的onCharacteristicWrite()方法,说明将数据已经发送给下位机;

若发送的数据符合通信协议,则服务端会向客户端回复相应的数据。发送的数据通过回调onCharacteristicChanged()方法获取,其处理方式如下:

通过向服务端发送指令获取服务端的回复数据,即可完成与设备的通信过程;

当与设备完成通信之后之后一定要断开与设备的连接。调用以下方法断开与设备的连接:

源码上传在CSDN上了,有需要的可以借鉴。

=====>Android蓝牙Ble通讯Demo示例源码–扫描,连接,发送和接收数据,分包解包

BLE单次写的数据量大小是有限制的,通常是20字节,可以尝试通过requestMTU增大,但不保证能成功。分包写是一种解决方案,需要定义分包协议,假设每个包大小20字节,分两种包,数据包和非数据包。对于数据包,头两个字节表示包的序号,剩下的都填充数据。对于非数据包,主要是发送一些控制信息。

总体流程如下:

1、定义通讯协议,如下(这里只是个举例,可以根据项目需求扩展)

2、封装通用发送数据接口(拆包)

该接口根据会发送数据内容按最大字节数拆分(一般20字节)放入队列,拆分完后,依次从队列里取出发送

3、封装通用接收数据接口(组包)

该接口根据从接收的数据按协议里的定义解析数据长度判读是否完整包,不是的话把每条消息累加起来

4、解析完整的数据包,进行业务逻辑处理

5、协议还可以引入加密解密,需要注意的选算法参数的时候,加密后的长度最好跟原数据长度一致,这样不会影响拆包组包

一般都是Android版本适配以及不同ROM机型(小米/红米、华为/荣耀等)(EMUI、MIUI、ColorOS等)的权限问题

蓝牙开发中有很多问题,要静下心分析问题,肯定可以解决的,一起加油;

初涉android的蓝牙 *** 作,按照固定MAC地址连接获取Device时,程序始终是异常终止,查了好多天代码都没查出原因。今天改了一下API版本,突然就成功连接了。总结之后发现果然是个坑爹之极的错误。

为了这种错误拼命查原因浪费大把时间是非常不值得的,但是问题不解决更是揪心。可惜我百度了那么多,都没基梁有给出确切原因。今天特此mark,希望后来者遇到这个问题的时候能轻松解决。

下面是我的连接过程,中间崩溃原因及解决办法。

1:用AT指令获得蓝牙串口的MAC地址,地址是简写的,按照常理猜测可得标准格式。

2:开一个String adress= "************" //MAC地址, String MY_UUID= "************"//UUID根据通信而定,网上都有。

3:取得本地Adapter用getDefaultAdapter()远程的则用getRemoteDevice(adress); 之后便可用UUID开socket进行通信。

如果中途各种在getRemoteDevice处崩溃,大家可以查看一下当前的API版本,如果是2.1或以下版本的话,便能确定是API版本问题,只要换成2.2或者以上就都可以正常运行了~   这么坑爹的错误的确很为难初学者。  唉··········  为这种小trick浪费很多时间真是难过。

(另外有个重要地方,别忘了给manifest里面加以下两个蓝牙 *** 作权限哦局磨~)

<uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>

下面附上Android蓝牙 *** 作中用固定MAC地址传输信息的模板,通用搜索模式日后再补删模板:

private BluetoothAdapter mBluetoothAdapter = null

private BluetoothSocket btSocket = null

private OutputStream outStream = null

private InputStream inStream = null

private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")  //这条是蓝牙串口通用的UUID,不要更改

private static String address = "00:12:02:22:06:61" // <==要连接的蓝牙设备MAC地址

/*获得通信线路过程*/

/*1:获搏腊运取本地BlueToothAdapter*/

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()

if(mBluetoothAdapter == null)

{

Toast.makeText(this, "Bluetooth is not available.", Toast.LENGTH_LONG).show()

finish()

return

}

if(!mBluetoothAdapter.isEnabled())

{

Toast.makeText(this, "Please enable your Bluetooth and re-run this program.", Toast.LENGTH_LONG).show()

finish()

return

}

/*2:获取远程BlueToothDevice*/

BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address)

if(mBluetoothAdapter == null)

{

Toast.makeText(this, "Can't get remote device.", Toast.LENGTH_LONG).show()

finish()

return

}

/*3:获得Socket*/

try {

btSocket = device.createRfcommSocketToServiceRecord(MY_UUID)

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Socket creation failed.", e)

}

/*4:取消discovered节省资源*/

mBluetoothAdapter.cancelDiscovery()

/*5:连接*/

try {

btSocket.connect()

Log.e(TAG, "ON RESUME: BT connection established, data transfer link open.")

} catch (IOException e) {

try {

btSocket.close()

} catch (IOException e2) {

Log .e(TAG,"ON RESUME: Unable to close socket during connection failure", e2)

}

}

/*此时可以通信了,放在任意函数中*/

/*  try {

outStream = btSocket.getOutputStream()

inStream = btSocket.getInputStream() //可在TextView里显示

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.", e)

}

String message = "1"

byte[] msgBuffer = message.getBytes()

try {

outStream.write(msgBuffer)

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e)

}

*/

通用搜索模式代码模板:

简洁简洁方式1 demo

作用: 用VerticalSeekBar控制一个 LED屏幕的亮暗。

直接上码咯~

package com.example.seed2

import android.app.Activity

import android.app.AlertDialog

import android.app.Dialog

import android.os.Bundle

import java.io.IOException

import java.io.InputStream

import java.io.OutputStream

import java.util.UUID

import android.bluetooth.BluetoothAdapter

import android.bluetooth.BluetoothDevice

import android.bluetooth.BluetoothSocket

import android.content.DialogInterface

import android.util.Log

import android.view.KeyEvent

import android.widget.Toast

public class MetalSeed extends Activity {

private static final String TAG = "BluetoothTest"

private BluetoothAdapter mBluetoothAdapter = null

private BluetoothSocket btSocket = null

private OutputStream outStream = null

private InputStream inStream = null

private VerticalSeekBar vskb = null

private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")  //这条是蓝牙串口通用的UUID,不要更改

private static String address = "00:12:02:22:06:61" // <==要连接的蓝牙设备MAC地址

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState)

setContentView(R.layout.main)

this.vskb = (VerticalSeekBar)super.findViewById(R.id.mskb)

this.vskb.setOnSeekBarChangeListener(new OnSeekBarChangeListenerX())

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()

if(mBluetoothAdapter == null)

{

Toast.makeText(this, "Bluetooth is not available.", Toast.LENGTH_LONG).show()

finish()

return

}

if(!mBluetoothAdapter.isEnabled())

{

Toast.makeText(this, "Please enable your Bluetooth and re-run this program.", Toast.LENGTH_LONG).show()

finish()

return

}

}

private class OnSeekBarChangeListenerX implements VerticalSeekBar.OnSeekBarChangeListener {

public void onProgressChanged(VerticalSeekBar seekBar, int progress, boolean fromUser) {

//Main.this.clue.setText(seekBar.getProgress())

/*  String message

byte [] msgBuffer

try {

outStream = btSocket.getOutputStream()

} catch (IOException e) {

Log.e(TAG,"ON RESUME : Output Stream creation failed.", e)

}

message =Integer.toString( seekBar.getProgress() )

msgBuffer = message.getBytes()

try{

outStream.write(msgBuffer)

} catch (IOException e) {

Log.e (TAG, "ON RESUME : Exception during write.", e)

}       */

}

public void onStartTrackingTouch(VerticalSeekBar seekBar) {

String message

byte [] msgBuffer

try {

outStream = btSocket.getOutputStream()

} catch (IOException e) {

Log.e(TAG,"ON RESUME : Output Stream creation failed.", e)

}

message =Integer.toString( seekBar.getProgress() )

msgBuffer = message.getBytes()

try{

outStream.write(msgBuffer)

} catch (IOException e) {

Log.e (TAG, "ON RESUME : Exception during write.", e)

}

}

public void onStopTrackingTouch(VerticalSeekBar seekBar) {

String message

byte [] msgBuffer

try {

outStream = btSocket.getOutputStream()

} catch (IOException e) {

Log.e(TAG,"ON RESUME : Output Stream creation failed.", e)

}

message =Integer.toString( seekBar.getProgress() )

msgBuffer = message.getBytes()

try{

outStream.write(msgBuffer)

} catch (IOException e) {

Log.e (TAG, "ON RESUME : Exception during write.", e)

}

}

}

@Override

public void onStart()

{

super.onStart()

}

@Override

public void onResume()

{

super.onResume()

BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address)

try {

btSocket = device.createRfcommSocketToServiceRecord(MY_UUID)

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Socket creation failed.", e)

}

mBluetoothAdapter.cancelDiscovery()

try {

btSocket.connect()

Log.e(TAG, "ON RESUME: BT connection established, data transfer link open.")

} catch (IOException e) {

try {

btSocket.close()

} catch (IOException e2) {

Log .e(TAG,"ON RESUME: Unable to close socket during connection failure", e2)

}

}

// Create a data stream so we can talk to server.

/*  try {

outStream = btSocket.getOutputStream()

inStream = btSocket.getInputStream()

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.", e)

}

String message = "read"

byte[] msgBuffer = message.getBytes()

try {

outStream.write(msgBuffer)

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e)

}

int ret  = -1

while( ret != -1)

{

try {

ret = inStream.read()

} catch (IOException e)

{

e.printStackTrace()

}

}

*/

}

@Override

其实这里有很多的:

[gnokii-0.3.2.tar.gz]

Nokia手机工具程序。可以管理手机的电话薄,发送/接收短消息,查看电池状态等 (2001-02-14, UNIX, 731KB, 2130次)

[smslink-0.44b.tar.gz]

手机短消息服务的服务器和客户端 (2001-01-08, LINUX, 91KB, 1883次)

[移动短信SMS综合资料库.rar]

短消息基础知识;短消息的信息处理流程及其分析、解决问题的方法;手机短信息SMS开发—编码,解码;PDU介绍;短消备薯息的体系结构等 (2005-09-28, CHM, 1009KB, 1536次)

[nle-0.0.1-2.tgz]

可以修改Nokia手机的logo图标的程序 (2001-02-14, LINUX, 21KB, 1442次)

[是男人就下一百层SHY.rar]

制作的第一款休闲类的手机游戏,适合初学者参考 (2005-06-15, Java, 484KB, 1357次)

[sms_client-2.0.7k.tgz]

使用TAP的蜂窝型GSM手机短消息服务中心 (2001-01-08, LINUX, 82KB, 1333次)

[mobile_sms.zip]

使用手机发送短消息的编程方法 (2001-11-21, HTML, 5KB, 1174次)

[kvanttisms-src-0.5.tgz]

Java写的通过手机收发短信息的程序。 (2001-11-20, Java, 10KB, 1049次)

[BREW开发-海信(王宏兵).rar]

深入研究BREW手机游戏开发———— 王洪信开发者最好的初学资料 (2005-09-26, Visual C++, 7229KB, 841次)

[jSMSEngine_2_0_4.zip]

开源的手机短信开发包!包括例子程序和比较详细的文档,还有开发者的网站!来源于sourceforge! (2006-01-21, Java, 438KB, 729次)

[qrcode_js.zip]

手机仿槐者内嵌二维条码图像识别的JAVA的源程序,强烈推荐下载。 (2006-01-14, Java, 2210KB, 677次)

[gprs_sms.zip]

一个用COM或USB接口连接gsm/gprs手机进行短信收发的程序,用到的是simense的通讯模块 (2003-02-20, Visual C++, 97KB, 629次)

[PaoPao.rar]

j2me手机泡泡龙游戏。写得不错还未完工的版本。不过可以用来学习。 (2005-03-04, Java, 83KB, 613次)

[MakeMap.rar]

用java写的地图编辑器,可用于j2me手机游戏的地图编辑。 (2005-03-04, Java, 26KB, 606次)

[J2mebox.rar]

一个类似打地鼠的j2me手机游戏。 (2005-03-04, Java, 58KB, 521次)

[shoujihaomachaxun.rar]

输入手机号码可查询:归属地址、手机号码、区号、所属卡型 (2006-06-05, Java, 686KB, 520次)

[rich_man+src.rar]

大富翁手机游戏。 (2005-03-04, Java, 269KB, 513次)

[gsmssend-1.6.tar.gz]

通过网站发送手机短信息的程序。需要GNOME/GTK支持 (2001-11-20, LINUX, 352KB, 498次明禅)

[MTKstart.rar]

台湾联发(MTK)手机芯片资料,可作为手机应用的平台 (2007-08-09, C-C++, 118KB, 495次)

[C# 发短信.rar]

使用C#发短信,连接Modem或者手机,通过串口发送短信, (2004-06-30, CSharp, 437KB, 469次)

[WindowsMobile5.0.rar]

Windows Mobile 5.0 三十几个经典手机软件开发源码希望对大家有帮助. (2006-08-30, CSharp, 578KB, 449次)

[motorola_RingerToneFormat.zip]

motorola手机铃声格式文档 (2002-06-07, PDF, 45KB, 445次)

[ksiemens-0.1.tar.gz]

KDE下的西门子手机管理程序,如图标,电话薄,短信息等管理 (2001-11-21, LINUX, 3437KB, 444次)

[nec麻将.rar]

一个java编的小游戏.对初学手机游戏编程的人很有用啊. (2005-06-07, Java, 50KB, 434次)

[nokiacomposer.src.zip]

Nokia手机语音管理程序,如上载音乐等。 (2001-11-21, Visual C++, 315KB, 422次)

[SmartMessagingFAQ.zip]

诺基亚手机图片铃声开发文档 (2002-06-07, PDF, 23KB, 410次)

[motolora_smscertguide.zip]

motorola手机短信息开发文档 (2002-06-07, PDF, 134KB, 400次)

[MV100-0.1.rar]

是一个手机功能的模拟程序,从界面到功能都做了很好的模拟 (2005-07-29, C-C++, 14630KB, 384次)

[helix.src.0812.rar]

著名的 helix realplayer 基于手机 symbian 系统的 播放器全套源代码,内含编译工具、以及配套相关软件:WinCVS、Python等。花了近一个多月才整理完成,是非常难得的全套代码。 (2005-05-19, C++, 43787KB, 373次)

[eluosi方块.rar]

经典的手机游戏源码俄罗斯方块,基于C+Brew开发 (2005-07-14, C-C++, 425KB, 373次)

[MTK2.rar]

这是我上传MTK手机开发的一些资料2,这两天起上传6份资料,全部是手开发的。希望对你们有用。 (2007-04-13, C-C++, 5859KB, 371次)

[resource]

压缩包中一个为一般 *** 作系统下的fft,一个是手机或类似设备中的T9拼音输入法 (2003-08-05, C-C++, 53KB, 359次)

[SeaHorse.rar]

手机游戏,画面效果还可以,可以作为手机游戏入门参考 (2005-06-15, Java, 273KB, 356次)

[nec 打飞机.rar]

一个JAVA编的小游戏,对初学手机游戏的人很有帮助. (2005-06-07, Java, 73KB, 335次)

[多级菜单.rar]

/*[原创]一个树形多级菜单参考程序 这是一个用于车载电话的菜单程序,可以看成是手机功能菜单的简化板. 我所认为的树形多级菜单是指:在一个父菜单项目下面有多个子菜单, 子菜单下面又有多个孙菜单...,进入下层菜单主要依*当前选中的索引.有点象文件的目录结构. 本木从前实现这类的菜单主要*分层的switch语句,每层都是一个switch.但当我看到晓奇大侠的 程序和耳朵灌满lq等人的争论后,那时那地,我的心境变化了,我意识到指针代表了先进的生产力, 代表了社会的发展方向,是建设和谐社会的必要条件.不管你用了多长时间C语言,只要你不善于用 一个小针指来指去,你就是那种"用嘴吃饭的高贵骑士,决不用屁股装d步q"的守旧分子和社会发 展的绊脚石.(跑题太远,删去1万字...打住) .言归正传,下面的程序适用CPU为Mega16,编译器为CVAVR 1.24.4a 由于按键数目较多,所以按键程 序把按键事件分为数字键,快捷键,确认键,取消键,上下翻键几类,以减小菜单结构的容量.一下菜单 数据在菜单结构数组中的偏移量,有多少个菜单象就有多少个宏定义*/ (2005-08-02, C-C++, 2KB, 334次)

[与小灵通讯的软件.zip]

手机的通讯,特别是小灵通的通讯,是非常难得的技术,也是很受欢迎的,快下啊! (2005-09-30, Visual C++, 39KB, 324次)

[C16汉字输入方案.rar]

“C16汉字输入方案”,是针对小键盘设备(如手机、遥控器等)通常为16个基本键(“0”到“9”、“*”、“#”、左右键、删除键、确认键)的情况,充分发掘16个键位条件下进行汉字输入和符号输入的潜力,使汉字、英文、数字输入达到尽可能高的效率,是在16键的小键盘设备进行汉字输入的优秀方案。 (2005-10-27, C++ Builder, 76KB, 316次)

[CDMA短消息发送程序.zip]

用vc开发的cdma手机模块收发短信的功能,主要部分是串口通信和gb->unicode码间的转换。 (2005-12-08, Visual C++, 193KB, 313次)


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8243173.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-14
下一篇 2023-04-14

发表评论

登录后才能评论

评论列表(0条)

保存