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

从Android 5.0+开始才支持。 api level >= 21

所以5.0 之前设备,是不能向外发送广播的。

Android中心设备(central)的支持:

从Android 4.3+ 。 api level  >= 18

1、初始化蓝牙

2、检查ble是否可用

3、开启广播

4、扫描响应数据

5、创建iBeacon 广播数据

6、广播设置

7、开启广播后的回调

(1)初始化蓝牙:

添加权限:

     <uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- 6.0之后蓝牙还需要地理位置权限 -->
<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="false" />

初始化:

 //初始化BluetoothManager和BluetoothAdapter
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) mContext.getSystemService(BLUETOOTH_SERVICE);
} if (mBluetoothManager != null && mBluetoothAdapter == null) {
mBluetoothAdapter = mBluetoothManager.getAdapter();
}

(2)检查是否可使用ble:

 if (!activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(activity, "不支持ble", Toast.LENGTH_LONG).show(); return;
} final BluetoothManager mBluetoothManager = (BluetoothManager) activity.getSystemService(BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter(); if (mBluetoothAdapter == null) {
Toast.makeText(activity, "不支持ble", Toast.LENGTH_LONG).show(); return;
}
// 获取蓝牙ble广播
16 mBluetoothAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
if (mBluetoothAdvertiser == null) {
Toast.makeText(activity, "the device not support peripheral", Toast.LENGTH_SHORT).show();
Log.e(TAG, "the device not support peripheral"); return;
}

(3)开启广播:

public void startAdvertising(MockServerCallBack callBack) {
//获取BluetoothLeAdvertiser,BLE发送BLE广播用的一个API
if (mBluetoothAdvertiser == null) {
mBluetoothAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
}
if (mBluetoothAdvertiser != null) {try {
//创建BLE beacon Advertising并且广播
mBluetoothAdvertiser.startAdvertising(createAdvSettings(true, 0)
, createIBeaconAdvertiseData(BluetoothUUID.bleServerUUID,
mMajor, mMinor, mTxPower)
, createScanAdvertiseData(mMajor, mMinor, mTxPower, mAdvCallback);
} catch (Exception e) {
Log.v(TAG, "Fail to setup BleService");
}
}
}

一个ble广播包:广播数据其实包含两部分:Advertising Data(广播数据) 和 Scan Response Data(扫描响应数据)。

通常情况下,广播的一方,按照一定的间隔,往空中广播 Advertising Data。

当某个监听设备监听到这个广播数据时候,会通过发送 Scan Response Request,请求广播方发送扫描响应数据数据。

这两部分数据的长度都是固定的 31 字节。

在 Android 中,系统会把这两个数据拼接在一起,返回一个 62 字节的数组。

(4)扫描响应数据:

可以自定义数据,比如增加湿度,温度等。

     //设置scan广播数据
public static AdvertiseData createScanAdvertiseData(short major, short minor, byte txPower) {
AdvertiseData.Builder builder = new AdvertiseData.Builder();
builder.setIncludeDeviceName(true); byte[] serverData = new byte[5];
ByteBuffer bb = ByteBuffer.wrap(serverData);
bb.order(ByteOrder.BIG_ENDIAN);
bb.putShort(major);
bb.putShort(minor);
bb.put(txPower);
builder.addServiceData(ParcelUuid.fromString(BluetoothUUID.bleServerUUID.toString())
, serverData); AdvertiseData adv = builder.build();
return adv;
}

5、创建ibeacon 广播数据。

iBeacon 的广播结构:iBeacon 只是协议.

  • the 2 byte beacon identifier (0xBEAC)
  • the 16 bytes UUID
  • the 2 byte major
  • the 2 byte minor
  • the 1 byte tx power

Byte 0-2: Standard BLE Flags

 Byte 0: Length :  0x02
Byte 1: Type: 0x01 (Flags)
Byte 2: Value: 0x06 (Typical Flags)

Byte 3-29: Apple Defined iBeacon Data

 Byte 3: Length: 0x1a
Byte 4: Type: 0xff (Custom Manufacturer Packet)
Byte 5-6: Manufacturer ID : 0x4c00 (Apple)
Byte 7: SubType: 0x2 (iBeacon)
Byte 8: SubType Length: 0x15
Byte 9-24: Proximity UUID
Byte 25-26: Major
Byte 27-28: Minor
Byte 29: Signal Power

ManufactureData : 设备厂商的自定义数据

使用addManufactureData(int manufacturerId, byte[] manufacturerSpecificData);

第一个参数0x004c,是厂商id,id长度为2个字节,不足2个字节系统会补0.

(比如id传入0xac, 不足两个字节,输入广播时:ac, 00)

 /**
* create AdvertiseDate for iBeacon
*/
public static AdvertiseData createIBeaconAdvertiseData(UUID proximityUuid, short major, short minor, byte txPower) { String[] uuidstr = proximityUuid.toString().replaceAll("-", "").toLowerCase().split("");
byte[] uuidBytes = new byte[16];
for (int i = 1, x = 0; i < uuidstr.length; x++) {
uuidBytes[x] = (byte) ((Integer.parseInt(uuidstr[i++], 16) << 4) | Integer.parseInt(uuidstr[i++], 16));
}
byte[] majorBytes = {(byte) (major >> 8), (byte) (major & 0xff)};
byte[] minorBytes = {(byte) (minor >> 8), (byte) (minor & 0xff)};
byte[] mPowerBytes = {txPower};
byte[] manufacturerData = new byte[0x17];
byte[] flagibeacon = {0x02, 0x15}; System.arraycopy(flagibeacon, 0x0, manufacturerData, 0x0, 0x2);
System.arraycopy(uuidBytes, 0x0, manufacturerData, 0x2, 0x10);
System.arraycopy(majorBytes, 0x0, manufacturerData, 0x12, 0x2);
System.arraycopy(minorBytes, 0x0, manufacturerData, 0x14, 0x2);
System.arraycopy(mPowerBytes, 0x0, manufacturerData, 0x16, 0x1); AdvertiseData.Builder builder = new AdvertiseData.Builder();
builder.addManufacturerData(0x004c, manufacturerData); AdvertiseData adv = builder.build();
return adv;
}

6、创建广播设置:模式,是否可连接,功率

 setAdvertiseMode(int advertiseMode)
设置广播的模式,低功耗,平衡和低延迟三种模式;
对应 AdvertiseSettings.ADVERTISE_MODE_LOW_POWER ,ADVERTISE_MODE_BALANCED ,ADVERTISE_MODE_LOW_LATENCY
从左右到右,广播的间隔会越来越短
 setConnectable(boolean connectable)
设置是否可以连接。
广播分为可连接广播和不可连接广播,一般不可连接广播应用在iBeacon设备上,这样APP无法连接上iBeacon设备
 setTimeout(int timeoutMillis)
设置广播的最长时间,最大值为常量AdvertiseSettings.LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; 180秒
设为0时,代表无时间限制会一直广播
 setTxPowerLevel(int txPowerLevel)
设置广播的信号强度
常量有AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW, ADVERTISE_TX_POWER_LOW, ADVERTISE_TX_POWER_MEDIUM, ADVERTISE_TX_POWER_HIGH
从左到右分别表示强度越来越强.
举例:当设置为ADVERTISE_TX_POWER_ULTRA_LOW时,
手机1和手机2放在一起,手机2扫描到的rssi信号强度为-56左右,
当设置为ADVERTISE_TX_POWER_HIGH 时, 扫描到的信号强度为-33左右,
信号强度越大,表示手机和设备靠的越近

* AdvertiseSettings.ADVERTISE_TX_POWER_HIGH -56 dBm @ 1 meter with Nexus 5

* AdvertiseSettings.ADVERTISE_TX_POWER_LOW -75 dBm @ 1 meter with Nexus 5

* AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM -66 dBm @ 1 meter with Nexus 5

*AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW not detected with Nexus 5

 public AdvertiseSettings createAdvSettings(boolean connectable, int timeoutMillis) {
AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
//设置广播的模式, 功耗相关
builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED);
builder.setConnectable(connectable);
builder.setTimeout(timeoutMillis);
builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
AdvertiseSettings mAdvertiseSettings = builder.build();
if (mAdvertiseSettings == null) {
Log.e(TAG, "mAdvertiseSettings == null");
}
return mAdvertiseSettings;
}

7、开始广播后的回调。提示广播开启是否成功。

 //发送广播的回调,onStartSuccess/onStartFailure很明显的两个Callback
private AdvertiseCallback mAdvCallback = new AdvertiseCallback() {
public void onStartSuccess(android.bluetooth.le.AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
if (settingsInEffect != null) {
Log.d(TAG, "onStartSuccess TxPowerLv=" + settingsInEffect.getTxPowerLevel() + " mode=" + settingsInEffect.getMode() + " timeout=" + settingsInEffect.getTimeout());
} else {
Log.d(TAG, "onStartSuccess, settingInEffect is null");
}
} public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Log.d(TAG, "onStartFailure errorCode=" + errorCode); if (errorCode == ADVERTISE_FAILED_DATA_TOO_LARGE) {
Toast.makeText(mContext, "advertise_failed_data_too_large", Toast.LENGTH_LONG).show();
Log.e(TAG, "Failed to start advertising as the advertise data to be broadcasted is larger than 31 bytes.");
} else if (errorCode == ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) {
Toast.makeText(mContext, "advertise_failed_too_many_advertises", Toast.LENGTH_LONG).show();
Log.e(TAG, "Failed to start advertising because no advertising instance is available."); } else if (errorCode == ADVERTISE_FAILED_ALREADY_STARTED) {
Toast.makeText(mContext, "advertise_failed_already_started", Toast.LENGTH_LONG).show();
Log.e(TAG, "Failed to start advertising as the advertising is already started"); } else if (errorCode == ADVERTISE_FAILED_INTERNAL_ERROR) {
Toast.makeText(mContext, "advertise_failed_internal_error", Toast.LENGTH_LONG).show();
Log.e(TAG, "Operation failed due to an internal error"); } else if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
Toast.makeText(mContext, "advertise_failed_feature_unsupported", Toast.LENGTH_LONG).show();
Log.e(TAG, "This feature is not supported on this platform"); }
}
};

