参考:芯海厂家《CST92F25 SDK开发指南V1.2.pdf》 蓝牙BLE 5.0
参考:「物联网」- 蓝牙4.0 BLE开发入门到精通(视频使用CC2540蓝牙BLE芯片、介绍IAR编程环境安装及使用)
地址: https://www.bilibili.com/video/BV1qZ4y1W7dz?p=3&spm_id_from=pageDriver
配套书籍:《蓝牙4.0BLE开发完全手册》 物联网开发技术实战 280页 31.5M 高清书签版
参考:BLE协议栈入门一(基本概念)
参考:浅谈BLE协议栈
参考:BLE协议栈详解
BLE全称Bluetooth Low Energy,低功耗蓝牙,是一种无线传输小数据的超低功耗蓝牙技术。蓝牙设备总共分为三种:Bluetooth、Bluetooth smart、Bluetooth smart ready。
这三种设备的区别和联系如下图(箭头表示可以连接):
既然是无线芯片呢,其组成由软件和硬件构成。
BLE 的协议规范,全部需要遵循 Core Spec 来进行定制,Core Spec 中包含了 RF 、Modem、Air Interface,数据编码/解码,以及软件的协议规范
摘自:BLE(2)——基本特性(状态、角色、地址、信道)
蓝牙 BLE 工作在 2.4GHz 的频段上,分为 40 个 RF 信道,每个信道 2M。同一时刻,只能用一个信道进行数据的传输/接收。
这 40 个 RF Channel 上,并不是平等的, SIG 把物理信道转换成为一个叫做 Channel Index 的玩意(其实就是简单的转换关系):
物理信道从 0 - 39 进行编号。Index :PHY Ch0 对应 37,PHY Ch12 对应 38,PHY 39 对应 39。
在 BLE 4.2 时代,Advertising、Scanning、Initiating 状态只允许在 Ch Index 的 37/38/39 上执行数据的收发。Advertising 是为了让其他 BLE 设备发现本设备,Scanning 是为了扫描到其他设备。为了让发现设备和广播更容易不受诸如 WIFI 的干扰,专门将 Advertising、Scanning、Initiating 数据收发放到了 与 WIFI 频段隔离的部分,起到一定抗干扰作用:
除了 37、38、39 这些频段,在 Connection 状态下使用了 其他的 37 个 Channel,通过跳频技术(Hopping),来减少数据干扰,增强系统的可靠性。
下图是协议栈的结构分层:
蓝牙协议分host和controller两个部分,Host是正真意义的蓝牙协议,Controller为蓝牙底层,或者说是基带芯片。
Profiles(配置文件)和应用总是基于GAP和GATT 之上。
在单芯片方案中,Controller 和HOST、Profiles、应用层都在同一个芯片上。
视频中介绍的CC2540蓝牙BLE SOC芯片可单芯片实现协议栈结构的所有组件,开发者可以直接搭建自己的应用程序。
PHY层用来指定BLE所用的无线频段(2.4G),调制解调方式和方法、跳频等。PHY层做得好不好,直接决定整个BLE芯片的功耗,灵敏度以及selectivity等射频指标。
链路层定义了协议栈中最为基础的状态机、数据包格式、广播和连接流程等问题
LL层是整个BLE协议栈的核心,也是BLE协议栈的难点和重点。像Nordic的BLE协议栈能同时支持20个link(连接),就是LL层的功劳。LL层要做的事情非常多,比如具体选择哪个射频通道进行通信,怎么识别空中数据包,具体在哪个时间点把数据包发送出去,怎么保证数据的完整性,ACK如何接收,如何进行重传,以及如何对链路进行管理和控制等等。LL层只负责把数据发送出去或者接收回来,对数据进行怎样的解析则交给上面的GAP或者GATT。
LL层的五种RF状态:
- Standby 待机状态
- Advertising 广播状态
- Scaning 扫描状态
- Initiating 发起连接状态
- Connected 连接状态
(1)Advertising、Scaning
广播状态下还没有任何连接,广播者只是单纯地往外广播数据。
扫描状态下,扫描者仅仅接收广播者广播的数据。(2)Initiating 、Connected
发起连接状态是发起者发起的,发起者是通过发出request连接请求来响应广播者的一个设备,如果广播者接受了这个request则进行连接,进入Connected连接状态。进入连接状态之后,发起连接的设备成为主设备,接受连接的设备成为从设备。
HCI 层通信层,向 host 和 controller 提供一个标准化的接口。该层可以由软件api 实现或者使用硬件接口 uart、spi、usb 来控制。
HCI是可选的(具体请参考文章: 三种蓝牙架构实现方案(蓝牙协议栈方 案)),HCI主要用于2颗芯片实现BLE协议栈的场合,用来规范两者之间的通信协议和通信命令等。
HCI 实现host 与controller 间命令与事件的传输。
L2CAP 为上层提供数据封装服务。层相当于快递,将数据打包,可以让客户点对点的通信(允许点对多点)。
L2CAP对LL进行了一次简单封装,LL只关心传输的数据本身,L2CAP就要区分是加密通道还是普通通道,同时还要对连接间隔进行管理。
L2CAP 是BLE 蓝牙的复用层,其支持数据的分割和重组,使得较大的报文可以在底层无线电中进行传输。L2CAP 决定了MTU size(Maximum Transmission Unit:最大传输单元),目前芯片支持的MTU_SIZE 为23~247 Bytes。
SM 层安全服务层,提供配对和密钥的分发,实现安全连接和数据交换。
SMP用来管理BLE连接的加密和安全的,如何保证连接的安全性,同时不影响用户的体验,这些都是SMP要考虑的工作。
GAP是对LL层payload(有效数据包)如何进行解析的两种方式中的一种,而且是最简单的那一种。
GAP简单的对LL payload进行一些规范和定义,因此GAP能实现的功能极其有限。GAP目前主要用来进行广播,扫描和发起连接等,控制LL层的五种RF状态切换。
BLE协议栈GAP层实现了蓝牙连接功能,其定义了设备的访问模式与流程,包括:设备发现,建立连接,断开连接,初始化安全特性,设备配置等。
GAP 层的蓝牙状态切换图如图5.3所示,每个状态描述如下:
BLE 连接参数包含如下:
1)连接时间间隔:
在蓝牙通信过程中,所有的数据传输均发生在连接事件(connection interval)期间。连接事件周期性的发生,两个连接事件间的间隔时间即为连接间隔。每个连接事件内可传输多个包,该芯片支持的最大传输包数量为8 个。
连接间隔示意图如图5.4所示。连接间隔单位时间为1.25ms,时间范围为7.5ms~4.0s 之间。
2)从机潜伏:
当Slave 没有数据发送时,允许Slave 跳过连接事件。从机潜伏值(Latency),是允许从设备跳过的最大连接次数。在连接事件中,如果Slave 没有对Master 的包做出回应,Master 将会在后来的连接事件中重复发送,直到Slave 回应。图5.5 为Latency=0 与Latency=3 的对比示意图。
3)监督超时:
在每个连接事件中,主机发送信号,从机进行回应。当长时间未收到对方信号包时,蓝牙将断开。超时断开的时间即为监督超时时间。监督超时时间单位为10ms,范围为100ms~32s 之间。
连接参数由主机进行维护与更新,从机无法更改连接参数,但是从机可以请求主机进行连接参数更新。从机将连接参数期望值发送给主机,主机根据自身情况,选择一个合适的连接参数更新或者拒绝更新。
当设备处于广播状态时,将发送广播信号。广播信号发生于广播事件中,相邻两个广播事件的间隔即为广播间隔。在每次广播事件中,广播包会分别在3 个广播通道(37、38、39 信道)中被发送一次。
广播事件如图5.6 所示。广播间隔单位时间为0.625ms,范围为20ms~10.24s。
GAP Role 为一个单独的任务,其主要实现GAP 参数的配置,例如广播内容、连接参数更新参数等。
GAP 层的角色包含如下:
2)设置参数
GAPBondMgr 用于蓝牙连接时安全特性的初始化。蓝牙安全特性定义如表5.5 所示。
类型 | 描述 |
---|---|
Pairing | 配对,进行密钥交换的过程 |
Encryption | 加密,配对完成后或重新建立连接后,数据将加密 |
Authentication | 认证,在配对过程中采用了MITM 保护(如Passcode、NFC 等) |
Bonding | 绑定,存储加密密钥至flash 存储器以便下次连接使用 |
Authorization | 授权,除了认证外的一种应用层密钥交换方式 |
OOB | 一种MITM 认证方式,密钥交换不通过空中传输,如通过NFC、串口等传输 |
MITM | Man in the Middle Protection. 在配对过程中,通过该方式实现了认证过程,防止通信被监听 |
Just Works | 在配对过程中,不需要MITM,实现了密钥传输 |
在通常的带有安全特性的连接中,首次连接步骤如下:
1)Pairing:配对,采用Just Works 或者MITM(如passcode)交换秘钥
2)Encryption:使用步骤1)的秘钥加密链路
3)Bonding:保存加密秘钥至Flash
当需要重新建立连接时,可使用首次连接过程中Bonding 中的加密秘钥,对连接链路进行加密。如
果取消Bonding 步骤,那么每次连接均需要进行重新配对。
配对绑定配置例程如程序清单5.5 所示。
uint32 passkey = DEFAULT_PASSCODE;
uint8 pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
uint8 mitm = TRUE; //开启MITM 认证加密
uint8 ioCap = GAPBOND_IO_CAP_NO_INPUT_NO_OUTPUT;
uint8 bonding = TRUE; //开启bonding
GAPBondMgr_SetParameter( GAPBOND_DEFAULT_PASSCODE, sizeof ( uint32 ), &passkey );
GAPBondMgr_SetParameter( GAPBOND_PAIRING_MODE, sizeof ( uint8 ), &pairMode );
GAPBondMgr_SetParameter( GAPBOND_MITM_PROTECTION, sizeof ( uint8 ), &mitm );
GAPBondMgr_SetParameter( GAPBOND_IO_CAPABILITIES, sizeof ( uint8 ), &ioCap );
GAPBondMgr_SetParameter( GAPBOND_BONDING_ENABLED, sizeof ( uint8 ), &bonding );
简单来说,ATT层用来定义用户命令及命令操作的数据,比如读取某个数据或者写某个数据。BLE协议栈中,开发者接触最多的就是ATT。
ATT 层 ATT 环境中,允许设备向另外一个设备展示一块特定的数据,称之为“属性” ,展示“属性”的设备称为服务器,与之配对的设备称为客户端。链路层LL状态(主机和从机)与设备的 ATT 角色是相互独立的,也就是说,主机设备可以是 ATT 服务器,也可以是 ATT 客户端,从机也一样。
BLE引入了attribute概念,用来描述一条一条的数据。Attribute除了定义数据,同时定义该数据可以使用的ATT命令,因此这一层被称为ATT层。
GATT 层从名字就能看出,GATT 是在 ATT 上面的一层结构,定义了使用 ATT的服务框架,
GATT用来规范attribute中的数据内容,并运用group(分组)的概念对attribute进行分类管理。没有GATT,BLE协议栈也能跑,但互联互通就会出问题,也正是因为有了GATT和各种各样的应用profile,BLE摆脱了ZigBee等无线协议的兼容性困境,成了出货量最大的2.4G无线通信产品。
BLE 协议栈GATT 层用于实现两个设备间的应用层数据通信。GATT 层采用Client/Server 架构,客户端(Client)通过访问服务器端(Server)的服务(Service)与特征(Characteristic),实现数据交互。
GATT 层架构如图5.7 所示。
关于服务与特征的描述如下:
1)用户的应用由一个或多个服务Service 组成的
2)每个服务Service 包含一个或多个特征(Characteristic)
3)每个特征(Characteristic)包含一个或多个属性(Attribute),Attribute属性是设备之间传输的信息的基本单元,GATT将这些基本单元组织成一块一块的数据,称为Characteristic。属性分别如下:
下图是GATT某个服务的属性表(TI为例):
上表中,每一行就是一个属性,第1行表示一个服务(比如温湿度服务)的开始,第2, 3, 4行是该服务的第一个Characteristic,包含了UUID、Declaration、Char Value、Description。第5, 6, 7行是该服务的第二个Characteristic。。。特别的是第13行,作用是配置客户端特征值,用于server主动发送数据给client。
蓝牙的应用开发入门就是设定特征值的属性表,然后调用API去接收发送属性。
由两个部分组成,处于协议栈的最顶层,将协议栈和应用层紧密的连接到一起,开发者对协议栈本身底层原理不用深入理解,就可以进行使用开发。
下面这两个是芯海蓝牙模块开发指南的说明
Lib 为蓝牙库文件,大部分蓝牙协议栈在MASK ROM 代码中实现,少部分蓝牙协议栈代码在Lib 库文件中。
为了实现更好的移植性,协议栈将硬件层抽象出了一个HAL 硬件抽象层,当新的硬件平台做好后,只需修改HAL,而不需修改HAL 之上的协议栈的其他组件和应用程序。
教程使用IAR,添加BLE蓝牙协议栈文件,类似操作系统的OSAL
相关博文:
OSAL
STM32 OSAL操作系统抽象层的移植
OSAL 为Operating System Abstraction Layer,即操作系统抽象层,支持多任务运行,其中BLE 协议栈、配置文件以及所有的应用程序(app)都在其上运行。它并不是一个传统意义上的操作系统,而是一个允许软件建立和执行事件的循环。OSAL 基于消息驱动,是一个简易的任务轮询系统。OSAL 与RTOS 相比,缺少了任务堆栈,系统延时,中断管理,进程间通信,保存上下文的任务调度。
软件功能是由任务事件来实现的,创建一个任务事件需要以下工作:
1)创建任务ID(task identifier);
2)编写任务初始化(task initialization routine)进程,并需要添加到OSAL 初始化进程中,这就是说系统启动后不能动态添加任务;
3)编写任务处理程序;
4)如有需要提供消息服务。
BLE 协议栈的各层都是以OSAL 任务方式实现,由于LL 控制任务的时间要求最为迫切,所以其任务优先级最高。为了实现任务管理,OSAL 通过消息处理(message process),存储管理,计时器定时等附加服务实现。
OSAL 任务事件处理回调函数数组示例如程序清单5.1所示。任务事件处理函数xx_ProcessEvent 顺序需与后续的osalInitTasks 函数内的Task 初始化顺序一致。
///< The order in this table must be identical to the task initialization calls below in osalInitTask.
const pTaskEventHandlerFn tasksArr[] =
{
LL_ProcessEvent, // task 0 ID值越小,优先级越高
HCI_ProcessEvent, // task 1
#if defined ( OSAL_CBTIMER_NUM_TASKS )
OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ), // task 2
#endif
L2CAP_ProcessEvent, // task 3
SM_ProcessEvent, // task 4
GAP_ProcessEvent, // task 5
GATT_ProcessEvent, // task 6
GAPRole_ProcessEvent, // task 7
#if (DEFAULT_GAPBOND_MGR_ENABLE==1)
GAPBondMgr_ProcessEvent, // task 8
#endif
GATTServApp_ProcessEvent, // task 9
SimpleBLEPeripheral_ProcessEvent, // task 10
};
为了使用OSAL,在main 函数的最后要启动一个名叫osal_start_system 的进程,该进程会调用由特定应用决定的启动函数osalInitTasks 来启动系统。osalInitTasks 函数示例如程序清单5.2所示。
/**
* @fn void osalInitTasks(void)
* @brief This function invokes the initialization function for each task.
* @param none
* @return none
*/
void osalInitTasks( void )
{
uint8 taskID = 0; //tasksEvents 前面任务首地址 循环查询新的事件不断发生
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
/* LL Task */ //ID越小,优先级越高
LL_Init( taskID++ );//LL控制任务的时间要求最为迫切,任务优先级最高
/* HCI Task */
HCI_Init( taskID++ );
#if defined ( OSAL_CBTIMER_NUM_TASKS )
/* Callback Timer Tasks */
osal_CbTimerInit( taskID );
taskID += OSAL_CBTIMER_NUM_TASKS;
#endif
/* L2CAP Task */
L2CAP_Init( taskID++ );
/* SM Task */
SM_Init( taskID++ );
/* GAP Task */
GAP_Init( taskID++ );
/* GATT Task */
GATT_Init( taskID++ );
/* Profiles */
GAPRole_Init( taskID++ );
#if(DEFAULT_GAPBOND_MGR_ENABLE==1)
GAPBondMgr_Init( taskID++ );
#endif
GATTServApp_Init( taskID++ );
/* Application */
SimpleBLEPeripheral_Init( taskID );//最后的一个初始化任务
}
osalInitTasks 函数逐个调用BLE 协议栈各层的启动进程来初始化协议栈,并设置一个任务的8bit 任务ID(task ID)。osalInitTasks 函数执行完成后,将跳入循环等待执行任务,系统启动完成。
使用OSAL 时,注意事项如下:
1)任务优先级决定于任务ID,任务ID 越小,优先级越高
2)BLE 协议栈各层的任务优先级比应用程序的高。
OSAL 完成TASK 初始化后,将调用osal_start_system 函数开始运行系统。系统是以一个死循环的形式工作的,其函数实现如程序清单5.3 所示,该代码在MaskROM 中运行。
void osal_start_system( void )
{
for(;;) // Forever Loop
{
osal_run_system();
}
}
OSAL 任务事件采用16 位变量定义,每1 位表示1 个唯一的事件标识,因此每个任务最多可定义16个不同类型事件。OSAL 事件处理流程图如图5.1所示。
osal_run_system 函数(在osal_start_system 函数的死循环内调用)流程如下:
当事件处理程序处理完相应事件后,需要清除相应标识。如未清除相应标识,OSAL 会认为事件还没有处理完成,后续会继续调用相应处理函数进行处理。
如在simpleBLEPeripheral(从机) 例程中,当事件START_DEVICE_EVT 发生,其处理函数SimpleBLEPeripheral_ProcessEvent 就运行,结束后返回16bit 事件变量,并清除事件标识SBP_START_DEVICE_EVT。程序代码如程序清单5.4 所示。
if ( events & SBP_START_DEVICE_EVT )
{
……
return ( events ^ SBP_START_DEVICE_EVT );
}
OSAL 事件产生(添加)方式主要有如下几种:
uint8 osal_set_event( uint8 task_id, uint16 event_flag )
调用该函数后将产生立即事件。
uint8 osal_start_timerEx( uint8 task_id, uint16 event_id, uint32 timeout_value )
调用该函数将设置一个软件定时器,超时后将产生对应事件。超时时间timeout_value 单位为ms。
uint8 osal_start_reload_timer( uint8 taskID, uint16 event_id, uint32 timeout_value );
调用该函数将设置一个自动加载的软件定时器,超时后将产生对应事件,并自动重新加载定时器。
超时时间timeout_value 单位为ms。
SYS_EVENT_MSG(0x8000)是OSAL 保留的消息处理事件,所有任务均包含该事件。该事件用于消息的传递,即将参数从一个任务传递给另一个任务,详情请参考“OSAL 消息”章节。
OSAL 提供了基本的内存管理函数。内存管理函数定义如下:
1)内存分配
void *osal_mem_alloc( uint16 size )
调用该函数将分配指定size 的内存空间,并返回内存首地址。当分配失败时,将返回NULL。
2)内存释放
void osal_mem_free( void *ptr )
调用该函数将释放之前分配的内存空间。
OSAL 消息机制实现了不同子系统之间的通信。消息即为数据,数据种类和长度都不限定。消息管理函数定义如下:
uint8 * osal_msg_allocate(uint16 len );
发送消息前,需要调用该函数创建消息占用的内存空间(内部已经包含了osal_mem_alloc 函数功
能)。需要为该函数指定空间大小,该函数返回内存空间地址指针。
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
将待发送的消息内容拷贝至msg_ptr 内存空间后,调用该函数向指定任务发送消息。调用该函数将产
生SYS_EVENT_MSG 事件并告知对应Task。
uint8 *osal_msg_receive( uint8 task_id )
目标Task 判断有SYS_EVENT_MSG 事件后,将调用该函数从指定任务接收消息数据。
uint8 osal_msg_deallocate( uint8 *msg_ptr );
消息处理完成后,需要调用该函数来释放内存空间(内部已经包含了osal_mem_free 函数功能)。
OSAL API
参考:蓝牙4.0BLE协议栈学习笔记(二)
地址:https://blog.csdn.net/u012246376/article/details/47700477?spm=1001.2014.3001.5502
在学习开发蓝牙协议栈遇到的问题总结:
1.蓝牙设备号BD_ADDR就是MAC地址,不同于uuid,uuid是服务号,作为唯一标识符。
2.scanRspData数组是扫描回应数据数组,用户可以自定义设备名。advertData数组是广播数据数组,主要是包含在广播里的数据信息。
3.主从机通信:
主从机通信具体流程就是 Scanning (搜索) -->Devices Found(发现从机) --> Connected (连接) --> discoverService(发现设备服务)–>读写characteristic(属性)。
协议栈中的SimpleBLEPeripheral是从机模式,主要是广播信息,让其他设备知道。
SimpleBLECentral是主机模式,主要是与从机建立连接。
读写characteristic可以理解为GATT层的client向service发送数据,或者是service向client端发送数据。
主机设备可以是client(客户端),也可以是service(服务器),即主机向从机发送数据,从机主动向主机发送数据。
主机向从机读写数据调用GATT_WriteCharValue函数和GATT_ReadCharValue函数。如下:
if ( simpleBLEDoWrite )
{
// Do a write
attWriteReq_t req;
req.handle = simpleBLECharHdl+2;
req.len = 2;
req.value[0] = simpleBLECharVal;
req.sig = 0;
req.cmd = 0;
status = GATT_WriteCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
if ( status == SUCCESS )
{
NPI_WriteTransport(“write ok\r\n”, 10);
simpleBLEProcedureInProgress = TRUE;
simpleBLEDoWrite = !simpleBLEDoWrite;
}
else
{
// Do a read
attReadReq_t req;
req.handle = simpleBLECharHdl+2;
status = GATT_ReadCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
}
从机和主机发送数据机制不一样。主机用write命令,从机用Notification通知命令。从机向主机发送数据调用GATT_Notification函数,如:
static attHandleValueNoti_t Report ;
uint16 GetHandle;
noti.len = 1;
noti.value[0] = GetLen;
GATT_Notification(GetHandle, &Report, FALSE );
4.获取电池电量:
battMeasure函数是通过ADC采集内部电压获得的电压值,参考电压是1.25v,最大测量电压是3.75V。如果要获取精度较高的值需要从外部输入引脚接入稳定性较高的参考电压,然后通过ADC采集转换。
if ( events &SBP_ADV_RGB_EVT )
{
//P0_3=~P0_3;
advertData[6]= battMeasure();//获取电池电量
GAPRole_SetParameter( GAPROLE_ADVERT_DATA, sizeof( advertData ), advertData );
osal_start_timerEx(simpleBLEBroadcaster_TaskID,SBP_ADV_RGB_EVT,1000);
return ( events ^ SBP_ADV_RGB_EVT );
}
5.获取RSSI值:
通过获取信号强度RSSI值,可以测定信号源与接收点的距离,即标签和基站的距离。从而用相关算法进行定位。注意的是由于受到脉冲干扰等会出现浮动值,需要进行滤波算法来获得比较准确的采样值。
case GAP_DEVICE_INFO_EVENT:
{
if( (pEvent->deviceInfo.pEvtData+7)==0xA7)
simpleBLEAddDeviceInfo( pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType, pEvent->deviceInfo.rssi ,(pEvent->deviceInfo.pEvtData+6));
}
break;
ibeacon和蓝牙有什么区别_它们的区别在哪里
7-iBeacon参数
蓝牙IBEACON协议案例详细解析
摘自:nrf Connect低功耗蓝牙APP工具的使用
APP下载: nRF Master Control Panel (BLE)
安卓、IOS平台都有
该APP可以实现SCANER和ADVERTER两种角色
下面主要来讲讲SCANER角色的使用
点击SCAN或者下拉界面,可以刷新设备列表,右滑界面可以看到每个设备的信号强度的变化曲线图,不同颜色代表不同的设备。
测试设备的设备名称GCBT40-my,点击CONNECT
连接成功后会自动获取所有的服务UUID
设备GCBT40-my发布了服务UUID=0xff10,
特征值UUID=0xff11为可写操作(点击图标”↑“),特征值UUID=0xff12为可监听操作(点击图标”↓↓↓“),
写操作,数据从APP端发送到从设备端
点击写操作后,可以选择输入数据的格式,TEXT为字符串格式,可以先在Save as里输入数据,再点SAVE保存,下次可以直接在LOAD里发送该数据包
数据从从设备端发送到APP端
点击图标”↓↓↓“后,APP后台自动监听从设备notify上来的数据,右滑界面,可以看到接收到的每条数据
参考:千锋教育嗨哥_JDY-10M蓝牙模块教学视频
地址:https://www.bilibili.com/video/BV1tK4y1Y7qb?from=search&seid=539420550276958520&spm_id_from=333.337.0.0
使用AT指令,隐藏了底层的蓝牙通信协议栈