【转】BLE 学习记录
原文网址:http://m.blog.csdn.net/blog/chiooo/43985401
BLE 学习记录
ANROID BLE 开发,基于 bluetoothlegatt 分析
- mBluetoothAdapter = mBluetoothManager.getAdapter(); 得到 手机上蓝牙主机的适配器 mBluetoothAdapter - public boolean initialize() { 
 // For API level 18 and above, get a reference to BluetoothAdapter through
 // BluetoothManager.
 if (mBluetoothManager == null) {
 mBluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
 if (mBluetoothManager == null) {
 Log.e(TAG, “Unable to initialize BluetoothManager.”);
 return false;
 }
 }- mBluetoothAdapter = mBluetoothManager.getAdapter();
 if (mBluetoothAdapter == null) {
 Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
 return false;
 } return true;
 - } 
2.获得mBluetoothGatt,注册回调
 * Connects to the GATT server hosted on the Bluetooth LE device.
 *
 * @param address The device address of the destination device.
 *
 * @return Return true if the connection is initiated successfully. The connection result
 *         is reported asynchronously through the
 *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
 *         callback.
public boolean connect(final String address) {
    if (mBluetoothAdapter == null || address == null) {
        Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
        return false;
    }
    // Previously connected device.  Try to reconnect.
    if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
            && mBluetoothGatt != null) {
        Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
        if (mBluetoothGatt.connect()) {
            return true;
        } else {
            return false;
        }
    }
    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);  得到bluetoothdevice
    if (device == null) {
        Log.w(TAG, "Device not found.  Unable to connect.");
        return false;
    }
    // We want to directly connect to the device, so we are setting the autoConnect
    // parameter to false.
    mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);//bluetoothdevice得到mBluetoothGatt
传进去的是 BluetoothGattCallback,从名字看就是回调。
    Log.d(TAG, "Trying to create a new connection.");
    mBluetoothDeviceAddress = address;
    return true;
}
3.读写BluetoothGattCharacteristic,使能notification
mBluetoothGatt.readCharacteristic(characteristic); 读
mBluetoothGatt.writeCharacteristic(characteristic);  写
 * Enables or disables notification on a give characteristic.
 *
 * @param characteristic Characteristic to act on.
 * @param enabled If true, enable notification.  False otherwise.
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
                                          boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
}
向下写数据:
//设置数据内容
                gattCharacteristic.setValue("send data->");
                //往蓝牙模块写入数据
                mBLE.writeCharacteristic(gattCharacteristic);
   读数据:
    if(gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA)){
                //测试读取当前Characteristic数据,会触发mOnDataAvailable.onCharacteristicRead()
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mBLE.readCharacteristic(gattCharacteristic);
                    }
                }, 500);
                //接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite()
                mBLE.setCharacteristicNotification(gattCharacteristic, true);
4.得到 supported services
 * Retrieves a list of supported GATT services on the connected device. This should be
 * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
 *
 * @return A {@code List} of supported services.
public List<BluetoothGattService> getSupportedGattServices() {
    if (mBluetoothGatt == null) return null;
    return mBluetoothGatt.getServices();
}
5.关系
BluetoothGatt 代表一个连接,里面包含一个或者多个 BluetoothGattService , 而每个BluetoothGattService 包含多个BluetoothGattCharacteristic , 一个 BluetoothGattCharacteristic 里面可能包含0个或者多个 BluetoothGattDescriptor
6.BLE 设备端LOG 
条件: 
主机端:BLEGATTLE 参考程序, 
设备端: NODIC官方BLE UART 程序
串口端LOG:
 main start trace
