蓝牙服务
skaiuijing
GATT层
GATT:属性协议配置规范,用来规范属性中的数据内容,并对属性进行分组,这使得BLE具有强大的兼容性
在GTTA中定义了两类角色:服务器和客户端。
GTTA是建立服务的基础层,服务就是BLE中组织数据传输与接收的最小单位。服务配置列表文件称为profile。
profile
应用层
蓝牙任务分为两类:标准蓝牙任务规范profile(公有任务),非标准蓝牙任务规范profile(私有任务)
公有任务:GATT规范列表发布
私有任务:用户自定义任务
程序
服务初始化
包含两个部分:队列初始化和服务配置初始化
队列初始化:
1 | ret_code_t nrf_ble_qwr_init(nrf_ble_qwr_t * p_qwr, |
服务配置初始化:
添加基础UUID
通过系统调用sd_ble_uuid_vs_add:
1 | SVCALL(SD_BLE_UUID_VS_ADD, uint32_t, sd_ble_uuid_vs_add(ble_uuid128_t const *p_vs_uuid, uint8_t *p_uuid_type)); |
添加主服务
通过系统调用sd_ble_gatts_service_add:
1 | SVCALL(SD_BLE_GATTS_SERVICE_ADD, uint32_t, sd_ble_gatts_service_add(uint8_t type, ble_uuid_t const *p_uuid, uint16_t *p_handle)); |
添加角色
通过系统调用sd_ble_gatts_characteristic_add:
1 | SVCALL(SD_BLE_GATTS_CHARACTERISTIC_ADD, uint32_t, sd_ble_gatts_characteristic_add(uint16_t service_handle, ble_gatts_char_md_t const *p_char_md, ble_gatts_attr_t const *p_attr_char_value, ble_gatts_char_handles_t *p_handles)); |
协议栈事件处理
主机
1 | graph LR |
蓝牙服务建立
以手机和BLE设备为例:
1.搜索后发现BLE设备的广播
2.连接设备
3.获取其蓝牙地址和提供的服务
4.使用服务特性,间接调用read,write和notify等接口
5.断开连接
但是现在有一个问题,在分布式系统中,如何确保对象的唯一性?
我们马上会联想到TCP/IP协议中的IPv4/6机制,但蓝牙协议使用了UUID作为通用唯一识别码。
但不同于IP协议,BLE在GATT层中规范定义的所有特性都有一个UUID值。
继续细分:
基础UUID
该UUID是用来规范蓝牙技术联盟的,所有联盟都共用该基础UUID
主服务UUID
在协议栈中,通常我们会增加一个特定的基础UUID,这就是主服务UUID
特性UUID
在主服务的基础上,再自定义不同的UUID用于区分不同特性。
蓝牙派发
(callback翻译成回调有够糟糕的,翻译成调用还差不多)
观察者
观察者负责处理感兴趣的事件
FLASH段
简单来说,其实就是在内存地址上进行分段,每个内存段分别进行命名。
创建观察者时,就把观察者变量和callback函数放进行这些内存段中。
1 | section_name在bootloader或者链接脚本里面进行定义,找几块顺眼的数据段进行命名。 |
执行
其实就是轮询FLASH段内的每个观察者,对比一下是否感兴趣,如果感兴趣,就执行该观察者的callback函数。
至于优先级,其实就是轮询的先后,根据定义时的优先级设置,把观察者信息放在不同先后的内存位置。
思考
笔者来设计的话,想到的方法就是建立一条链表,把观察者变量和callback函数都挂上去,通过链表进行轮询。
但蓝牙派发则不同,它在先前定义好的内存地址上存放观察者,轮询时顺着地址遍历即可。
嗯,说实话,这样做真的是非常节约内存。