重要概念点总结

1.profile 规范。包含有service服务,如电量。
2.service,每一个服务可能包含一个或多个特征值。
3.characteristic 特征值。通信载体,电量为20%,20%即是特征值的value。主从机之间通信,通过读写特征值实现。
4.UUID 统一识别码。刚才提到的service和characteristic,都需要一个唯一的uuid来标识。

连接与通信过程中,用到协议栈这两层
GAP 主要进行建立连接的过程,类似握手。
GATT 传输的数据段通用规范。这些很短的数据段被称为属性(Attribute),主要包括service服务、characteristic 特征值,特征值描述characteristic descriptors。

通信过程:由中心设备( 客户端client,主站)发起,外围设备(服务器server,从站)响应。一般情况下,客户端进行读写特征值,实现通信。服务器还可以通过发“通知”(Notification)或“指示”(indication)进行通信。
Notification 服务器发出数据,无需客户端回复。
indication 服务器发出数据,需要客户端回复后再继续发。

代码分析

Main Task 是主任务,初始化。
App_Thread 和Main Task共用堆栈。处理协议栈发送的所有事件和消息。
App_Thread 主要通过App_HandleHostMessageInput处理协议栈发送给APP的队列。通过回调机制处理队列中的消息。

开启调用流程:
App_Thread->App_HandleHostMessageInput->BleApp_GenericCallback->BleApp_Config->BleApp_Start->App_StartScanning or BleApp_Advertise。

以下以主站代码进行分析:
一. 协议栈开启前注册回调函数

    App_RegisterGattServerCallback(BleApp_GattServerCallback);App_RegisterGattClientProcedureCallback(BleApp_GattClientCallback);GattServer_RegisterHandlesForWriteNotifications(NumberOfElements((mCharMonitoredHandles)), mCharMonitoredHandles);BleServDisc_RegisterCallback(BleApp_ServiceDiscoveryCallback);App_RegisterGattClientNotificationCallback(BleApp_GattNotificationCallback);//App_RegisterGattClientIndicationCallback(BleApp_GattNotificationCallback);

作为主站,重点关注
1.BleApp_GattClientCallback
2.BleApp_ServiceDiscoveryCallback
这俩个回调,前者是客户端通用回调,客户端的所有操作的响应都会在这个回调中触发,在如客户端发现服务,读写特征值,读写特征值描述等等。
后者是服务发现回调,在发现服务后,可对服务里的特征值句柄进行保存。

二. 开启扫描函数:

App_StartScanning(&gScanParams, BleApp_ScanningCallback, FALSE);

开启扫描或广播后,在对应回调函数中进行判断并建立连接。

static void BleApp_ScanningCallback (gapScanningEvent_t* pScanningEvent)
{bleResult_t result = gBleStatusBase_c;switch (pScanningEvent->eventType){case gDeviceScanned_c:{if (BleApp_CheckScanEvent(&pScanningEvent->eventData.scannedDevice)){        gConnReqParams.peerAddressType = pScanningEvent->eventData.scannedDevice.addressType;FLib_MemCpy(gConnReqParams.peerAddress, pScanningEvent->eventData.scannedDevice.aAddress,sizeof(bleDeviceAddress_t));Gap_StopScanning();result = App_Connect(&gConnReqParams, BleApp_ConnectionCallback);}}        break;case gScanStateChanged_c:{mScanningOn = !mScanningOn;/* Node starts scanning */if (mScanningOn){/* Start advertising timer */TMR_StartLowPowerTimer(mAppTimerId, gTmrLowPowerSecondTimer_c,TmrSeconds(gScanningTime_c),ScaningTimerCallback, NULL);  Led1Flashing();}/* Node is not scanning */else{                TMR_StopTimer(mAppTimerId);   }}break;case gScanCommandFailed_c:{panic(0, 0, 0, 0);break;}default:break;}
}

扫描回调主要有两种事件:
1.已经扫描到设备(gDeviceScanned_c )
2.扫描状态改变(gScanStateChanged_c),①从一开始的不扫描到开始扫描,②从扫描到停止扫描。
注意这里开启扫描后开了一个定时器,时间到后调用回调ScaningTimerCallback,这里面会关闭扫描,这样就实现了扫描超时机制。但调试时发现并不是一开启扫描就可以扫到对应设备,所以可以将关闭扫描的代码干掉,让主机一直扫

三 . 扫描设备校验函数:

BleApp_CheckScanEvent(&pScanningEvent->eventData.scannedDevice)

