I currently have one DA14531mod running the peripheral example. Using the LightBlue app I can read different values from the module.
How can I instead of using a phone use another module to read data from the peripheral? I have another DA14531mod running the central example but I don't know how to use that to read data from the peripheral.
I have read this document: http://lpccs-docs.renesas.com/tutorial-custom-profile-DA145xx/create_custom_profile_characteristic.html but I don't get how I should read a peripherals characteristic with the central.
Hi Markus,Let me try to explain based on the central example and the prox_reporter example which are both being used:As you can see on user_ble_gatt.c file we use the following function for GATT Writes…
Hi Markus,In order to try to explain your question as well:If you want to toggle the LED in the peripheral you will need to write on Service 1, on characteristic:
// LED State Characteristic Declaration…
Hi Markus,The status message you are getting corresponds to:
///Request not allowed in current state. GAP_ERR_COMMAND_DISALLOWED = 0x43,
Hi Markus,Thank you for posting your question online and for your interest in our BLE products.Please check our Central example on our GitRepo:BLE_SDK6_examples/connectivity/central at main · dialog-semiconductor/BLE_SDK6_examples · GitHubIn this example:Inside the user_ble_gatt.c file you can find the functions we are using for GATT Reads and Writes.Kind Regards,OV_Renesas
Ok so let us say I run the "ble_app_peripheral" example on one module and the central example on another. If I for example want to toggle the LED in the peripheral I need to write to the characteristic "SVC1_IDX_LED_STATE_VAL" right?
If that is the case, can I use the "user_ble_gatt_write" function, and what should the parameters be?
I'm just trying to understand how I actually should use these functions to write to and read from characteristics.
Hi Markus,Let me try to explain based on the central example and the prox_reporter example which are both being used:As you can see on user_ble_gatt.c file we use the following function for GATT Writes:
/** **************************************************************************************** * @brief Perform a gatt write * @param[in] con_idx - connection identifier * @param[in] handle - attribute handle to write * @param[in] data - data to write * @param[in] data_len - data len * @return void **************************************************************************************** */ void user_ble_gatt_write(uint8_t op, uint8_t con_idx, uint16_t handle, uint8_t *data, uint16_t data_len) { struct gattc_write_cmd *cmd = KE_MSG_ALLOC_DYN(GATTC_WRITE_CMD, KE_BUILD_ID(TASK_GATTC, con_idx), TASK_APP, gattc_write_cmd, sizeof(struct gattc_write_cmd) + data_len); cmd->operation = op; cmd->auto_execute = 1; cmd->seq_num = 0; cmd->offset = 0; cmd->length = data_len; cmd->cursor = 0; cmd->handle = handle; memcpy(&cmd->value[0], data, data_len); ke_msg_send(cmd); }
user_ble_gatt_write(central_app_env.periph_devices[i].serv_disc.ias_write_op, central_app_env.periph_devices[i].con_idx, central_app_env.periph_devices[i].serv_disc.ias_char.c.value_handle, ¢ral_app_env.periph_devices[i].serv_disc.ias_alert_counter, sizeof(uint8_t));
uint8_t op
. . . if(gatt_char->c.properties & GATT_PROP_WRITE) { central_app_env.periph_devices[con_idx].serv_disc.ias_write_op = GATTC_WRITE; } else if(gatt_char->c.properties & GATT_PROP_WRITE_NO_RESP) { central_app_env.periph_devices[con_idx].serv_disc.ias_write_op = GATTC_WRITE_NO_RESPONSE; } else if(gatt_char->c.properties & GATT_PROP_WRITE_SIGNED) { central_app_env.periph_devices[con_idx].serv_disc.ias_write_op = GATTC_WRITE_SIGNED; }else { central_app_env.periph_devices[con_idx].serv_disc.ias_handle_valid = false; } . . .
typedef struct{ uint8_t con_idx; bool con_valid; struct bd_addr addr; service_disc_t serv_disc; }periph_device_t;
/** **************************************************************************************** * @brief Callback from SDK when connection occurs. * @param[in] connection_idx connection identifier * @param[in] param peer connection info * @return void **************************************************************************************** */ void user_on_connection(uint8_t connection_idx, struct gapc_connection_req_ind const *param) { if(central_app_env.connection_timer != EASY_TIMER_INVALID_TIMER){ app_easy_timer_cancel(central_app_env.connection_timer); } default_app_on_connection(connection_idx, param); dbg_printf("%s\r\n", __func__); central_app_env.periph_devices[connection_idx].addr = param->peer_addr; central_app_env.periph_devices[connection_idx].con_idx = connection_idx; central_app_env.periph_devices[connection_idx].con_valid = true; central_app_env.num_connections++; if(central_app_env.num_connections < CFG_MAX_CONNECTIONS) { ble_scan_for_devices(); } #ifdef ENABLE_IAS central_app_env.periph_devices[connection_idx].serv_disc.ias_handle_valid = false; #endif #ifdef ENABLE_BAS central_app_env.periph_devices[connection_idx].serv_disc.bas_handle_valid = false; #endif user_gatt_discover_all_services(connection_idx, 1); }
central_app_env.periph_devices[i].serv_disc.ias_char.c.value_handle,
typedef struct{ uint16_t last_handle; #ifdef ENABLE_BAS gattc_chars_t bas_char; bool bas_handle_valid; #endif #ifdef ENABLE_IAS gattc_chars_t ias_char; bool ias_handle_valid; uint8_t ias_write_op; uint8_t ias_alert_counter; #endif }service_disc_t;
typedef struct { att_uuid_t uuid; ///< item UUID uint16_t handle; ///< item handle /** characteristic data (if type == ::GATTC_ITEM_TYPE_CHARACTERISTIC) */ struct { uint16_t value_handle; ///< characteristic value handle uint8_t properties; ///< characteristic properties } c; } gattc_chars_t;
// LED State Characteristic Declaration [SVC1_IDX_LED_STATE_CHAR] = {(uint8_t*)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL}, // LED State Characteristic Value [SVC1_IDX_LED_STATE_VAL] = {SVC1_LED_STATE_UUID_128, ATT_UUID_128_LEN, PERM(WR, ENABLE) | PERM(WRITE_COMMAND, ENABLE), PERM(RI, ENABLE) | DEF_SVC1_LED_STATE_CHAR_LEN, 0, NULL}, // LED State Characteristic User Description [SVC1_IDX_LED_STATE_USER_DESC] = {(uint8_t*)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC1_LED_STATE_USER_DESC) - 1, sizeof(DEF_SVC1_LED_STATE_USER_DESC) - 1, (uint8_t *) DEF_SVC1_LED_STATE_USER_DESC},
GATTC_WRITE_NO_RESPONSE
... for(i = 0; i < svc->end_hdl - svc->start_hdl; i++) { const union gattc_sdp_att_info *info = &svc->info[i]; if (info->att_type == GATTC_SDP_NONE) { continue; } current_handle = svc->start_hdl + i + 1; switch(info->att_type) { case GATTC_SDP_ATT_VAL: { //struct gattc_sdp_att att; memcpy(&svc_ret->items[char_index].uuid.uuid, info->att.uuid, info->att.uuid_len); svc_ret->items[char_index].c.value_handle = current_handle; svc_ret->items[char_index].uuid.type = info->att.uuid_len == sizeof(uint16_t) ? ATT_UUID_16 : ATT_UUID_128; }break; . . .
uint8_t val = 01;
sizeof(uint8_t) or sizeof(val)
Thanks a lot for this! It made things a lot clearer.
I now have the write functionality working with two modules, but struggling with reading.
To begin with, I'm running the peripheral example on one of the modules. I connect to it with my phone and read service 3 characteristic 4. This returns a value that increments with every read.
Now when running the central example on another module and connecting to the peripheral I can't read the same characteristic that I did with the phone. This is what I've tried:
When the central is discovering all the services, it lists all the available characteristics. This is a picture of the discovery data being displayed for service 3. For characteristic 4 (the one I want to read) we can see it has a handle ID "0045".
I've then made some changes to the button press callback on the central. So when I'm pressing the button the "user_gatt_read_simple" gets called and passed the connection ID and the handle ID we got earlier (0045). So to my understanding, this should read service 3 characteristic 4.
The problem is that this does not do anything. It doesn't even trigger the "user_catch_rest_hndl" function on the peripheral side.
What am I doing wrong?
Hi Markus, Glad you were able to get the write functionality working.Regarding the read functionality on the central example:The example is searching for specific Services and Characteristics. Have you made any changes in your implementation?As you can see on handle_svc_ind function on the user_central_app.c file:
#ifdef ENABLE_IAS bool ias_uuid_match; uint16_t ias_uuid = ATT_SVC_IMMEDIATE_ALERT; uint16_t ias_alert_uuid = ATT_CHAR_ALERT_LEVEL; if(svc->svc_uuid.type == ATT_UUID_16){ ias_uuid_match = match_uuid((uint8_t *)&ias_uuid, (uint8_t *)&svc->svc_uuid.uuid.uuid16, sizeof(uint16_t)); } if(ias_uuid_match){ dbg_block_printf("Immediate Alert Service: ", NULL); } #endif
/// Immediate alert Service ATT_SVC_IMMEDIATE_ALERT = ATT_UUID_16(0x1802),
#ifdef ENABLE_IAS if(ias_uuid_match) { if( match_uuid( (uint8_t *)&ias_alert_uuid , (uint8_t *)&gatt_char->uuid.uuid, 2) ) { memcpy(¢ral_app_env.periph_devices[con_idx].serv_disc.ias_char, gatt_char, sizeof(gattc_chars_t) ); central_app_env.periph_devices[con_idx].serv_disc.ias_handle_valid = true; dbg_block_printf("\tAlert Level Char\r\n", NULL); if(gatt_char->c.properties & GATT_PROP_WRITE) { central_app_env.periph_devices[con_idx].serv_disc.ias_write_op = GATTC_WRITE; } else if(gatt_char->c.properties & GATT_PROP_WRITE_NO_RESP) { central_app_env.periph_devices[con_idx].serv_disc.ias_write_op = GATTC_WRITE_NO_RESPONSE; } else if(gatt_char->c.properties & GATT_PROP_WRITE_SIGNED) { central_app_env.periph_devices[con_idx].serv_disc.ias_write_op = GATTC_WRITE_SIGNED; }else { central_app_env.periph_devices[con_idx].serv_disc.ias_handle_valid = false; } } } #endif
dbg_block_printf("\t%04x char %s prop=%02x (%s)\r\n", gatt_char->handle, format_uuid(&gatt_char->uuid), gatt_char->c.properties, format_properties(gatt_char->c.properties));
typedef struct { att_uuid_t uuid; ///< item UUID uint16_t handle; ///< item handle /** characteristic data (if type == ::GATTC_ITEM_TYPE_CHARACTERISTIC) */ struct { uint16_t value_handle; ///< characteristic value handle uint8_t properties; ///< characteristic properties } c; } gattc_chars_t; typedef struct{ uint16_t last_handle; #ifdef ENABLE_BAS gattc_chars_t bas_char; bool bas_handle_valid; #endif #ifdef ENABLE_IAS gattc_chars_t ias_char; bool ias_handle_valid; uint8_t ias_write_op; uint8_t ias_alert_counter; #endif }service_disc_t; typedef struct{ uint8_t con_idx; bool con_valid; struct bd_addr addr; service_disc_t serv_disc; }periph_device_t; typedef struct{ att_uuid_t svc_uuid; uint16_t num_chars; gattc_chars_t items[__ARRAY_EMPTY]; }service_info_t;
user_gatt_read_simple(con_idx, central_app_env.periph_devices[con_idx].serv_disc.bas_char.c.value_handle);
In your implementation you are reading the handler from the print I shared above. Please see that you are printing the item handle of the gattc_chars_t struct and not the value_handle of the c struct which is inside the gattc_chars_t struct.I know it can be quite confusing to follow. You could try with your debugger attached and try to read the values (by adding them on a Watch window) saved inside those structs in order to get a better understanding. I hope this could be helpful. If you face any issues, feel free to ask. If you found any of the answers helpful to your issues, please verify them so you can help others in the community as well.Kind Regards,OV_Renesas
Thank you for the thorough answers. Here are some clarifications:
"The example is searching for specific Services and Characteristics. Have you made any changes in your implementation?"
- Yes, I have made some modifications so the code executes even though there is no UUID-match
"Your Gatt Read is not working because you have given wrong Handle ID."
- I checked this and I am giving the right Handle ID. If I print the value handle for every discovered characteristic like this:
I get this:
When I read the last characteristic (0048) on my phone everything works. When I do it with my central nothing happens for 20-30 seconds and then I get a message on the central: GATTC_READ: status: 0045
As I mentioned earlier, I'm able to write to characteristics just fine, but reading does not work.
Hi Markus,Thank you for your reply.After checking it again I think the problem is that you do not catch the response message.The GATTC_READ: status 0045 you got corresponds to a Timeout Error.As you can see on the uset_gatt_read_simple function:
/** **************************************************************************************** * @brief Perform a simple gatt read - read is returned by GATTC_READ_IND * @param[in] con_idx - connection identifier * @param[in] handle - attribute handle to read * @return void **************************************************************************************** */ void user_gatt_read_simple(uint8_t con_idx, uint16_t handle) { struct gattc_read_cmd * req = KE_MSG_ALLOC(GATTC_READ_CMD, KE_BUILD_ID(TASK_GATTC, con_idx), TASK_APP, gattc_read_cmd); //request type req->operation = GATTC_READ; req->nb = 1; req->req.simple.offset = 0; req->req.simple.length = 0; req->req.simple.handle = handle; //send request to GATT ke_msg_send(req); }
/*Read Value*/ /*Read Using UUID*/ /*Read Long Value*/ /*Read Multiple Values*/ /// Read command GATTC_READ_CMD, /// Read response GATTC_READ_IND,
... case GATTC_READ_IND: { struct gattc_read_ind const *ind = (struct gattc_read_ind const*)param; #ifdef ENABLE_BAS if(ind->handle == central_app_env.periph_devices[conn_idx].serv_disc.bas_char.c.value_handle) { dbg_printf("Battery Level Read: %d \r\n", ind->value[0]); }else #endif { dbg_printf("GATTC_READ_IND: handle: %04x\r\n", ind->handle); } }break; default: break; ...
Inside the GATTC_READ_IND case you should create another case for the specific characteristic you want. Then you should be able to get the response (read) from this characteristic and print it.I hope this would work on your side.Kind Regards,OV_Renesas
This can't be the problem. I placed a breakpoint inside the GATTC_READ_IND case, and apparently, the code never goes there.
So to clarify, I have the read-call placed inside the pushbutton callback. So every time I push the button I should read the characteristic. When I first press the button, nothing happens for 20-30s but then I get the status message "GATTC_READ: status 0045". If I press the button after this I get the status message GATTC_READ: status 0043.