注意:对于ios 设备接受广播,外围设备还是要广播出来一个16位的serviceUUID,因为扫描的时候要用(如果不指定特定服务的UUID,没有办法进行后台持续扫描连接).

Android 上的低功耗蓝牙实践

源代码demo

android BLE Peripheral 模拟 ibeacon 发出ble 广播的更多相关文章

  1. android BLE Peripheral 手机模拟设备发出BLE广播 BluetoothLeAdvertiser

    android 从4.3系统开始可以连接BLE设备,这个大家都知道了.iOS是从7.0版本开始支持BLE. android 进入5.0时代时,开放了一个新功能,手机可以模拟设备发出BLE广播, 这个新 ...

  2. android BLE Peripheral 做外设模拟设备,供ios、android 连接通讯。

    为了能让其它设备可以发现其设备,先启动特定广播.看自己需要什么广播格式. 对于广播可见的mac address: 在调用startAdvertising();时,mac address 就会改变. 并 ...

  3. android蓝牙4.0(BLE)开发之ibeacon初步

    一个april beacon里携带的信息如下 ? 1 <code class=" hljs ">0201061AFF4C0002159069BDB88C11416BAC ...

  4. android5.0(Lollipop) BLE Peripheral牛刀小试

    转载请表明作者:http://blog.csdn.net/lansefeiyang08/article/details/46468743 知道Android L对蓝牙对了一些改进.包含加入A2dp s ...

  5. Android 4.4.2上与BLE 蓝牙锁设备的通讯

    Android从4.3(Api level 18)开始支持BLE的开发,本文记录了Android 4.4.2设备与BLE设备通讯的流程. 权限需求: <uses-permission andro ...

  6. Android 6.0 扫描不到 Ble 设备需开启位置权限

    Android 6.0 扫描不到 Ble 设备需开启位置权限 之前做 Ble 开发都是在 Android 6.0 系统以下的版本中进行测试的,今天使用 Android 6.0 的设备测试的时候,发现扫 ...

  7. 详解BLE 空中包格式—兼BLE Link layer协议解析

    BLE有几种空中包格式?常见的PDU命令有哪些?PDU和MTU的区别是什么?DLE又是什么?BLE怎么实现重传的?BLE ACK机制原理是什么?希望这篇文章能帮你回答以上问题. 虽然BLE空中包(pa ...

  8. Android 中的消息传递,详解广播机制

    --------------------------------------广播机制简介--------------------------------------------- Android中的广 ...

  9. Android学习总结(七)———— 本地广播

    一.本地广播 2.1 基本概念 由于之前的广播都是全局的,所有应用程序都可以接收到,这样就很容易会引起安全性的问题,比如说我们发送一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停 ...

随机推荐

  1. spring kafka生产、消费消息

    参考网址: https://blog.csdn.net/lansetiankong12/article/details/54946641 1.新建Maven项目-KafkaMaven ->点击n ...

  2. ES7的async/await

    async 表示这是一个async函数,await只能用在这个函数里面. await 表示在这里等待promise返回结果了,再继续执行. await 后面跟着的应该是一个promise对象 awai ...

  3. day 23-1 类的命名空间、组合

    类的命名空间 类与对象命名空间 类里 可以定义两种属性 静态属性 动态属性 类中的静态变量 可以被对象和类调用对于不可变数据类型来说,类变量最好用类名操作对于可变数据类型来说,对象名的修改是共享的,重 ...

  4. 软件测试之实际工作工作方式001--log4

    软件测试之实际工作工作方式001--log4 Dotest软件测试-董浩整理   领导安排任务后:   1)首先要确认理解:是指的某工作吗?具体有什么要求吗?时间截止到什么时候? 解析:   a.万一 ...

  5. windows安装pycrypto报错

    在Windows上安装的时候直接 pip install pycrypto会报错 由于直接安装安装Crypto模块 会报错如下:因此需要先安装Microsoft Visual C++ 9.0 进入下载 ...

  6. 【转载 | 笔记】IIS无法删除应该程序池 因为它包含X个应用程序

    IIS无法删除应该程序池 因为它包含X个应用程序 今天代码主分支在vs2015创建了虚拟目录http://localhost/webapp指向的物理路径是E:\webapp 之后新开了一个分支把代码放 ...

  7. 21 re正则模块 垃圾回收机制

    垃圾回收机制 不能被程序访问到的数据,就称之为垃圾 引用计数 引用计数:用来记录值的内存地址被记录的次数的:当一个值的引用计数为0时,该值就会被系统的垃圾回收机制回收 每一次对值地址的引用都可以使该值 ...

  8. Scyther 论文相关资料整理

    1.Scyther 的特点使用方法 Scyther可以提供轨迹的简单描述,方便分析协议可能出现的攻击和表现,使用Athena算法,该软件表现如下特点: 该软件有明确的终止,能工提供无限会话协议安全性的 ...

  9. 总结UIViewController的view在有navBar和tabBar影响下布局区域的问题

    影响 View 布局区域的有以下三个属性: self.edgesForExtendedLayout (影响View布局区域的主要属性) self.navigationController.naviga ...

  10. python3 字典(dictionary)(二)

    内置函数: len(dict):计算字典元素个数,即键的总数 str(dict):输出字典,以可打印的字符串表示. type(variable):返回输入的变量类型,如果变量是字典就返回字典类型.   ...