uart_init
..\main.c:leds_init:137> enter_now
..\main.c:timers_init:149> enter_now
..\main.c:ble_stack_init:457> enter_now
..\main.c:gap_params_init:166> enter_now
Start...
..\main.c:advertising_start:331> enter_now
..\main.c:ble_evt_dispatch:442> enter_now
p_ble_evt->header.evt_id:16// connect event
p_ble_evt->header.evt_id:16
p_ble_evt->header.evt_id:16
..\main.c:ble_evt_dispatch:442> enter_now
p_ble_evt->header.evt_id:20
p_ble_evt->header.evt_id:20// BLE_GAP_EVT_SEC_INFO_REQUEST
p_ble_evt->header.evt_id:20
..\main.c:ble_evt_dispatch:442> enter_now
p_ble_evt->header.evt_id:19
p_ble_evt->header.evt_id:19// BLE_GAP_EVT_SEC_PARAMS_REQUEST
p_ble_evt->header.evt_id:19
..\main.c:ble_evt_dispatch:442> enter_now
p_ble_evt->header.evt_id:80  // write event
p_ble_evt->header.evt_id:80
p_ble_evt->header.evt_id:80
..\main.c:ble_evt_dispatch:442> enter_now
p_ble_evt->header.evt_id:80 // write event
p_ble_evt->header.evt_id:80
..\main.c:nus_data_handler:224> enter_now
send data->                         // received data
p_ble_evt->header.evt_id:80     // write event
..\main.c:ble_evt_dispatch:442> enter_now
p_ble_evt->header.evt_id:24//   BLE_GAP_EVT_AUTH_STATUS
p_ble_evt->header.evt_id:24
p_ble_evt->header.evt_id:24
..\main.c:ble_evt_dispatch:442> enter_now
p_ble_evt->header.evt_id:23  //   BLE_GAP_EVT_AUTH_KEY_REQUEST
p_ble_evt->header.evt_id:23
p_ble_evt->header.evt_id:23
..\main.c:ble_evt_dispatch:442> enter_now
p_ble_evt->header.evt_id:18
..\main.c:on_conn_params_evt:279> enter_now
p_ble_evt->header.evt_id:18   //  BLE_GAP_EVT_CONN_PARAM_UPDATE
p_ble_evt->header.evt_id:18
7.BLE连接参数
#define MIN_CONN_INTERVALMSEC_TO_UNITS(500, UNIT_1_25_MS) /**< Minimum acceptable connection interval (0.5 seconds). */6~3200, 就是说7.5MS~ 4S, 1.25MS单位。连接时间间隔。
#define MAX_CONN_INTERVALMSEC_TO_UNITS(1000, UNIT_1_25_MS)/**< Maximum acceptable connection interval (1 second). */
#define SLAVE_LATENCY0 跳过连接事件/**< Slave latency. */
#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)  /**< Connection supervisory timeout (4 seconds). */  管理超时,如果超过此时间没有连接成功事件,则认为是连接丢失。
实验验证:
#define MIN_CONN_INTERVAL               16                                          /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */
#define MAX_CONN_INTERVAL               500                                          /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */
#define SLAVE_LATENCY                   0                                          /**< slave latency. */
#define CONN_SUP_TIMEOUT                2000                                         /**< Connection supervisory timeout (4 seconds), Supervision Timeout uses 10 ms units. */
#define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(20000, APP_TIMER_PRESCALER)  /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000, APP_TIMER_PRESCALER) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT    3
测试记录:
当APP刚连接上时,这之间用得连接参数是由手机端决定的,经过FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS 后,手机端会发新的参数过来,我测试过的,手机端都是MAX_CONN_INTERVAL 来决定,SLAVE_LATENCY 不管我改成多少,下发的都是0。
网络上讨论
- 对于IOS设备来说, 苹果设置了一系列规定, 不允许从设备的配置超出这些范围. 其他主设备来说目前还没有听说有什么具体范围设定. Android设备目前google也还没有明确规定. 所以换句话说, 只要符合主设备的要求, 从设备是可以在主设备规定的范围内请求主设备对connection interval进行改变的.
- 你通过GAP_SetParamValue()只是设置了参数, 最后是需要通过发送到主设备那里去请求修改的. 所以这里不正确.请参考 GAPRole_SetParameter( GAPROLE_PARAM_UPDATE_ENABLE, ..) 函数的做法.
- 你可以通过packet sniffer抓包, 在时间戳上很清楚能看到connection interval. 或者你也可以自己加点代码, 从程序里面获取, 或者以notify方式发给主设备, 从主设备看, 总之, 方法很多哈. 
 另外附上苹果对connection interval的要求, 其实还有其他的连接参数要求, 比如slave latency, supervision timeout, 如果不满足这些, IOS设备会拒绝.- The connection parameter request may be rejected if it does not comply with all of these rules: 
 Interval Max * (Slave Latency + 1) ≤ 2 seconds
 Interval Min ≥ 20 ms
 Interval Min + 20 ms ≤ Interval Max
 Slave Latency ≤ 4
 connSupervisionTimeout ≤ 6 seconds
 Interval Max * (Slave Latency + 1) * 3 < connSupervisionTimeout
8.从设备怎么主动断开连接
你可以直接调用 GAPRole_TerminateConnection() 来主动断开连接。 
那个函数调用后,连接顺利断开后会收到 GAP_LINK_TERMINATED_EVENT 事件。 
在这个事件之后,你再重新启动广播,即可
【转】BLE 学习记录的更多相关文章
- Quartz 学习记录1
		原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ... 
- Java 静态内部类与非静态内部类 学习记录.
		目的 为什么会有这篇文章呢,是因为我在学习各种框架的时候发现很多框架都用到了这些内部类的小技巧,虽然我平时写代码的时候基本不用,但是看别人代码的话至少要了解基本知识吧,另外到底内部类应该应用在哪些场合 ... 
