Closed cwahn closed 1 year ago
1- A profile can correspond to one or more services, for example, in the example "esp-idf/examples/bluetooth/bluedroid/ble/gatt_server," one profile corresponds to two services.
2- ESP-IDF supports two methods for creating services:
esp_ble_gatts_create_attr_tab
: Create a service. When service creation is done, a callback event ESP_GATTS_CREATE_EVT
is called to report status and service ID to the profile.esp_ble_gatts_start_service
: Start a service.@zhp0406 Thank you for the reply. I have checked out both of the examples and they still don't give a answer.
esp_ble_gatts_create_attr_tab
is for a service. Is that mean, if one goes with 2nd approach of multiple services, one should call multiple esp_ble_gatts_create_attr_tab
at an ESP_GATTS_REG_EVT
event and start the corresponding service on ESP_GATTS_CREAT_ATTR_TAB_EVT
??@cwahn you are right,If you want to create multiple services, you need to call esp_ble_gatts_create_attr_tab and esp_ble_gatts_start_service multiple times.
Let me explain again,
A Profile includes one or more Services, and each Service contains one or more Characteristics.
For example, if we have a health monitoring device, it can support two Profiles: "Health Status" and "Heart Rate Monitoring." Each Profile includes different Services; for instance, the "Health Status" Profile may contain two Services, "Step Count" and "Sleep Quality," while the "Heart Rate Monitoring" Profile may include two Services, "Heart Rate Measurement" and "Sensor Location."
In practical use, the client only discovers some services provided by the server. It is up to the client to determine which profile a specific service belongs to.
Hello! I just started to learn/try Ble on esp32s3 and documentation or examples really not enought to understand How to create Profile: [service1: char1, char2], [service2: char11, char22]
in the example "esp-idf/examples/bluetooth/bluedroid/ble/gatt_server," one profile corresponds to two services. Tutorial says two profiles with one service in each
So, if I want to create two services in one profile by gatt service table, i need:
#define PROFILE_NUM 1
#define COMMON_PROFILE_APP_IDX 0
static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = {
[COMMON_PROFILE_APP_IDX] = {
.gatts_cb = gatts_common_event_handler,
.gatts_if = ESP_GATT_IF_NONE,
},
};
enum first_service_idx
{
IDX_SRV1_SERVICE,
IDX_SRV1_CHAR1,
IDX_SRV1_CHAR2,
SRV1_NUMB_OF_IDX,
};
static const esp_gatts_attr_db_t first_gatt_db[SRV1_NUMB_OF_IDX] =
{
//Service 1
[IDX_SRV1_SERVICE] =
{
{ESP_GATT_AUTO_RSP},
{
ESP_UUID_LEN_16,
(uint8_t *)&primary_service_uuid,
ESP_GATT_PERM_READ,
sizeof(uint16_t), /
sizeof(GATTS_COM_SERVICE_UUID),
(uint8_t *)&GATTS_COM_SERVICE_UUID
}
},
// Characteristic1 Declaration
[IDX_SRV1_CHAR1] =
{
{ESP_GATT_AUTO_RSP},
{
ESP_UUID_LEN_16,
(uint8_t *)&character_declaration_uuid,
ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,
CHAR_DECLARATION_SIZE,
(uint8_t *)&char_prop_read_write_notify
}
},
// Characteristic2 Declaration
[IDX_SRV1_CHAR2] =
{
{ESP_GATT_AUTO_RSP},
{
ESP_UUID_LEN_16,
(uint8_t *)&character_declaration_uuid,
ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,
CHAR_DECLARATION_SIZE,
(uint8_t *)&char_prop_read_write_notify
}
}
}
enum second_service_idx
{
IDX_SRV2_SERVICE,
IDX_SRV2_CHAR11,
IDX_SRV2_CHAR22,
SRV2_NUMB_OF_IDX,
};
static const esp_gatts_attr_db_t second_gatt_db[SRV2_NUMB_OF_IDX] =
{
//Service 2
[IDX_SRV2_SERVICE] =
{
{ESP_GATT_AUTO_RSP},
{
ESP_UUID_LEN_16,
(uint8_t )&primary_service_uuid,
ESP_GATT_PERM_READ,
sizeof(uint16_t), /
sizeof(GATTS_COM_SERVICE_UUID),
(uint8_t )&GATTS_COM_SERVICE_UUID
}
},
// Characteristic11 Declaration
[IDX_SRV2_CHAR11] =
{
{ESP_GATT_AUTO_RSP},
{
ESP_UUID_LEN_16,
(uint8_t )&character_declaration_uuid,
ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,
CHAR_DECLARATION_SIZE,
(uint8_t )&char_prop_read_write_notify
}
},
// Characteristic2 Declaration
[IDX_SRV2_CHAR22] =
{
{ESP_GATT_AUTO_RSP},
{
ESP_UUID_LEN_16,
(uint8_t )&character_declaration_uuid,
ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,
CHAR_DECLARATION_SIZE,
(uint8_t )&char_prop_read_write_notify
}
}
}
uint16_t first_handle_table[SRV1_NUMB_OF_IDX];
uint16_t second_handle_table[SRV2_NUMB_OF_IDX];
3. in event ESP_GATTS_REG_EVT: call twice esp_ble_gatts_create_attr_tab
esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(first_gatt_db, gatts_if, SRV1_NUMB_OF_IDX, 1); esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(second_gatt_db, gatts_if, SRV2_NUMB_OF_IDX, 2);
4. in case ESP_GATTS_CREAT_ATTR_TAB_EVT: call twice esp_ble_gatts_start_service
memcpy(first_handle_table, param->add_attr_tab.handles, sizeof(first_handle_table)); esp_ble_gatts_start_service(first_handle_table[IDX_SRV1_SERVICE]); memcpy(second_handle_table, param->add_attr_tab.handles, sizeof(second_handle_table)); esp_ble_gatts_start_service(second_handle_table[IDX_SRV2_SERVICE]);
4. In other events check both services
That's right?
@kitty7c6 right,and the following approach would be better: in case ESP_GATTS_CREAT_ATTR_TAB_EVT: call twice esp_ble_gatts_start_service
if(param->add_attr_tab.svc_inst_id == IDX_SRV1_SERVICE){
//...
//esp_ble_gatts_start_service IDX_SRV1_SERVICE
}
if(param->add_attr_tab.svc_inst_id == IDX_SRV2_SERVICE){
//...
//esp_ble_gatts_start_service IDX_SRV2_SERVICE
}
if(param->add_attr_tab.svc_inst_id == IDX_SRV1_SERVICE){ //... //esp_ble_gatts_start_service IDX_SRV1_SERVICE }
if I make two enums
enum first_service_idx
{
IDX_SRV1_SERVICE, // here will be default 0
IDX_SRV1_CHAR1, // and 1
IDX_SRV1_CHAR2, // 2
SRV1_NUMB_OF_IDX, // 3
};
and
enum second_service_idx
{
IDX_SRV2_SERVICE, // here will be default 0 too!
IDX_SRV2_CHAR11,
IDX_SRV2_CHAR22,
SRV2_NUMB_OF_IDX,
};
the code
f(param->add_attr_tab.svc_inst_id == IDX_SRV1_SERVICE){
//...
//esp_ble_gatts_start_service IDX_SRV1_SERVICE
}
if(param->add_attr_tab.svc_inst_id == IDX_SRV2_SERVICE){
//...
//esp_ble_gatts_start_service IDX_SRV2_SERVICE
}
will be similar
f(param->add_attr_tab.svc_inst_id == 0){
//...
//esp_ble_gatts_start_service IDX_SRV1_SERVICE
}
if(param->add_attr_tab.svc_inst_id == 0){
//...
//esp_ble_gatts_start_service IDX_SRV2_SERVICE
}
May be, if I call
esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(first_gatt_db, gatts_if, SRV1_NUMB_OF_IDX, 1); - here id of srv1=1
esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(second_gatt_db, gatts_if, SRV2_NUMB_OF_IDX, 2); - here id of srv1=2
will be better:
#define srv1_ID 1
#define srv2_ID 2
esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(first_gatt_db, gatts_if, SRV1_NUMB_OF_IDX, srv1_ID );
esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(second_gatt_db, gatts_if, SRV2_NUMB_OF_IDX, srv2_ID );
f(param->add_attr_tab.svc_inst_id == srv1_ID ){
//...
//esp_ble_gatts_start_service IDX_SRV1_SERVICE
}
if(param->add_attr_tab.svc_inst_id == srv2_ID ){
//...
//esp_ble_gatts_start_service IDX_SRV2_SERVICE
}
Thanks for reporting, feel free to reopen.
Answers checklist.
General issue report
This might be because I am not familiar with the ESP-IDF Bluetooth stack, but it seems the relationship with the GATT specification concept and implementation are somewhat vague and unspecified.
I have reviewed the examples and guides in
/esp-idf/examples/bluetooth/
. And could not figure out the clear relationship between BLE GATT specification and implementation.GATT has concepts of;
In implementation
I guess a profile table is to support multiple profiles, so basically a list of profiles. A profile instance consists of a callback function, attributes, app id, gatt_if, conn_id, which apparently represent a profile.
However, it is not clear what is representing a service. It seems like
esp_ble_gatts_create_attr_tab
could be used instead ofesp_ble_gatts_create_service
and vice versa. This makes the attribute table corresponds to "a" service. However ,when creating the table the example refersto an app_id, which corresponds to an application of profile. This make the list of attributes meant to represent a profile or multiple services. However, can't find such an example.So my question is what is the idiomatic way to represent a service in ESP-IDF Bluetooth stack?
Is it a list of attributes that should be used for a service? In this case what happens to the secondary, and includes the service of GATT specification? They should major and auxiliary functionality of a "device" per SIG specification. So are they should be in different lists in the same profile(device)? or one. How should they register? Is only the primary handle or all the service handles?
Otherwise, there is no such structure in ESP-IDF Bluetooth stack and a list of attributes should be used to represent a profile or multiple services. If this is the case, what should be the construction of the list? (svc decls, chars... or svc decl, chars..., svc decl, chars... ) Also, if this is the case, how do the services should be registered???