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

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

基础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-E50E24DCCA9E16bit uuid为0x0001
6E400002-B5A3-F393-E0A9-E50E24DCCA9E16bit uuid为0x0002
6E400003-B5A3-F393-E0A9-E50E24DCCA9E16bit 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_typeuuid_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的数量如下图所示

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

根据log的提示去修改IRAM1

 在向协议栈注册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进行对比判断。

 

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6