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. 基于Gecko内核的简单浏览器实现

    分享一个基于Gecko内核的简单浏览器实现过程. 项目需要需要开发一个简单浏览器,由于被访问的网页中有大量Apng做的动画,使用IE内核的webbrowser不能播放,使用基于WebKit和Cefsh ...

  2. word20170106在机场 At the airport有用的词和句子

    有用的词: airport terminal: 航站楼 domestic flight: 国内航班 international flight: 国际航班 checked luggage: 托运行李 c ...

  3. axios formData提交数据 && axios设置charset无效???

    但是这样会出现一个问题,什么问题呢? 我设置了请求头编码utf-8,但是没生效 content-type里面没有出现utf-8???????查了很多资料,说这是axios固有的bug,我....... ...

  4. SpringMVC使用CommonsMultipartResolver上传文件

    DispatcherServlet并没有实现任何解析multipart请求数据的功能,它将该任务委托给了Spring中MultipartResolver策略接口的实现,通过该接口的实现类来解析mult ...

  5. AttributeError: 'module' object has no attribute 'enableTrace'

    Traceback (most recent call last): File "Long-lived-connection.py", line 29, in <module ...

  6. 015_ICMP专项研究监控

    一.数据demo cat /proc/net/snmp Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagr ...

  7. 收集Typecho 0.9还能用的插件

    收集Typecho 0.9还能用的插件 名称 描述 版本 作者 BaiduSubmit 百度结构化插件 for Typecho 0.5.2 老高 CateFilter 首页过滤指定分类 1.2.1 R ...

  8. C# this关键字的四种用法

    Go to below URL to find more details and example. http://blog.csdn.net/longlong821/article/details/7 ...

  9. LeetCode.atoi

    请你来实现一个 atoi 函数,使其能将字符串转换成整数. 首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止. 当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之 ...

  10. 【mongo】查询超时处理

    使用no_cursor_timeout collection = self.db[tb_name] cols = collection.find(no_cursor_timeout=True) for ...