在这个函数中校验需要连接的从站MAC地址,返回TURE,即可开启连接。

四. 连接回调

BleApp_ConnectionCallback

连接回调函数中调用开启状态机

BleApp_StateMachineHandler(peerDeviceId, mAppEvt_PeerConnected_c);

五. 状态机

BleApp_StateMachineHandler

mAppExchangeMtu_c状态下发现服务,可以通过UUID发现特定服务,也可以直接发现所有Primary服务。

BleServDisc_Start(peerDeviceId);      //封装后的发现所有服务函数
BleServDisc_FindService(peerDeviceId, gBleUuidType128_c,(bleUuid_t*) &uuid_service_wireless_uart)//封装后的通过UUID发现特定服务

六 .发现服务回调
发现服务后其实先回调了BleApp_GattClientCallback,在BleServDisc_SignalGattClientEvent中发现服务的所有特征值。这步无需修改。
需要关注的是BleApp_ServiceDiscoveryCallback,在这个回调中校验发现服务的UUID,然后校验特征值的UUID,保存服务需要的特征值句柄。
eg.保存一个16bitUUID的gBleSig_BatteryLevel_d特征值句柄

static void BleApp_StoreServiceHandles (deviceId_t peerDeviceId, gattService_t *pService)
{maPeerInformation[peerDeviceId].clientInfo.hService = pService->startHandle;for (uint8_t i = 0; i < pService->cNumCharacteristics; i++){if ((pService->aCharacteristics[i].value.uuidType == gBleUuidType16_c) &&(pService->aCharacteristics[i].value.uuid.uuid16 == gBleSig_BatteryLevel_d))//校验服务中的特征值UUID{maPeerInformation[peerDeviceId].clientInfo.hUartStream = pService->aCharacteristics[i].value.handle;  //保存特征值句柄}}
}

有了句柄之后 可以在状态机中对特征值进行读写操作。完成通信。✌

主站开启通知的流程

在和一个从站通信过程中,从站在不停发送通知,但是主站无法触发通知接收的回调

App_RegisterGattClientNotificationCallback(BleApp_GattNotificationCallback);

这时主站需要使能通知接收,就是修改特征值描述的CCCD描述。使能流程:
1.确定从站发送通知的服务的特征值,读取对应特征值句柄。
2.通过特征值句柄发现特征值描述

myChar. aDescriptors = aDescriptors;
myChar.value.handle = maPeerInformation[peerDeviceId].clientInfo.hUartStream;bleResult_t result = GattClient_DiscoverAllCharacteristicDescriptors(peerDeviceId,&myChar,0xFFFF,mcMaxDescriptors_c);

3.在客户端回调函数中,在发现的特征值描述中找到CCCD,将使能通知的FLAG写入特征值描述

  case gGattProcDiscoverAllCharacteristicDescriptors_c:if (gGattProcSuccess_c == procedureResult){/* Find CCCD */for ( uint8_t j = 0; j < myChar. cNumDescriptors ; j++){if (gBleUuidType16_c == myChar. aDescriptors [j]. uuidType&& gBleSig_CCCD_d == myChar. aDescriptors [j]. uuid . uuid16 ){uint16_t value = gCccdNotification_c;//packTwoByteValue(gCccdNotification_c, cccdValue); //官方给的伪代码有问题bleResult_t result = GattClient_WriteCharacteristicDescriptor(serverDeviceId,&myChar. aDescriptors [j],sizeof(value),(void*)&value);if (gBleSuccess_c != result){/* Handle error */}break;}}}

4.继续在客户端回调中,读取特征值写入结果

  case gGattProcWriteCharacteristicDescriptor_c:if (gGattProcSuccess_c == procedureResult){int text = 1; //写入成功/* Notification successfully activated */}else{/* Handle error */}

至此对应特征值的通知接收已经被使能,可通过通知接收回调函数接收通知数据。

扩展:

1.建立连接后最重要的一个步骤是交互MTU,主从站都可以发起。GattClient_ExchangeMtu()发起交互,在client_callback中回调。若从站发起,主站会在server_callback()中回调。

基于NXP的蓝牙BLE协议栈代码分析相关推荐

  1. 【转】TI蓝牙BLE 协议栈代码学习

    BLE就是低功率蓝牙.要着重了解两种设备: dual-mode双模设备:简单说就是向下兼容. single-mode单模设备:仅仅支持BLE. 关于开发主要讲的是单模设备,它可以只靠纽扣电池即可持续工 ...

  2. java 协议栈_深入浅出讲解低功耗蓝牙(BLE)协议栈

    详解BLE连接建立过程 https://www.cnblogs.com/iini/p/8972635.html 详解BLE 空中包格式-兼BLE Link layer协议解析 https://www. ...

  3. 蓝牙:深入浅出低功耗蓝牙(BLE)协议栈

    深入浅出低功耗蓝牙(BLE)协议栈 BLE协议栈为什么要分层?怎么理解BLE"连接"?如果BLE协议只有ATT层没有GATT层会发生什么? 协议栈框架 一般而言,我们把某个协议的实 ...

  4. 深入浅出低功耗蓝牙(BLE)协议栈

    深入浅出低功耗蓝牙(BLE)协议栈 BLE协议栈为什么要分层?怎么理解BLE"连接"?如果BLE协议只有ATT层没有GATT层会发生什么? 协议栈框架 一般而言,我们把某个协议的实 ...

  5. 基于windows PE文件的恶意代码分析;使用SystemInternal工具与内核调试器研究windows用户空间与内核空间...

    基于windows PE文件的恶意代码分析:使用SystemInternal工具与内核调试器研究windows用户空间与内核空间 ******************** 既然本篇的主角是PE文件,那 ...

  6. 深入浅出低功耗蓝牙(BLE)协议栈,使用Ubertooth one扫描嗅探低功耗蓝牙

    BLE协议栈为什么要分层?怎么理解BLE"连接"?如果BLE协议只有ATT层没有GATT层会发生什么? 深入浅出低功耗蓝牙BLE协议栈 1. 协议栈框架 2. 如何通过无线发送一个 ...

  7. 蓝牙BLE(协议栈、OSAL、蓝牙APP工具)

    目录 蓝牙配对和绑定 蓝牙4.0 BLE 信道(RF Channel) BLE协议栈分层 PHY层(Physical layer 物理层) LL层(Link Layer 链路层) HCI层(Host ...

  8. 从零开始的nrf52832蓝牙开发--蓝牙BLE主函数分析

    关注.星标公众号,直达精彩内容 来源:https://blog.csdn.net/solar_Lan/article/details/88964043 工程目录中大部分文件都是官方 SDK 库中提供好 ...

  9. Hadoop基于Protocol Buffer的RPC实现代码分析-Server端--转载

    原文地址:http://yanbohappy.sinaapp.com/?p=110 最新版本的Hadoop代码中已经默认了Protocol buffer(以下简称PB,http://code.goog ...

最新文章

  1. 信号完整性 带宽的确定
  2. PCM设备能在公网使用吗?
  3. MongoDB 计划从“Data Sprawl”中逃脱
  4. 小学生计算机辅助教学系统--练习加,减,乘,除法
  5. web视图引擎框架对比
  6. r roc曲线 语言_R语言系列6:生存分析中多重时间依赖性ROC曲线绘制 timeROC
  7. 找不到服务器micro,Go Micro服务发现
  8. python如何打开npy文件_操作python实现npy格式文件转换为txt文件
  9. GTK+ 3.5.18 发布,GUI 开发工具包
  10. 被裁员 60%,以太坊大神忍不住要洗白了
  11. linux查看java jdk安装路径
  12. 自定义ZXing二维码扫描界面并解决取景框拉伸等问题
  13. 【sklearn第四讲】数据集变换
  14. Java程序连接数据库
  15. 计算机硬件硬盘分区,电脑硬盘分几个区最好?电脑硬盘分区教程
  16. 创业公社:亦庄分中心开业 借好创业东风
  17. 亿级视频内容如何实时更新?
  18. python迅雷下载任务出错_迅雷下载“任务出错”最新解决方法
  19. Firefox设置中文
  20. 阿里服务器微信发不了图片,为什么微信发不了图片?这四招教你解决难题

热门文章

  1. oracle export utf-8,Linux操作系统下终端乱码的终极解决方案 export LANG=zh_CN.UTF-8 export LANG=en_US...
  2. Verilog设计中如何匹配变量的位宽?($clog2系统函数)
  3. LS-DYNA摆锤重力仿真分析
  4. pdf文件怎么压缩大小?压缩pdf大小的方法
  5. 【Java】基础02
  6. 软件采购必备:软件供应商评估表 之七(共七),结束!
  7. 【linux命令详解】scp远程传输文件命令
  8. Python作业题——“汽车迷”
  9. python--利用有道网址编写一个翻译句子的程序
  10. [卷积系列] P3338 [ZJOI2014]力