- Apache Shiro 学习记录4
		今天看了教程的第三章...是关于授权的......和以前一样.....自己也研究了下....我觉得看那篇教程怎么说呢.....总体上是为数不多的精品教程了吧....但是有些地方确实是讲的太少了.... ... 
- UWP学习记录12-应用到应用的通信
		UWP学习记录12-应用到应用的通信 1.应用间通信 “共享”合约是用户可以在应用之间快速交换数据的一种方式. 例如,用户可能希望使用社交网络应用与其好友共享网页,或者将链接保存在笔记应用中以供日后参 ... 
- UWP学习记录11-设计和UI
		UWP学习记录11-设计和UI 1.输入和设备 通用 Windows 平台 (UWP) 中的用户交互组合了输入和输出源(例如鼠标.键盘.笔.触摸.触摸板.语音.Cortana.控制器.手势.注视等)以 ... 
- UWP学习记录10-设计和UI之控件和模式7
		UWP学习记录10-设计和UI之控件和模式7 1.导航控件 Hub,中心控件,利用它你可以将应用内容整理到不同但又相关的区域或类别中. 中心的各个区域可按首选顺序遍历,并且可用作更具体体验的起始点. ... 
- UWP学习记录9-设计和UI之控件和模式6
		UWP学习记录9-设计和UI之控件和模式6 1.图形和墨迹 InkCanvas是接收和显示墨迹笔划的控件,是新增的比较复杂的控件,这里先不深入. 而形状(Shape)则是可以显示的各种保留模式图形对象 ... 
- UWP学习记录8-设计和UI之控件和模式5
		UWP学习记录8-设计和UI之控件和模式5 1.日历.日期和时间控件 日期和时间控件提供了标准的本地化方法,可供用户在应用中查看并设置日期和时间值. 有四个日期和时间控件可供选择,选择的依据如下: 日 ... 
- UWP学习记录7-设计和UI之控件和模式4
		UWP学习记录7-设计和UI之控件和模式4 1.翻转视图 使用翻转视图浏览集合中的图像或其他项目(例如相册中的照片或产品详细信息页中的项目),一次显示一个项目. 对于触摸设备,轻扫某个项将在整个集合中 ... 
随机推荐
- 关于ADO.NET的一些知识整理
			ADO.NET是什么 虽然我们都知道ADO.NET是对数据库的操作,但是要真的说出ADO.NET的具体含义还不是很容易. ADO.NET是ActiveX Data Objects的缩写,它是一个COM ... 
- 原生JS添加节点方法与jQuery添加节点方法的比较及总结
			一.首先构建一个简单布局,来供下边讲解使用 1.HTML部分代码: <div id="div1">div1</div> <div id="d ... 
- linux 简单命令
			很久没有接触linux了,很多命令也忘记了,现在自己独立安装一个linux,独立安装LAMP,让自己记录下来这段. 怎么进入命令行 init 3, 回到桌面 init 5在不是root用户情况下,切换 ... 
- 关于WINDOWS命令
			1. Windows netstat 查看端口.进程占用 netstat -aon — 显示全部进程 2. 查看进程命令 tasklist — 显示全部进程 taskkill — 关闭至少一个系统进 ... 
- HTML - 键盘事件
			Keyboard 事件 onkeydown: 在用户按下按键时触发. onkeypress: 在用户敲击按钮时触发. onkeyup: 当用户释放按键时触发. 示例 <!DOCTYPE html ... 
- spring事务管理学习
			spring事务管理学习 spring的事务管理和mysql自己的事务之间的区别 参考很好介绍事务异常回滚的文章 MyBatis+Spring 事务管理 spring中的事务回滚例子 这篇文章讲解了@ ... 
- WCF 用netTcpbinding,basicHttpBinding 传输大文件
			问题:WCF如何传输大文件 方案:主要有几种绑定方式netTcpbinding,basicHttpBinding,wsHttpbinding,设置相关的传输max消息选项,服务端和客户端都要设置,tr ... 
- mysql - 编码
			show variables like 'character%'; 通过以上命令可以查询编码情况, 不过,在安装的时候,建议选择‘gbk’这类中文编码, 如果选择的是utf8,则在处理的过程中需要进行 ... 
- memcached和mongodb 在windows下安装
			要在新机器上安装memcached和mongodb服务,折腾了一天,终于把这两个服务在windows下跑起来了. memcached主要参考http://www.rootop.org/pages/27 ... 
- Angularjs总结(六) 上传附件
			所用插件:angular-file-upload 这个插件用到的几个指令:nv-file-select(点击选择).uploader(用于绑定控制器中新建的uploader对象) HTML: < ... 
