Hi,
I am using NC9 2.9.0 with VSCode and NRF extension.
I have seen that since version 1.2 of Device Information Service (DIS) spec, it is theorically possible to have multiple instances of this service, by respecting the following rules:
- No more than one instance of the Device Information Service may be exposed as a «Primary Service» on a device. One or more instances of the Device Information Service may be exposed as a «Secondary Service» on the same device.
- A Device Information Service instance that is exposed as a «Secondary Service» must be included in another service
So, I have developed some code to do that: here is the declaration of the services
/********************DIS MULTI BEGIN******************* */
/* ---- UUIDs standard (Assigned Numbers)
* Service DIS: 0x180A
* Model Number: 0x2A24
* Manufacturer: 0x2A29
* PnP ID: 0x2A50
*/
#define BT_UUID16_DIS BT_UUID_DECLARE_16(0x180A)
#define BT_UUID16_DIS_MODEL_NUMBER BT_UUID_DECLARE_16(0x2A24)
#define BT_UUID16_DIS_MANUF_NAME BT_UUID_DECLARE_16(0x2A29)
#define BT_UUID16_DIS_PNP_ID BT_UUID_DECLARE_16(0x2A50)
/* PnP ID format (7 bytes) :
* [0] VendorID_Source (0x01 = Bluetooth SIG, 0x02 = USB)
* [1..2] VendorID (Little Endian)
* [3..4] ProductID (LE)
* [5..6] ProductVersion (LE)
* Cf. DIS spec. */
struct __packed pnp_id_t {
uint8_t vid_source;
uint16_t vid;
uint16_t pid;
uint16_t ver;
};
/* DIS primary */
static const char dis_primary_manufacturer[] = "WIKA";
static const char dis_primary_model[] = "Nordic";
static const struct pnp_id_t dis_primary_pnp = {
.vid_source = 0x02, .vid = 0x1915, .pid = 0x1337, .ver = 0x0102
};
/* DIS secondary */
static const char dis_module_manufacturer[] = "WIKA";
static const char dis_module_model[] = "STM";
static const struct pnp_id_t dis_module_pnp = {
.vid_source = 0x01, .vid = 0x05F9, .pid = 0x0001, .ver = 0x0001
};
static ssize_t read_str(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
const char *str = attr->user_data;
size_t slen = strlen(str);
return bt_gatt_attr_read(conn, attr, buf, len, offset, str, slen);
}
static ssize_t read_pnpid(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
const struct pnp_id_t *pnp = attr->user_data;
return bt_gatt_attr_read(conn, attr, buf, len, offset, pnp, sizeof(*pnp));
}
/* ===== 1) DIS PRIMARY ===== */
BT_GATT_SERVICE_DEFINE(dis_primary_svc,
BT_GATT_PRIMARY_SERVICE(BT_UUID16_DIS),
BT_GATT_CHARACTERISTIC(BT_UUID16_DIS_MANUF_NAME, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_str, NULL, (void *)dis_primary_manufacturer),
BT_GATT_CHARACTERISTIC(BT_UUID16_DIS_MODEL_NUMBER, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_str, NULL, (void *)dis_primary_model),
BT_GATT_CHARACTERISTIC(BT_UUID16_DIS_PNP_ID, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_pnpid, NULL, (void *)&dis_primary_pnp),
// BT_GATT_INCLUDE_SERVICE(&dis_module_svc)
);
/* ===== 2) DIS SECONDARY ===== */
// BT_GATT_SERVICE_DEFINE(dis_module_svc,
// BT_GATT_SECONDARY_SERVICE(BT_UUID16_DIS),
// BT_GATT_CHARACTERISTIC(BT_UUID16_DIS_MANUF_NAME, BT_GATT_CHRC_READ,
// BT_GATT_PERM_READ, read_str, NULL, (void *)dis_module_manufacturer),
// BT_GATT_CHARACTERISTIC(BT_UUID16_DIS_MODEL_NUMBER, BT_GATT_CHRC_READ,
// BT_GATT_PERM_READ, read_str, NULL, (void *)dis_module_model),
// BT_GATT_CHARACTERISTIC(BT_UUID16_DIS_PNP_ID, BT_GATT_CHRC_READ,
// BT_GATT_PERM_READ, read_pnpid, NULL,(void *)&dis_module_pnp)
// );
/* 1) Secondary DIS as a runtime (non-const) service */
static struct bt_gatt_attr dis2_attrs[] = {
BT_GATT_SECONDARY_SERVICE(BT_UUID16_DIS),
BT_GATT_CHARACTERISTIC(BT_UUID16_DIS_MANUF_NAME, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_str, NULL, (void *)dis_module_manufacturer),
BT_GATT_CHARACTERISTIC(BT_UUID16_DIS_MODEL_NUMBER, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_str, NULL, (void *)dis_module_model),
BT_GATT_CHARACTERISTIC(BT_UUID16_DIS_PNP_ID, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_pnpid, NULL, (void *)&dis_module_pnp),
};
static struct bt_gatt_service dis2_svc = BT_GATT_SERVICE(dis2_attrs);
/* ===== 3) Service "Module X" including secondary DIS ===== */
/* UUID 128-bit for characteristic of module X */
static struct bt_uuid_128 modx_ver_uuid =
BT_UUID_INIT_128(BT_UUID_128_ENCODE(0xaaaaaaaa,0xbbbb,0xcccc,0xdddd,0xeeeeffffffff));
static uint8_t modx_version = 0x01;
static ssize_t read_modx_ver(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
const uint8_t *val = attr->user_data;
return bt_gatt_attr_read(conn, attr, buf, len, offset, val, sizeof(*val));
}
static struct bt_uuid_128 module_x_uuid =
BT_UUID_INIT_128(0x23,0x01,0x45,0x67, 0x89,0xab,0xcd,0xef, 0x01,0x23,0x45,0x67, 0x89,0xab,0xcd,0xef);
static struct bt_gatt_attr modx_attrs[] = {
BT_GATT_PRIMARY_SERVICE(&module_x_uuid.uuid),
BT_GATT_INCLUDE_SERVICE(&dis2_svc), /* include runtime service */
BT_GATT_CHARACTERISTIC(&modx_ver_uuid.uuid,
BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
read_modx_ver, NULL, &modx_version),
};
static struct bt_gatt_service modx_svc = BT_GATT_SERVICE(modx_attrs);
/********************DIS MULTI END******************* */
and here is related code in main
err = bt_enable(NULL);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return 0;
}
printk("Bluetooth initialized\n");
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
err = bt_gatt_service_register(&dis2_svc);
if (err) {
printk("Bluetooth secondary service init failed (err %d)\n", err);
}
err = bt_gatt_service_register(&modx_svc);
if (err) {
printk("Bluetooth primary service init failed (err %d)\n", err);
}
So I have a DIS declared as Primary service, and a second Primary service, which includes my secondary service, second instance of DIS.
After compiling flashing, I can see my Primary service DIS, my custom primary Service, and I see also a secondary service, but it is empty and has UUID 0x2801 as you can in picture:

Do you know what is the problem and how I can resolve this ?