蓝牙Beacon广播数据包格式以及解析
内容转载自我的博客
如果你只想找到如何用代码解析各数据请点击目录"使用Java解析数据"
@
1. 获取原始蓝牙广播包
首先需要开启开发者选项:不同Android手机打开此功能的方法基本一致,首先打开设置,然后找到系统版本号(例如MIUI系统的全部参数选项的MIUI版本),快速连续点击5次以上即可自动打开开发者选项;然后选择"打开蓝牙数据包日志"功能,接着打开蓝牙功能即可开始记录数据包,日志文件存放位置在不同的手机上略有不同;最后把日志复制到电脑上等待处理
2. 安装WireShark软件
对于ubuntu系统来说,只需要输入以下命令即可成功安装:
sudo apt-get install wireshark
对于windows或其他系统来说,打开官网按照提示下载安装即可
3. 分析Beacon广播包数据
把日志文件导入WireShark软件,会自动识别为蓝牙广播包。首先需要了解蓝牙数据包的主要格式:一个广播包是由若干个广播单元AD Structure构成的。每个广播单元的组成是:第一个字节是长度值 length,表示接下来的 length个字节是数据部分;数据部分的第一个字节表示数据的类型AD Type,AD type非常关键,决定了AD Data的数据代表的是什么以及怎么解析,这是官网上面不同的值代表的数据类型 https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile ;剩下的length-1个字节是真正的数据AD data。这里需要特别注意的是,由于发送数据是从低位到高位依次发送,所以接收到的数据要反过来按字节拼接。例如接收到的MAC为 8b 03 00 b0 01 c2,那么实际的MAC为 c2:01:b0:00:03:8b
通过分析可以发现,蓝牙设备会连续收到两个来自(同一个)Beacon的广播数据包,每个原始数据包都是59bytes,前一个主要包含MAC和设备名称等信息,后一个主要包含UUID,txPower等信息。不妨认为前一个数据包为packetA,后一个为packetB。以下是一组实际数据
3.1 第一个数据包格式
可以看到,软件会自动把每个部分的数据进行解释,如果你的英语水平可以的话,以下内容就不需要看了。数据包内容:
04 3e 38 0d 01 1b 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e
02 0a 00 08 16 f0 ff 64 27 11 4c b9 11 09 4d 69 6e 69 42 65 61 63 6f 6e 5f 30 30 39 30 37
下面是每个字节对应的含义:
第一个字节是HCI Packet Type,04表示这是HCI Event;剩下的58bytes则是HCI Event的具体内容
第二个字节是EventCode,3e是此事件的代码;第三个字节是Parameter Length,0x38(十进制56)表示后面数据长度56bytes
第四个字节是SubEvent,0d表示这是LE Extended Advertising Report;第五个字节是Num Reports,数值为01
1b 00这两个字节代表Event Type,由于发送数据都是按字节发送以及从低位向高位发送,因此真实值是 001b
01 表示这是随机设备地址
8b 03 00 b0 01 c2 是此设备的MAC,根据从低向高的发送规则,所以真实MAC是 c2:01:b0:00:03:8b
01 代表首要广播信道的带宽
00 代表次要广播信道的带宽,此处表示不使用次要信道
ff 表示广播SID
7f 代表Tx Power的大小,此处是127dbm
af 代表RSSI的大小,此处是-81dbm
00 00 代表周期广播间隔
00 代表直接地址类型,次数是公共设备地址
00 00 00 00 00 00 代表直接BD_ADDR
1e 代表接下的的数据的字节数(长度),以下数据就是最重要的广播数据了
-------------------------------------------
02 0a 00 代表的是Tx Power Level的信息,02 表示数据字节数,0a 表示数据类型,00 表示功率水平(单位是dBm)
08 16 f0 ff 代表的是Service Data信息,08 表示数据字节数,16 表示数据类型,ff f0 表示16bit UUID ,
64 27 11 4c b9 表示Service Data的具体信息
11 表示后面的数据的字节数
09 表示数据类型
4d 69 6e 69 42 65 61 63 6f 6e 5f 30 30 39 30 37 表示设备的名称,每一个字节对应一个ASCII
如果你想查看WireShark软件的解析名称,可在附录里浏览
3.2 第二个数据包格式
此数据包才是最重要的,59bytes的数据包内容如下:
04 3e 38 0d 01 13 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e
02 01 06 1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 27 11 4c b9 c5
下面是每个字节对应的含义:
第一行数据同上,不再分析,重点分析第二行(也就是广播数据部分)
--------------------------------------
02 表示接下来的数据有两个字节
01 表示数据类型,此处的类型是Flags
06 表示Flags的具体模式
1a 表示接下来的数据有26个字节
ff 表示数据类型,此处是厂家特定字(Manufacturer specific)
4c 00 表示公司的ID,此处的004c代表苹果公司
02 代表beacon标识位
15 表示接下来有22个字节的数据
fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 表示beacon UUID
27 11 是major的值,2711转化为10进制是10001
4c b9 是minor的值,4cb9转化为10进制是19641
c5 是txPower的补码,计算可知原码是-59
如果你想查看WireShark软件的解析名称,可在附录里浏览
3.3 Android程序开发中的蓝牙广播包
上面所讲解的都是Android 系统中的蓝牙广播包的格式,是最底层的数据包格式,如果我们是在开发OS 的话才可能会接触解析这些数据。对于只是进行普通应用程序开发的我们来说,只需要处理已经被Android 系统一次解析之后的数据包,这个数据包才是我们开发应用程序时遇到的数据记录。
对于Android 开发中,系统会把packetB中的第二行数据(30bytes,长度不定)和packetA(30bytes)中的第二行数据连接在一起,最后总的数据长度为62bytes,不够的话用0填充,如下所示:
02 01 06 1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 27 11 4c b9 c5 02 0a 00 08 16 f0 ff 64 27 11 4c b9 11 09 4d 69 6e 69 42 65 61 63 6f 6e 5f 30 30 39 30 37 00 00
4. 使用Java解析各数据
以下是主要解析代码,兼容Android P最新版本和Android 较低版本:
//Android Lollipop 版本以上的扫描回调函数
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
final BluetoothDevice device = result.getDevice();
final int rssi = result.getRssi();
final byte[] scanRecord = result.getScanRecord().getBytes();
// 判断Activity是否已经退出
if (mainActivity == null) {
return;
}
mainActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
String name = device.getName();
if (name == null) {
//Log.d(TAG, "onLeScan: 此条数据被过滤---"+mac);
return;
}
Log.d(TAG, "onScanResult: 开始处理广播数据");
handleScanResult(device, rssi, scanRecord);
}
});
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.e(TAG, "启动扫描失败");
}
};
// Android KITKAT 版本以下的扫描回调函数
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
// 判断Activity是否已经退出
if (mainActivity == null) {
return;
}
mainActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
String name = device.getName();
String mac = device.getAddress();
// 官方提供的过滤UUID方法存在问题,此处自己先根据MAC过滤
if (name == null || mac == null || !Arrays.asList(beaconMAC).contains(mac)) {
//Log.d(TAG, "onLeScan: 此条数据被过滤---"+mac);
return;
}
Log.d(TAG, "onLeScan: 开始处理广播数据");
handleScanResult(device, rssi, scanRecord);
}
});
}
};
// 处理广播数据
private void handleScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) {
int startIndex = 2;
boolean patternFound = false;
Log.d(TAG, "handleScanResult: "+bytesToHex(scanRecord));
// 寻找是否存在beacon以及有效数据的起始索引
while (startIndex <= 5) {
if (((int) scanRecord[startIndex + 2] & 0xff) == 0x02
&& ((int) scanRecord[startIndex + 3] & 0xff) == 0x15) {
patternFound = true;
break;
}
startIndex++;
}
// 如果找到
if (patternFound) {
String ibeaconName = device.getName();
String mac = device.getAddress();
Log.d(TAG, "onLeScan: 搜索到一个Beacon设备" + mac);
Log.d(TAG, "onLeScan: 它的名字是" + ibeaconName);
String data = parseBLEData(scanRecord, startIndex);
Log.d(TAG, "onLeScan: RSSI=" + rssi);
String result = "Beacon设备名称:" + ibeaconName + " MAC:" + mac + "\n";
double distance = rssi2distance(rssi);
String dis = String.format("%.2f", distance);
result = result + data + " RSSI:" + rssi + " ( " + dis + " )";
Log.d(TAG, result);
} else {
String name = device.getName();
if (name == null) {
//Log.d(TAG, "onLeScan: 搜索到一个普通蓝牙设备,它的MAC是"+device.getAddress());
} else {
//Log.d(TAG, "onLeScan: 搜索到一个普通蓝牙设备,它的名字是"+name);
}
}
}
// 根据RSSI计算距离
public double rssi2distance(int rssi) {
int iRssi = Math.abs(rssi);
// 发射端和接收端相隔1米时的信号强度
int A = 59;
// 环境噪声衰减因子
double n = 2.0;
double power = (iRssi - A) / (10 * n);
return Math.pow(10, power);
}
// 字节数据转为Hex: 1 byte = 8bit = 两个16进制数字
public String bytesToHex(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
// 格式化UUID
public String parseUUID(String data) {
String uuid = "";
if (data.length() == 32) {
uuid = data.substring(0, 8) + "-"
+ data.substring(8, 12) + "-"
+ data.substring(12, 16) + "-"
+ data.substring(16, 20) + "-"
+ data.substring(20);
} else {
showTips(getString(R.string.toast_uuid_not_found));
}
return uuid;
}
// 解析BLE数据
public String parseBLEData(byte[] scanRecord, int startIndex) {
// uuid的长度是16bytes
byte[] uuidBytes = new byte[16];
System.arraycopy(scanRecord, startIndex + 4, uuidBytes, 0, 16);
String hexString = bytesToHex(uuidBytes);
// beacon的UUID值
String uuid = parseUUID(hexString);
// beacon的Major值
int major = (scanRecord[startIndex + 20] & 0xff) * 0x100
+ (scanRecord[startIndex + 21] & 0xff);
// ibeacon的Minor值
int minor = (scanRecord[startIndex + 22] & 0xff) * 0x100
+ (scanRecord[startIndex + 23] & 0xff);
int txPower = (scanRecord[startIndex + 24]);
Log.d(TAG, "onLeScan: 它的UUID是" + uuid + ",txPower是" + txPower);
Log.d(TAG, "onLeScan: major=" + major + ",minor=" + minor);
return "UUID:" + uuid + "\nmajor:" + major + " minor:" + minor + "\ntxPower:" + txPower;
}
以上就是主要代码
5. 附录
5.1 第一个数据包的内容以及解析
数据包内容:
04 3e 38 0d 01 1b 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e
02 0a 00 08 16 f0 ff 64 27 11 4c b9 11 09 4d 69 6e 69 42 65 61 63 6f 6e 5f 30 30 39 30 37
下面是每个字节对应的含义(WireShark软件的解析):
Bluetooth HCI H4
[Direction: Rcvd (0x01)]
HCI Packet Type: HCI Event (0x04)
Bluetooth HCI Event - LE Meta
Event Code: LE Meta (0x3e)
Parameter Total Length: 56
Sub Event: LE Extended Advertising Report (0x0d)
Num Reports: 1
Event Type: 0x001b, Connectable, Scannable, Scan Response, Legacy, Data Status: Complete
.... .... .... ...1 = Connectable: True
.... .... .... ..1. = Scannable: True
.... .... .... .0.. = Directed: False
.... .... .... 1... = Scan Response: True
.... .... ...1 .... = Legacy: True
.... .... .00. .... = Data Status: Complete (0x0)
0000 0000 0... .... = Reserved: 0x000
Peer Address Type: Random Device Address (0x01)
BD_ADDR: c2:01:b0:00:03:8b (c2:01:b0:00:03:8b)
Primary PHY: LE 1M (0x01)
Secondary PHY: No packets on the secondary advertising channel (0x00)
Advertising SID: 0xff (not available)
TX Power: 127dBm (not available)
RSSI: -81dBm
Periodic Advertising Interval: 0x0000 (no periodic advertising)
Direct Address Type: Public Device Address (0x00)
Direct BD_ADDR: 00:00:00_00:00:00 (00:00:00:00:00:00)
Data Length: 30
Advertising Data
Tx Power Level
Length: 2
Type: Tx Power Level (0x0a)
Power Level (dBm): 0
Service Data - 16 bit UUID
Length: 8
Type: Service Data - 16 bit UUID (0x16)
UUID 16: Unknown (0xfff0)
Service Data: 6427114cb9
Device Name: MiniBeacon_00907
Length: 17
Type: Device Name (0x09)
Device Name: MiniBeacon_00907
5.2 第二个数据包的内容以及解析
数据包内容:
04 3e 38 0d 01 13 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e
02 01 06 1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 27 11 4c b9 c5
下面是每个字节对应的含义(WireShark软件的解析):
Bluetooth HCI H4
[Direction: Rcvd (0x01)]
HCI Packet Type: HCI Event (0x04)
Bluetooth HCI Event - LE Meta
Event Code: LE Meta (0x3e)
Parameter Total Length: 56
Sub Event: LE Extended Advertising Report (0x0d)
Num Reports: 1
Event Type: 0x0013, Connectable, Scannable, Legacy, Data Status: Complete
.... .... .... ...1 = Connectable: True
.... .... .... ..1. = Scannable: True
.... .... .... .0.. = Directed: False
.... .... .... 0... = Scan Response: False
.... .... ...1 .... = Legacy: True
.... .... .00. .... = Data Status: Complete (0x0)
0000 0000 0... .... = Reserved: 0x000
Peer Address Type: Random Device Address (0x01)
BD_ADDR: c2:01:b0:00:03:8b (c2:01:b0:00:03:8b)
Primary PHY: LE 1M (0x01)
Secondary PHY: No packets on the secondary advertising channel (0x00)
Advertising SID: 0xff (not available)
TX Power: 127dBm (not available)
RSSI: -81dBm
Periodic Advertising Interval: 0x0000 (no periodic advertising)
Direct Address Type: Public Device Address (0x00)
Direct BD_ADDR: 00:00:00_00:00:00 (00:00:00:00:00:00)
Data Length: 30
Advertising Data
Flags
Length: 2
Type: Flags (0x01)
000. .... = Reserved: 0x0
...0 .... = Simultaneous LE and BR/EDR to Same Device Capable (Host): false (0x0)
.... 0... = Simultaneous LE and BR/EDR to Same Device Capable (Controller): false (0x0)
.... .1.. = BR/EDR Not Supported: true (0x1)
.... ..1. = LE General Discoverable Mode: true (0x1)
.... ...0 = LE Limited Discoverable Mode: false (0x0)
Manufacturer Specific
Length: 26
Type: Manufacturer Specific (0xff)
Company ID: Apple, Inc. (0x004c)
Data: 0215fda50693a4e24fb1afcfc6eb0764782527114cb9c5
[Expert Info (Note/Undecoded): Undecoded]
[Undecoded]
[Severity level: Note]
[Group: Undecoded]
蓝牙Beacon广播数据包格式以及解析的更多相关文章
- IM通信协议逆向分析、Wireshark自定义数据包格式解析插件编程学习
相关学习资料 http://hi.baidu.com/hucyuansheng/item/bf2bfddefd1ee70ad68ed04d http://en.wikipedia.org/wiki/I ...
- GPS数据包格式及数据包解析
GPS数据包解析 GPS数据包解析 目的 GPS数据类型及格式 数据格式 数据解释 解析代码 结构体定义 GPRMC解析函数 GPGGA解析函数 测试样例输出 gps数据包格式 gps数据解析 车联网 ...
- [转帖]IP /TCP协议及握手过程和数据包格式中级详解
IP /TCP协议及握手过程和数据包格式中级详解 https://www.toutiao.com/a6665292902458982926/ 写的挺好的 其实 一直没闹明白 网络好 广播地址 还有 网 ...
- IP数据包格式
IP数据包格式 0 4 8 16 31 |4位版本 | 4位首部长度 | 8位服务类型 | 16位总长度(字节数)| |16位标识 | 3位标志 | 13位片偏移 | |8位生存时间| 8位协议 | ...
- ETHERNET数据包格式( IP & UDP & ICMP & ARP )
ETHERNET数据包格式( IP & UDP & ICMP & ARP ) ETHERNET数据包格式 一.ETHERNET 数据包的协议类型 TYPE 的值为 0x0800 ...
- [na]ip数据包格式
IP Datagram Structure 字段名 解释 版本 IP协议的版本,目前的IP协议版本号为4,下一代IP协议版本号为6. 首部长度 IP报头的长度.固定部分的长度(20字节)和可变部分的长 ...
- 网络:W5500 UDP数据包格式注意事项
1. 主题 使用W5500测试UDP功能,发现收到的数据包和wireshark抓包的数据不同. 原来W5500接收寄存器的数据包并不是网络上的数据流,而是经过内部处理后展现出来的. 对于这个问题目前 ...
- H264的RTP负载打包的数据包格式,分组,分片
H264的RTP负载打包的数据包格式,分组,分片 1. RTP数据包格式 RTP报文头格式(见RFC3550 Page12): 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 ...
- 蓝牙BLE数据包格式汇总
以蓝牙4.0为例说明: BLE包格式有:广播包.扫描包.初始化连接包.链路层控制包(LL层数据包).逻辑链路控制和自适应协议数据包(即L2CAP数据包)等: 其中广播包又分为:定向广播包和非定向广播包 ...
随机推荐
- Topshelf结合Quartz.NET实现服务端定时调度任务
这周接受到一个新的需求:一天内分时间段定时轮询一个第三方WebAPI,并保存第三方WebAPI结果. 需求分析:分时段.定时开启.定时结束.轮询.主要工作集中在前三个上,轮询其实就是个Http请求,比 ...
- qtablewidget qss加上这个,QHeaderView::section
qtablewidget qss加上这个,QHeaderView::section { color: white; padding: 4px; height:24px; b ...
- API 文档管理工具 (Yapi) Docker Compose部署指南
前言介绍 Yapi 由 YMFE 开源,旨在为开发.产品.测试人员提供更优雅的接口管理服务,可以帮助开发者轻松创建.发布.维护 API. 权限管理 YApi 成熟的团队管理扁平化项目权限配置满足各类企 ...
- Centos 7上安装Python3.x(单版本)
Centos7默认安装的是2.7,这里选择安装使用Python3.6.3 安装Python3.6.3 1.安装python3 需要的依赖包 yum install -y openssl-devel b ...
- IIS7上传4M文件以上文件出现“Post大小超出允许的限制”错误解决方法
在web.config文件中的system.web节点中添加如下这句,即40M <system.web> <httpRuntime maxRequestLength = " ...
- Storm —— 单机环境搭建
1. 安装环境要求 you need to install Storm's dependencies on Nimbus and the worker machines. These are: Jav ...
- Java学习笔记-spring整合mybatis
这个项目就是一个例子,只有添加图书的功能: 项目架构: resource: 整合流程: 1.pom文件节点,这两个是整合用的,其他节点不再赘述: <!-- https://mvnreposito ...
- 基于STM32之UART串口通信协议(三)接收
一.前言 1.简介 回顾上一篇UART发送当中,已经讲解了如何实现UART的发送操作了,接下来这一篇将会继续讲解如何实现UART的接收操作. 2.UART简介 嵌入式开发中,UART串口通信协议是我们 ...
- c#基础四
写入一个XML文件 using System; using System.Collections.Generic; using System.Linq; using System.Text; usin ...
- HDU 5521:Meeting(最短路)
http://acm.hdu.edu.cn/showproblem.php?pid=5521 Meeting Problem Description Bessie and her friend E ...