NRF52系列多个 base uuid 的问题,以client为例。

基础uuid知识

nordic的nrf sdk希望我们设置uuid的方式跟蓝牙技术联盟SIG的方式一样,也就是服务和特性的uuid是基于同一个base uuid修改产生的,比如base uuid是0x0000xxxx-0000-1000-8000-00805F9B34FB,那么服务和特性的128bit uuid就要基于此base uuid通过修改其中的xxxx而生成,这里的xxxx称为16bit的uuid。一般来说,base uuid和16bit的xxxx都是自定义的,但是xxxx的位置一定是在128bit uuid的第三和第四个字节,这就是标准的限制。

在nus_c的例程里,有这样一段代码:

#define NUS_BASE_UUID                   {{0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E}} /**< Used vendor-specific UUID. */

#define BLE_UUID_NUS_SERVICE            0x0001                      /**< The UUID of the Nordic UART Service. */
#define BLE_UUID_NUS_RX_CHARACTERISTIC  0x0002                      /**< The UUID of the RX Characteristic. */
#define BLE_UUID_NUS_TX_CHARACTERISTIC  0x0003                      /**< The UUID of the TX Characteristic. */

这段代码自定义了一个BASE_UUID,注意它是按小端模式存储的(跟一般顺序反过来的),数组里面的第13和第14个字节对应到128bit uuid的第4和第3个字节,也就是对应到xxxx这两个字节共16bit,数组里面设置为0了。

由以上代码可以知道,三个uuid分别为:

6E400001-B5A3-F393-E0A9-E50E24DCCA9E(16bit uuid为0x0001 )
6E400002-B5A3-F393-E0A9-E50E24DCCA9E(16bit uuid为0x0002 )
6E400003-B5A3-F393-E0A9-E50E24DCCA9E(16bit uuid为0x0002 )

然后,在ble_nus_c.c里面的ble_nus_c_init()函数里面,调用sd_ble_uuid_vs_add()函数将base uuid添加到协议栈:

ble_uuid128_t nus_base_uuid = NUS_BASE_UUID;    
err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_ble_nus_c->uuid_type);

其中p_ble_nus_c->uuid_type是协议栈返回的uuid类型,0为非法,1为sig的base uuid,往后的数字就是咱们自己添加到协议栈的uuid编号,也就是说,如果我们只添加了一个base uuid到协议栈,那么uuid_type就会被协议栈设置为2。

接下来是特征的发现,在ble_nus_c_on_db_disc_evt()里面。

void ble_nus_c_on_db_disc_evt(ble_nus_c_t * p_ble_nus_c, ble_db_discovery_evt_t * p_evt)
{
    ble_nus_c_evt_t nus_c_evt;
    memset(&nus_c_evt,0,sizeof(ble_nus_c_evt_t));

    ble_gatt_db_char_t * p_chars = p_evt->params.discovered_db.charateristics;

    // Check if the NUS was discovered.
    if (    (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE)
        &&  (p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_NUS_SERVICE)
        &&  (p_evt->params.discovered_db.srv_uuid.type == p_ble_nus_c->uuid_type))
    {
        for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
        {
            switch (p_chars[i].characteristic.uuid.uuid)
            {
                case BLE_UUID_NUS_RX_CHARACTERISTIC:
                    nus_c_evt.handles.nus_rx_handle = p_chars[i].characteristic.handle_value;
                    break;

                case BLE_UUID_NUS_TX_CHARACTERISTIC:
                    nus_c_evt.handles.nus_tx_handle = p_chars[i].characteristic.handle_value;
                    nus_c_evt.handles.nus_tx_cccd_handle = p_chars[i].cccd_handle;
                    break;

                default:
                    break;
            }
        }
        if (p_ble_nus_c->evt_handler != NULL)
        {
            nus_c_evt.conn_handle = p_evt->conn_handle;
            nus_c_evt.evt_type    = BLE_NUS_C_EVT_DISCOVERY_COMPLETE;
            p_ble_nus_c->evt_handler(p_ble_nus_c, &nus_c_evt);
        }
    }
}

在服务的if判断中,它判断了uuid_type以及16位的uuid是否和我们想要的一致,实际上由于一个uuid_type对应一个base uuid,由base uuid和16位uuid是可以得到完整的128bit uuid的,所以可以认为是进行了完整的服务uuid的对比,也就是:

  • uuid_type 唯一对应一个 uuid base
  • uuid_base + 16bit uuid  可以得到 128bit uuid

接下来看swtich,这里是遍历属性表,并把需要用到的特性handle记录下来,注意看这里只对16bit uuid作了比较,这是因为nrf sdk认为特性的base uuid 应该是要和服务的 base uuid 相同,所以就省去了uuid_type的对比。

多个base uuid的问题

那么问题来了,有的厂商的设备不是按照上面的方式定义uuid,服务和特性的base uuid不同可咋办。比如说,有一个设备,它的uuid如下:

Service:                   49535343-FE7D-4AE5-8FA9-9FAFD205E455
data Characteristic: 49535343-1E4D-4BD9-BA61-23C647249616
cmd Characteristic: 49535343-8841-43F4-A8D4-ECBE34729BB3

看着真的挺离谱的,不按标准来真的麻烦。那么如果要连接这个设备,并发现服务和特性,应该咋搞呢,那还能咋搞,那就认为有多个base uuid呗。

首先还是按照xxxx在第3和4字节的标准来定义三个base uuid和三个16bit uuid,注意base uuid是小端存储的,因此数组第14和13字节都设置为0.

