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. vue组件创建的三种方式

    1.使用Vue.extend创建全局的Vue组件 //1.1 使用vue.extend创建组件 var com1 = Vue.extend({ //通过template属性指定组件要展示的html结构 ...

  2. hdu5965扫雷 枚举+递推

    题目链接 思路:枚举第一列的可能种数,然后递推即可,中途判断是否满足条件,最后再判断最后一列是否满足条件即可. #include<bits/stdc++.h> #define LL lon ...

  3. exists,in的区别-mysql

    如说两张表一张是用户表TDefUser(userid,address,phone),一张是消费表TAccConsume(userid,time,amount),我要查消费超过5000的用户记录,那么我 ...

  4. BZOJ4314 倍数?倍数!

    好神仙啊.... 题意 在$ [0,n) $中选$ k$个不同的数使和为$ n$的倍数 求方案数 $ n \leq 10^9, \ k \leq 10^3$ 题解 k可以放大到1e6的 先不考虑$ k ...

  5. js一些格式化

    /* 格式化金额 */function formatAmount(s, n) {      n = n > 0 && n <= 20 ? n : 2;      s = p ...

  6. 清北学堂学习总结day2

    今天是钟皓曦大佬讲课,先来膜一波   %%%%% •数论 数论是这次培训的一个重点,那么什么是数论呢? 数论是研究整数性质的东西,所以理论上day2不会涉及小数QwQ (切入正题) •整除性: 设a, ...

  7. Gitlab_ansible_jenkins三剑客②Gitlab的后台管理

    系统信息和日志 健康状态 使用gitlab的用户管理和审批功能 创建用户 创建一个lead普通账号 进入test-repo仓库 这样就把dev添加到了test-repo这个项目中,并且有了develo ...

  8. eclipse:插件安装总结

    1.SVN插件 首先安装SVNKIT,下载地址:https://svnkit.com/download.php 再安装Subclipse,这个在marketplace安装即可. 2.安装时报错:Una ...

  9. 高可用Redis(五):瑞士军刀之慢查询,Pipeline和发布订阅

    1.慢查询 1.1 慢查询的生命周期 步骤一:client通过网络向Redis发送一条命令 步骤二:由于Redis是单线程应用,可以把Redis想像成一个队列,client执行的所有命令都在排队等着s ...

  10. luoguP1941-

    飞扬的小鸟 20分&50分: #include<iostream> #include<cstdio> #include<cstring> #include& ...