#define SERVICE_UUID_BASE     {{0x55,0xe4,0x05,0xd2,0xaf,0x9f,0xa9,0x8f,0xe5,0x4a,0x7d,0xfe,0x00,0x00,0x53,0x49}}
#define DATA_UUID_BASE 		  {{0x16,0x96,0x24,0x47,0xC6,0x23,0x61,0xBA,0xD9,0x4B,0x4d,0x1e,0x00,0x00,0x53,0x49}}
#define CMD_UUID_BASE 		  {{0xb3,0x9b,0x72,0x34,0xbe,0xec,0xd4,0xa8,0xf4,0x43,0x41,0x88,0x00,0x00,0x53,0x49}}

#define BLE_UUID_SERVICE                0x5343
#define BLE_UUID_DATA_CHARACTERISTIC    0x5343
#define BLE_UUID_CMD_CHARACTERISTIC     0x5343

然后到ble_nus_c_init()里面,把三个uuid全注册到协议栈里面。一个base uuid对应一个uuid_type,uuid_type实际上就是协议栈分配的编号,为了保存对应的uuid_type,设置了一些全局变量。

static ble_uuid_t data_uuid;   //用于存储特性1的uuid_type
static ble_uuid_t cmd_uuid;    //用于存储特性2的uuid_type
//服务的uuid_type记录在p_ble_nus_c里面了,而这本来就是个全局变量,所以不另外存储服务的uuid_type

****** 以上两个全局变量是在ble_nus_c_init()之外定义的 *****
*********************************************************
******** 以下语句是在ble_nus_c_init()内的语句 ************

ble_uuid128_t service_base_uuid  =  SERVICE_UUID_BASE;
ble_uuid128_t data_base_uuid     =  DATA_UUID_BASE;
ble_uuid128_t cmd_base_uuid      =  CMD_UUID_BASE;

err_code = sd_ble_uuid_vs_add(&service_base_uuid , &p_ble_nus_c->uuid_type);
VERIFY_SUCCESS(err_code);
err_code = sd_ble_uuid_vs_add(&data_base_uuid , &data_uuid.type);
VERIFY_SUCCESS(err_code);
err_code = sd_ble_uuid_vs_add(&cmd_base_uuid , &cmd_uuid.type);
VERIFY_SUCCESS(err_code);

data_uuid.uuid = BLE_UUID_DATA_CHARACTERISTIC;
cmd_uuid.uuid  = BLE_UUID_CMD_CHARACTERISTIC;

以上语句向协议栈注册了三个base uuid,需要在sdk_config.h里面修改uuid的数量,如下图所示:

NRF52系列多个 base uuid 的问题,以client为例。_第1张图片

同时,这样操作会增大协议栈所需的RAM,因此如果出现报错,应该把 log level 提升到dubug级别,观察协议栈的log输出是否提示说内存不足,并根据log输出的建议修改app ram的起始大小和总大小。下图为修改log level的截图。

NRF52系列多个 base uuid 的问题,以client为例。_第2张图片

根据log的提示去修改IRAM1:

NRF52系列多个 base uuid 的问题,以client为例。_第3张图片

 在向协议栈注册base uuid之后,接下来看服务和特性的发现函数,在ble_nus_c_on_db_disc_evt()里面。

void ble_nus_c_on_db_disc_evt(ble_nus_c_t * p_ble_nus_c, ble_db_discovery_evt_t * p_evt)
{
    ble_nus_c_evt_t nus_c_evt;
    memset(&nus_c_evt,0,sizeof(ble_nus_c_evt_t));

    ble_gatt_db_char_t * p_chars = p_evt->params.discovered_db.charateristics;

    if (  (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE) &&
          (p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_SERVICE) &&
          (p_evt->params.discovered_db.srv_uuid.type == p_ble_nus_c->uuid_type) )
    {
        for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
        {
			if(p_chars[i].characteristic.uuid.uuid == BLE_UUID_CMD_CHARACTERISTIC && p_chars[i].characteristic.uuid.type == cmd_uuid.type){
                    nus_c_evt.handles.cmd_handle = p_chars[i].characteristic.handle_value;
			}else if(p_chars[i].characteristic.uuid.uuid == BLE_UUID_DATA_CHARACTERISTIC && p_chars[i].characteristic.uuid.type == data_uuid.type){
                    nus_c_evt.handles.data_handle = p_chars[i].characteristic.handle_value;
                    nus_c_evt.handles.cccd_handle = p_chars[i].cccd_handle;
			}
         }
				
        if (p_ble_nus_c->evt_handler != NULL)
        {
            nus_c_evt.conn_handle = p_evt->conn_handle;
            nus_c_evt.evt_type    = BLE_NUS_C_EVT_DISCOVERY_COMPLETE;
            p_ble_nus_c->evt_handler(p_ble_nus_c, &nus_c_evt);
        }
    }
}

以上函数跟之前的唯一变化就是,在遍历属性库的时候,需要同时对比特性的uuid_type和16bit uuid,而之前是仅对比16bit uuid的。多做了uuid_type的对比判断,其实就是多做了uuid base的对比判断,有了uuid base的对比判断,就能准确识别处所需要特性的handle,哪怕本案例中所有16bit uuid是一样的。

总结:

1. 16bit uuid一定是在128bit uuid的第三和第四个字节,这是标准、是规范。

2. 按照规范来设置uuid,那么当只有一个base uuid 时,在对服务的uuid_type和16bit uuid进行对比之后,特性只需要对比16bit uuid即可。

3. 如果不按照规范,即服务和特性的base uuid不同时,就需要向协议栈注册多个base uuid,并记录下对应的uuid_type(可以认为是该base uuid在协议栈中的编号)。

4. 需要修改sdk_config.h中uuid的个数,修改应用的RAM起始地址和大小。

5. 遍历属性表时,无论是服务和特性,都需要同时对16bit uuid和uuid type进行对比判断。

 

你可能感兴趣的:(嵌入式,低功耗蓝牙,BLE,nrf52,uuid)