Support,
Due to the limitations described in the referenced previous ticket, I am unable to successfully discover a service and its characteristics because of a hard fault occurrence. This fault does not occur when compiling and executing under debug mode but only fails in the release build. I have been developing this solution for a few weeks and recently began pre-production testing for a release scheduled for next week. It was during this production testing that I discovered this error. It has been challenging to determine the exact cause of the hard fault. My theory is that there is a GATT service conflict, but I cannot prove. The process can discover the primary service and begin the characteristic discovery process. However, a hard fault is thrown in the middle of the discovery. My logic is based off the "ble_app_interactive" example in the SDK.
I believe using something similar to a GATT queue would be ideal, but due to the discovery limitations described in my previous ticket, I can't utilize it as far as I'm aware. Do you have any recommendations or guidance to help understand this issue?
Thank you...
Related Ticket: https://devzone.nordicsemi.com/f/nordic-q-a/108832/gatt-service-discovery-failure-on-sig-base-uuid
#ifndef __SM_M_BLE_PEEKSMITH_H__ #define __SM_M_BLE_PEEKSMITH_H__ #include <stdint.h> #include "ble.h" #include "ble_srv_common.h" #include "nrf_sdh_ble.h" #include "nrf_ble_gq.h" /**@brief Macro for defining a ble_ps_c instance. * * @param _name Name of the instance. * @hideinitializer */ #define BLE_PEEKSMITH_C_DEF(_name) \ static ble_ps_c_t _name; \ NRF_SDH_BLE_OBSERVER(_name ## _obs, \ BLE_PEEKSMITH_C_BLE_OBSERVER_PRIO, \ m_ble_peeksmith_c_on_ble_evt, &_name) #define PS_UUID_DISPLAY_SERVICE 0xFFE0 /**< Test Service: Submit text to PS3 */ #define PS_UUID_DISPLAY_CHAR_1 0xFFE1 /**< Legacy: Diplay text and vibration */ #define PS_UUID_DISPLAY_CHAR_2 0xFFE2 /**< Custom Binary Images */ #define PS_UUID_DISPLAY_CHAR_3 0xFFE3 /**< Proxy for communicating with device connected to PS3 */ // -------------------------------------------------------------------------------------------------------------------- // Section: Service Discovery Structs // -------------------------------------------------------------------------------------------------------------------- #define NRF_BLE_LINK_COUNT (NRF_SDH_BLE_PERIPHERAL_LINK_COUNT + NRF_SDH_BLE_CENTRAL_LINK_COUNT) #define MAX_SERVICE_COUNT 6 #define MAX_CHARACTERISTIC_COUNT 6 // Structure storing data of all discovered services. typedef struct { ble_gattc_service_t services[MAX_SERVICE_COUNT]; /**< Data of the services found. */ uint8_t count; /**< Count of the services found. */ } m_ble_device_srv_t; typedef struct { ble_uuid_t uuid; /**< UUID of the characteristic. */ uint16_t decl_handle; /**< Handle of the characteristic declaration. */ uint16_t value_handle; /**< Handle of the characteristic value. */ uint16_t cccd_desc_handle; /**< Handle of the CCCD descriptors. */ ble_gatt_char_props_t char_props; /**< GATT Characteristic Properties. */ } m_ble_char_data_t; // Structure storing the data of all discovered characteristics. typedef struct { m_ble_char_data_t char_data[MAX_CHARACTERISTIC_COUNT]; /**< Characteristics data. */ uint8_t count; /**< Characteristics count. */ } m_ble_srv_char_t; // -------------------------------------------------------------------------------------------------------------------- // Section: Client Structs // -------------------------------------------------------------------------------------------------------------------- /**@brief time Client event type. */ typedef enum { BLE_PS_C_EVT_NOTIFICATION = 1 /**< Event indicating that a notification of the time characteristic was received from the peer. */ } ble_ps_c_evt_type_t; /**@brief Forward declaration of the ble_ps_c_t type. */ typedef struct ble_ps_c_s ble_ps_c_t; /**@brief Structure containing the handles related to the SppekSmith Service found on the peer. */ typedef struct { uint16_t legacy_handle; /**< Handle of the CCCD of the ??? characteristic. */ uint16_t custom_handle; /**< Handle of the CCCD of the ??? characteristic. */ uint16_t proxy_handle; /**< Handle of the CCCD of the ??? characteristic. */ } ble_ps_handles_t; /**@brief PeekSmith Client Structure. */ struct ble_ps_c_s { uint16_t conn_handle; /**< Connection handle as provided by the SoftDevice. */ ble_ps_handles_t ps_handles; /**< Handles related characteristics on the peer. */ nrf_ble_gq_t *p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */ }; /**@brief PeekSmith Client initialization structure. */ typedef struct { nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */ } ble_ps_c_init_t; /**@brief Display Configuration */ __ALIGN(4) typedef struct PACKED { uint8_t version; /**< Configuration Version */ char name[NRF_BLE_SCAN_NAME_MAX_LEN]; /**< Name of the device */ uint8_t name_len; /**< Length of device name */ uint8_t mac_address[BLE_GAP_ADDR_LEN]; /**< MAC Address */ bool mac_registered; /**< MAC Address Saved */ bool enabled; /**< Display client is enabled */ bool auto_connect; /**< Auto connection is enabled */ } ble_ps_config_t; #define PS_DEFAULT_NAME "PeekSmith-" #define PS_DEFAULT_NAME_LEN 10 /**@brief */ #define PEEKSMITH_CONFIG_DEFAULT { \ .name = PS_DEFAULT_NAME, \ .name_len = PS_DEFAULT_NAME_LEN, \ .mac_address = {0}, \ .mac_registered = false, \ .enabled = true, \ .auto_connect = false \ } /**@brief Function for initializing the PeekSmith client module. * * @details * * @retval NRF_SUCCESS On successful initialization. * @retval err_code Otherwise, this function propagates the error code returned by the Database Discovery module API * @ref ble_db_discovery_evt_register. */ ret_code_t m_ble_peeksmith_c_init(ble_ps_c_init_t *p_ble_ps_c_init, ble_ps_config_t **p_ps_config, bool factory_reset); /**@brief Function for handling BLE events from the SoftDevice. * * @details This function handles the BLE events received from the SoftDevice. If a BLE event * is relevant to the LED Button Client module, the function uses the event's data to update interval * variables and, if necessary, send events to the application. * * @param[in] p_ble_evt Pointer to the BLE event. * @param[in] p_context Pointer to the SBWatch client structure. */ void m_ble_peeksmith_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context); /**@brief Function for starting the service discovery process. * * @details * * @param[in] conn_handle */ ret_code_t m_ble_peeksmith_discovery_start(); /**@brief Function for handling primary service discovery response. * * @details This function will handle the primary service discovery response. * * @param[in] p_ble_gattc_evt Pointer to the GATT Client event. */ void m_ble_peeksmith_primary_discovery_rsp(ble_gattc_evt_t const *p_ble_gattc_evt); /**@brief Function for handling a characteristic discovery response. * * @param[in] p_ble_gattc_evt Pointer to the GATT Client event. */ bool m_ble_peeksmith_characteristics_discovery_rsp(ble_gattc_evt_t const * p_ble_gattc_evt); /**@brief Function for assigning handles to this instance of PeekSmith Client. * * @details * * @param[in] conn_handle Connection handle to associate with the given PeekSmith Client Instance. * * @retval NRF_SUCCESS If the status was sent successfully. * @retval err_code Otherwise, this API propagates the error code returned by function * @ref nrf_ble_gq_item_add. * */ ret_code_t m_ble_peeksmith_c_assign_handle(uint16_t conn_handle); /**@brief Function for checking if the client has a GAP connection */ bool m_ble_peeksmith_is_connected(); /**@brief Function for handling the client GAP disconnect */ ret_code_t m_ble_peeksmith_disconnect(); /**@brief Function to get the auto connection state */ bool m_ble_peeksmith_auto_conn_status(); /**@brief Function to toggle the auto connection state */ bool m_ble_peeksmith_toggle_auto_conn(); /**@brief Clear PeekSmith Screen Buffer */ void m_ble_peeksmith_buffer_clear(void); /**@brief Append PeekSmith Screen Buffer */ ret_code_t m_ble_peeksmith_buffer_append(char data, bool flush_buffer); /**@brief Load PeekSmith Screen Buffer */ ret_code_t m_ble_peeksmith_buffer_load(char* p_data, uint8_t length, bool flush_buffer); /**@brief */ ret_code_t m_ble_peeksmith_send_logo(); /**@brief */ void m_ble_peeksmith_rpt_bad_input(void); #endif /* __SM_M_BLE_PEEKSMITH_H__ */
#include <stdlib.h> #include "app_config.h" #include "ble_conn_state.h" #include "ble_gatt_db.h" #include "m_ble_peeksmith.h" #include "m_ble_peeksmith_flash.h" #include "m_haptic.h" #define NRF_LOG_MODULE_NAME ble_ps_c #define NRF_LOG_LEVEL BLE_MODULE_LOG_LEVEL #include "nrf_log.h" NRF_LOG_MODULE_REGISTER(); /**< Main structure used by the SBWatch client module. */ BLE_PEEKSMITH_C_DEF(m_ble_ps_c); // -------------------------------------------------------------------------------------------------------------------- // Section: Module Variables // -------------------------------------------------------------------------------------------------------------------- #define SRV_DISC_START_HANDLE 0x0001 /**< The start handle value used during service discovery. */ static ble_ps_config_t *m_ble_peeksmith_config; static const ble_ps_config_t m_ble_peeksmith_config_default = PEEKSMITH_CONFIG_DEFAULT; static bool m_forced_disconnect = false; static bool m_vendor_char_uuid_read = false; static bool m_vendor_uuid_read = false; static ble_gattc_handle_range_t m_handle_range; static uint16_t m_central_start_handle = 0x0001; static m_ble_device_srv_t *m_ble_device_srv[NRF_BLE_LINK_COUNT]; static m_ble_srv_char_t m_srv_char; static ret_code_t m_ble_peeksmith_flush_buffer(); // -------------------------------------------------------------------------------------------------------------------- // Section: Error Handling Methods // -------------------------------------------------------------------------------------------------------------------- /**@brief Function for handling the SBWatch Service client errors. * * @param[in] nrf_error Error code containing information about what went wrong. */ static void m_ble_peeksmith_client_error_handler(ret_code_t nrf_error) { APP_ERROR_HANDLER(nrf_error); } /**@brief Function for intercepting the errors of GATTC and the BLE GATT Queue. * * @param[in] nrf_error Error code. * @param[in] p_ctx Parameter from the event handler. * @param[in] conn_handle Connection handle. */ static void m_ble_peeksmith_gatt_error_handler(uint32_t nrf_error, void* p_ctx, uint16_t conn_handle) { ble_ps_c_t * p_ble_ps_c = (ble_ps_c_t *)p_ctx; NRF_LOG_DEBUG("A GATT Client error has occurred on conn_handle: 0X%X", conn_handle); m_ble_peeksmith_client_error_handler(nrf_error); } // -------------------------------------------------------------------------------------------------------------------- // Section: Client Methods // -------------------------------------------------------------------------------------------------------------------- /**@brief Function for assigning handles to this instance of PeekSmith Client. * * @details * * @param[in] conn_handle Connection handle to associate with the given PeekSmith Client Instance. * * @retval NRF_SUCCESS If the status was sent successfully. * @retval err_code Otherwise, this API propagates the error code returned by function * @ref nrf_ble_gq_item_add. * */ ret_code_t m_ble_peeksmith_c_assign_handle(uint16_t conn_handle) { NRF_LOG_INFO("Assigning Connection Handle, handle %d", conn_handle); m_ble_ps_c.conn_handle = conn_handle; return NRF_SUCCESS; } // -------------------------------------------------------------------------------------------------------------------- // Section: Buffer Timer Methods // -------------------------------------------------------------------------------------------------------------------- APP_TIMER_DEF(m_ble_peeksmith_buff_flush_timer_id); static bool m_ble_peeksmith_buff_flush_timer_started = false; static void m_ble_peeksmith_buff_flush_handler(void *unused) { NRF_LOG_DEBUG("PeekSmith Buffer Flush Handler Triggered!"); ret_code_t err_code; m_ble_peeksmith_buff_flush_timer_started = false; err_code = m_ble_peeksmith_flush_buffer(); if (err_code != NRF_SUCCESS) { NRF_LOG_ERROR("Unable to flush the PeekSmith buffer!"); } } static ret_code_t m_ble_peeksmith_buff_flush_timer_start() { ret_code_t err_code; if (!m_ble_peeksmith_buff_flush_timer_started) { NRF_LOG_DEBUG("PeekSmith Buffer Flush Timer Scheduled!"); err_code = app_timer_start(m_ble_peeksmith_buff_flush_timer_id, 0, NULL); if (err_code == NRF_SUCCESS) { m_ble_peeksmith_buff_flush_timer_started = true; } VERIFY_SUCCESS(err_code); } else { NRF_LOG_DEBUG("PeekSmith Buffer Flush Timer Already Scheduled!"); } return NRF_SUCCESS; } // -------------------------------------------------------------------------------------------------------------------- // Section: Buffer Methods // -------------------------------------------------------------------------------------------------------------------- #define PS_MAX_MEM_BUFFER_SIZE 35 static char m_peeksmith_buffer[PS_MAX_MEM_BUFFER_SIZE]; static uint8_t m_peeksmith_buffer_len = 0; /**@brief Flush PeekSmith Screen Buffer */ static ret_code_t m_ble_peeksmith_flush_buffer() { if (!m_ble_peeksmith_config->enabled) { NRF_LOG_INFO("PeekSmith is not enalbled!"); return NRF_ERROR_INVALID_STATE; } ble_conn_state_status_t conn_status = ble_conn_state_status(m_ble_ps_c.conn_handle); if (conn_status == BLE_CONN_STATUS_INVALID || conn_status == BLE_CONN_STATUS_DISCONNECTED) { NRF_LOG_ERROR("Failed PeekSmith connection is in a invalid state!"); return NRF_ERROR_INVALID_STATE; } if (m_peeksmith_buffer_len == 0) { return NRF_ERROR_INVALID_STATE; } if (m_peeksmith_buffer_len+1 <= PS_MAX_MEM_BUFFER_SIZE) { m_peeksmith_buffer[m_peeksmith_buffer_len] = 0x0A; m_peeksmith_buffer_len++; } else { NRF_LOG_ERROR("PeekSmith character buffer is full!"); return NRF_ERROR_INVALID_STATE; } ble_gattc_write_params_t const write_params = { .write_op = BLE_GATT_OP_WRITE_CMD, .flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE, .handle = m_ble_ps_c.ps_handles.legacy_handle, .offset = 0, .len = m_peeksmith_buffer_len, .p_value = (uint8_t*)&m_peeksmith_buffer }; return sd_ble_gattc_write(m_ble_ps_c.conn_handle, &write_params); } /**@brief Clear PeekSmith Screen Buffer */ void m_ble_peeksmith_buffer_clear(void) { memset(m_peeksmith_buffer, 0x00, PS_MAX_MEM_BUFFER_SIZE); m_peeksmith_buffer_len = 0; } /**@brief Append PeekSmith Screen Buffer */ ret_code_t m_ble_peeksmith_buffer_append(char data, bool flush_buffer) { if (m_peeksmith_buffer_len > PS_MAX_MEM_BUFFER_SIZE) { NRF_LOG_ERROR("PeekSmith character buffer is full!"); return NRF_ERROR_NO_MEM; } if (m_peeksmith_buffer_len == 0) { m_peeksmith_buffer[0] = 0x23; m_peeksmith_buffer_len++; } if (m_peeksmith_buffer_len >= 1) { if (m_peeksmith_buffer[m_peeksmith_buffer_len-1] == 0x0A) { m_peeksmith_buffer[m_peeksmith_buffer_len-1] = data; } else { m_peeksmith_buffer[m_peeksmith_buffer_len] = data; m_peeksmith_buffer_len++; } } if (flush_buffer & m_ble_peeksmith_is_connected()) { return m_ble_peeksmith_buff_flush_timer_start(); } return NRF_SUCCESS; } /**@brief Load PeekSmith Screen Buffer */ ret_code_t m_ble_peeksmith_buffer_load(char* p_data, uint8_t length, bool flush_buffer) { if (length > PS_MAX_MEM_BUFFER_SIZE) { NRF_LOG_ERROR("Data array is larger than buffer size!"); return NRF_ERROR_NO_MEM; } m_ble_peeksmith_buffer_clear(); memcpy((char**)&m_peeksmith_buffer[1], p_data, length); m_peeksmith_buffer[0] = 0x23; m_peeksmith_buffer_len = length + 1; if (flush_buffer & m_ble_peeksmith_is_connected()) { return m_ble_peeksmith_buff_flush_timer_start(); } return NRF_SUCCESS; } /**@brief */ ret_code_t m_ble_peeksmith_send_logo() { static char data[] = { ' ', 'N', 'E', 'X', 'U', 'S' }; return m_ble_peeksmith_buffer_load(data, sizeof(data), true); } /**@brief */ void m_ble_peeksmith_rpt_bad_input(void) { ret_code_t err_code; char bad_input_map[] = { 'B', 'A', 'D', ' ', 'I', 'N', 'P', 'U', 'T' }; err_code = m_ble_peeksmith_buffer_load(bad_input_map, 9, true); } // -------------------------------------------------------------------------------------------------------------------- // Section: GATT Timer Methods // -------------------------------------------------------------------------------------------------------------------- APP_TIMER_DEF(m_ble_peeksmith_gatt_svsc_timer_id); static bool m_ble_peeksmith_gatt_timer_started = false; static void m_ble_peeksmith_gatt_timer_handler(void *unused) { NRF_LOG_DEBUG("PeekSmith GATT Service Handler Triggered!"); ret_code_t err_code; m_ble_peeksmith_gatt_timer_started = false; err_code = sd_ble_gattc_primary_services_discover(m_ble_ps_c.conn_handle, m_central_start_handle, NULL); if (err_code != NRF_SUCCESS) { APP_ERROR_CHECK(err_code); } // TODO: Handle Error } static ret_code_t m_ble_peeksmith_gatt_timer_start(uint32_t mills) { ret_code_t err_code; NRF_LOG_DEBUG("PeekSmith GATT Service Timer Started!"); err_code = app_timer_start(m_ble_peeksmith_gatt_svsc_timer_id, APP_TIMER_TICKS(mills), NULL); if (err_code == NRF_SUCCESS) { m_ble_peeksmith_gatt_timer_started = true; } VERIFY_SUCCESS(err_code); } // -------------------------------------------------------------------------------------------------------------------- // Section: GATT Discovery Methods // -------------------------------------------------------------------------------------------------------------------- /**@brief Function for printing the UUID for each service. * * @param[in] conn_handle The connection handle identifying the connection to perform this procedure on. * @param[in] service_p Pointer to ble_gattc_service_t. */ static void m_ble_peeksmith_uuid_print(uint16_t conn_handle, ble_gattc_service_t const * p_service) { NRF_LOG_RAW_INFO("Found service UUIDs: \r\n"); for (uint8_t i = 0; i < m_ble_device_srv[conn_handle]->count; i++) { NRF_LOG_RAW_INFO("%s: %X %s: 0x%X\r\n", "UUID", p_service[i].uuid.uuid, "type", p_service[i].uuid.type); } } static void m_ble_peeksmith_char_print(void) { for (uint8_t i = 0; i < m_srv_char.count; i++) { ble_gatt_char_props_t const * p_char_props = &m_srv_char.char_data[i].char_props; NRF_LOG_RAW_INFO("Characteristic UUID: %X\r\n", m_srv_char.char_data[i].uuid.uuid); NRF_LOG_RAW_INFO("Parameters:\r\n"); NRF_LOG_RAW_INFO("broadcast: %d ", p_char_props->broadcast); NRF_LOG_RAW_INFO("read: %d ", p_char_props->read); NRF_LOG_RAW_INFO("write_wo_resp: %d ", p_char_props->write_wo_resp); NRF_LOG_RAW_INFO("write: %d ", p_char_props->write); NRF_LOG_RAW_INFO("notify: %d\r\n", p_char_props->notify); NRF_LOG_RAW_INFO("indicate: %d ", p_char_props->indicate); NRF_LOG_RAW_INFO("auth_signed_wr: %d\r\n", p_char_props->auth_signed_wr); } NRF_LOG_RAW_INFO("Number of characteristics: %d\r\n", m_srv_char.count); } //static void m_ble_cccd_descriptors_discovery(ble_gattc_evt_t const * p_ble_gattc_evt) { // for (uint8_t i = 0; i < m_srv_char.count; i++) { // // If it is possible to enable notification. // if ((m_srv_char.char_data[i].char_props.notify || // m_srv_char.char_data[i].char_props.indicate) && // (m_srv_char.char_data[i].cccd_desc_handle == 0)) // { // // Search for CCCD descriptor handle // cccd_descriptors_search(m_srv_char.char_data[i].uuid.uuid, p_ble_gattc_evt->conn_handle); // break; // } // } //} /**@brief Function for starting the service discovery process. * * @details * * @param[in] conn_handle */ ret_code_t m_ble_peeksmith_discovery_start() { return m_ble_peeksmith_gatt_timer_start(2000); } /**@brief Function for handling primary service discovery response. * * @details This function will handle the primary service discovery response. * * @param[in] p_ble_gattc_evt Pointer to the GATT Client event. */ void m_ble_peeksmith_primary_discovery_rsp(ble_gattc_evt_t const *p_ble_gattc_evt) { ret_code_t err_code; uint16_t count; uint16_t bytes_to_copy; static uint16_t offset = 0; //For readability. ble_gattc_evt_prim_srvc_disc_rsp_t const *p_prim_serv = &(p_ble_gattc_evt->params.prim_srvc_disc_rsp); ble_gattc_service_t *p_service = m_ble_device_srv[m_ble_ps_c.conn_handle]->services; // Number of services, currently discovered. count = p_prim_serv->count; // If no more services are found. if ((count != 0) && (p_ble_gattc_evt->gatt_status == BLE_GATT_STATUS_SUCCESS)) { if ((count + offset) > MAX_SERVICE_COUNT) { bytes_to_copy = MAX_SERVICE_COUNT - offset; } else { bytes_to_copy = count; } // Save services data. memcpy((p_service + offset), p_prim_serv->services, bytes_to_copy * sizeof(ble_gattc_service_t)); offset += count; NRF_LOG_INFO("Services Found: UUID-[%X], Type-[%d]", p_prim_serv->services[count - 1].uuid.uuid, p_prim_serv->services[count - 1].uuid.type); if (p_prim_serv->services[count - 1].uuid.uuid == PS_UUID_DISPLAY_SERVICE) { NRF_LOG_INFO("Found PeekSmith primary service!") m_handle_range.start_handle = p_prim_serv->services[count - 1].handle_range.start_handle; m_handle_range.end_handle = p_prim_serv->services[count - 1].handle_range.end_handle; err_code = sd_ble_gattc_characteristics_discover(m_ble_ps_c.conn_handle, &m_handle_range); APP_ERROR_CHECK(err_code); } else { // If the main service has not been found, this function must be called again with a new start handle. m_central_start_handle = p_prim_serv->services[count - 1].handle_range.end_handle + 1; err_code = sd_ble_gattc_primary_services_discover(m_ble_ps_c.conn_handle, m_central_start_handle, NULL); APP_ERROR_CHECK(err_code); } } else { m_ble_device_srv[m_ble_ps_c.conn_handle]->count = offset; // If service UUID type is unknown, then look for a 128-bit UUID. // Only the first one is searched for here, the rest is searched for in the @ref on_read_rsp. for (uint8_t i = 0; i < offset; i++) { if (p_service[i].uuid.type == BLE_UUID_TYPE_UNKNOWN) { m_vendor_uuid_read = true; // Read service 128-bit UUID. err_code = sd_ble_gattc_read(m_ble_ps_c.conn_handle, p_service[i].handle_range.start_handle, 0); APP_ERROR_CHECK(err_code); offset = 0; return; } } //NRF_LOG_INFO("Services count: %d", offset); //m_ble_peeksmith_uuid_print(p_ble_gattc_evt->conn_handle, p_service); offset = 0; } } /**@brief Function for handling a characteristic discovery response. * * @param[in] p_ble_gattc_evt Pointer to the GATT Client event. */ bool m_ble_peeksmith_characteristics_discovery_rsp(ble_gattc_evt_t const * p_ble_gattc_evt) { uint16_t count; static uint16_t offset = 0; uint16_t bytes_to_copy; ret_code_t err_code; // For readability. count = p_ble_gattc_evt->params.char_disc_rsp.count; ble_gattc_evt_char_disc_rsp_t const * p_char_disc_rsp_evt; p_char_disc_rsp_evt = &(p_ble_gattc_evt->params.char_disc_rsp); if (p_ble_gattc_evt->gatt_status == BLE_GATT_STATUS_SUCCESS) { if ((count + offset) > MAX_CHARACTERISTIC_COUNT) { bytes_to_copy = MAX_CHARACTERISTIC_COUNT - offset; NRF_LOG_RAW_INFO("Too many characteristics discovered\r\n"); } else { bytes_to_copy = count; } // Save characteristics data. for (uint8_t i = 0; i < bytes_to_copy; i++) { m_srv_char.char_data[i + offset].decl_handle = p_char_disc_rsp_evt->chars[i].handle_decl; m_srv_char.char_data[i + offset].value_handle = p_char_disc_rsp_evt->chars[i].handle_value; m_srv_char.char_data[i + offset].uuid = p_char_disc_rsp_evt->chars[i].uuid; m_srv_char.char_data[i + offset].char_props = p_char_disc_rsp_evt->chars[i].char_props; m_srv_char.char_data[i + offset].cccd_desc_handle = 0; switch (p_char_disc_rsp_evt->chars[i].uuid.uuid) { case PS_UUID_DISPLAY_CHAR_1: { NRF_LOG_INFO("Found PeekSmith legacy service characteristic"); m_ble_ps_c.ps_handles.legacy_handle = p_char_disc_rsp_evt->chars[i].handle_value; } break; case PS_UUID_DISPLAY_CHAR_2: { NRF_LOG_INFO("Found PeekSmith custom service characteristic"); m_ble_ps_c.ps_handles.custom_handle = p_char_disc_rsp_evt->chars[i].handle_value; } break; case PS_UUID_DISPLAY_CHAR_3: { NRF_LOG_INFO("Found PeekSmith proxy service characteristic"); m_ble_ps_c.ps_handles.proxy_handle = p_char_disc_rsp_evt->chars[i].handle_value; } break; } } offset += bytes_to_copy; } // If the last characteristic has not been reached, look for a new handle range. //ble_gattc_handle_range_t handle_range; m_handle_range.start_handle = m_srv_char.char_data[offset - 1].value_handle + 1; // Search for end handle. for (uint8_t j = 0; j < m_ble_device_srv[m_ble_ps_c.conn_handle]->count; j++) { if ((m_handle_range.start_handle > m_ble_device_srv[m_ble_ps_c.conn_handle]->services[j].handle_range.start_handle) && (m_handle_range.start_handle < m_ble_device_srv[m_ble_ps_c.conn_handle]->services[j].handle_range.end_handle)) { m_handle_range.end_handle = m_ble_device_srv[m_ble_ps_c.conn_handle]->services[j].handle_range.end_handle; break; } } // Handle value of the characteristic being discovered is less than the end handle of // the service being discovered. There is no possibility of more characteristics being // present. if ((m_srv_char.char_data[offset - 1].value_handle >= m_handle_range.end_handle) || (offset == MAX_CHARACTERISTIC_COUNT) || (p_ble_gattc_evt->gatt_status != BLE_GATT_STATUS_SUCCESS)) { m_srv_char.count = offset; offset = 0; for (uint8_t i = 0; i < m_srv_char.count; i++) { if (m_srv_char.char_data[i].uuid.type == BLE_UUID_TYPE_UNKNOWN) { m_vendor_char_uuid_read = true; // Read char 128-bit UUID. err_code = sd_ble_gattc_read(m_ble_ps_c.conn_handle, m_srv_char.char_data[i].decl_handle, 0); APP_ERROR_CHECK(err_code); return true; // TODO: Is this the correct response? } } // Print characteristic data. //m_ble_peeksmith_char_print(); // Discovery Complete return true; } // If the last Characteristic has not been reached, this function must be called again with new handle range. err_code = sd_ble_gattc_characteristics_discover(p_ble_gattc_evt->conn_handle, &m_handle_range); APP_ERROR_CHECK(err_code); return false; } // -------------------------------------------------------------------------------------------------------------------- /**@brief Function for checking if the client has a GAP connection */ /**@brief Check the status of the connection */ bool m_ble_peeksmith_is_connected() { return ble_conn_state_status(m_ble_ps_c.conn_handle) == BLE_CONN_STATUS_CONNECTED ? true : false; } /**@brief Function for handling the client GAP disconnect */ ret_code_t m_ble_peeksmith_disconnect() { ret_code_t err_code; NRF_LOG_DEBUG("Attempting to disconnect time connection."); if (m_ble_peeksmith_is_connected()) { err_code = sd_ble_gap_disconnect(m_ble_ps_c.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); if (err_code != NRF_SUCCESS) { NRF_LOG_WARNING("Failed to disconnect time connection. Connection handle: %d Error: %d", m_ble_ps_c.conn_handle, err_code); } else { NRF_LOG_DEBUG("Disconnected time connection handle %d", m_ble_ps_c.conn_handle); m_forced_disconnect = true; } return err_code; } else { NRF_LOG_DEBUG("time is not connected to remote."); return NRF_SUCCESS; } } /**@brief Function to get the auto connection state */ bool m_ble_peeksmith_auto_conn_status() { return m_ble_peeksmith_config->auto_connect; } /**@brief Function to toggle the auto connection state */ bool m_ble_peeksmith_toggle_auto_conn() { m_ble_peeksmith_config->auto_connect ^= true; if (m_ble_peeksmith_fds_update() == NRF_SUCCESS) { return true; } else { return false; } } /**@brief Function for handling the Disconnected event received from the SoftDevice. * * @details This function checks whether the disconnect event is happening on the link * associated with the current instance of the module. If the event is happening, the function sets the instance's * conn_handle to invalid. * * @param[in] p_ble_lbs_c Pointer to the Led Button Client structure. * @param[in] p_ble_evt Pointer to the BLE event received. */ static void m_ble_peeksmith_on_disconnected(ble_ps_c_t *p_ble_ps_c, ble_evt_t const *p_ble_evt) { if (p_ble_ps_c->conn_handle == p_ble_evt->evt.gap_evt.conn_handle) { p_ble_ps_c->conn_handle = BLE_CONN_HANDLE_INVALID; p_ble_ps_c->ps_handles.custom_handle = BLE_GATT_HANDLE_INVALID; p_ble_ps_c->ps_handles.legacy_handle = BLE_GATT_HANDLE_INVALID; p_ble_ps_c->ps_handles.proxy_handle = BLE_GATT_HANDLE_INVALID; NRF_LOG_DEBUG("PeekSmith Disconnected!"); m_haptic_play_feedback(HFB_ERROR); } } /**@brief Function for handling Handle Value Notification received from the SoftDevice. * * @details This function uses the Handle Value Notification received from the SoftDevice * and checks whether it is a notification of Button state from the peer. If * it is, this function decodes the state of the button and sends it to the * application. * * @param[in] p_ble_ps_c Pointer to the PeekSmith Client structure. * @param[in] p_ble_evt Pointer to the BLE event received. */ static void m_ble_peeksmith_on_hvx(ble_ps_c_t *p_ble_ps_c, ble_evt_t const *p_ble_evt) { if (p_ble_ps_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle) { return; } if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_ps_c->ps_handles.legacy_handle) { if (p_ble_evt->evt.gattc_evt.params.hvx.len >= 1) { NRF_LOG_HEXDUMP_DEBUG(p_ble_evt->evt.gattc_evt.params.hvx.data, p_ble_evt->evt.gattc_evt.params.hvx.len); } } } /**@brief Function for handling BLE events from the SoftDevice. */ void m_ble_peeksmith_c_on_ble_evt(ble_evt_t const *p_ble_evt, void *p_context) { if ((p_context == NULL) || (p_ble_evt == NULL)) { return; } ble_ps_c_t * p_ble_ps_c = (ble_ps_c_t *)p_context; switch (p_ble_evt->header.evt_id) { case BLE_GATTC_EVT_HVX: { NRF_LOG_DEBUG("NOTIFICATION Event Recieved!"); m_ble_peeksmith_on_hvx(p_ble_ps_c, p_ble_evt); } break; case BLE_GAP_EVT_CONNECTED: { NRF_LOG_DEBUG("CONNECTED Event Recieved!"); //forced_disconnect = false; } break; case BLE_GAP_EVT_DISCONNECTED: { NRF_LOG_DEBUG("DISCONNECTED Event Recieved!"); m_ble_peeksmith_on_disconnected(p_ble_ps_c, p_ble_evt); } break; default: break; } } /**@brief Initialize the PeekSmith Bluetooth Client */ ret_code_t m_ble_peeksmith_c_init(ble_ps_c_init_t *p_ble_ps_c_init, ble_ps_config_t **p_ps_config, bool factory_reset) { ret_code_t err_code; VERIFY_PARAM_NOT_NULL(p_ble_ps_c_init); VERIFY_PARAM_NOT_NULL(p_ps_config); m_forced_disconnect = false; /**< Initialize Flash */ err_code = m_ble_peeksmith_fds_init(&m_ble_peeksmith_config_default, &m_ble_peeksmith_config, factory_reset); if (err_code != NRF_SUCCESS) { NRF_LOG_ERROR("Failed to initialize time configuration flash - %d", err_code); return err_code; } *p_ps_config = m_ble_peeksmith_config; /**< Initialize Timer */ err_code = app_timer_create(&m_ble_peeksmith_gatt_svsc_timer_id, APP_TIMER_MODE_SINGLE_SHOT, m_ble_peeksmith_gatt_timer_handler); VERIFY_SUCCESS(err_code); err_code = app_timer_create(&m_ble_peeksmith_buff_flush_timer_id, APP_TIMER_MODE_SINGLE_SHOT, m_ble_peeksmith_buff_flush_handler); VERIFY_SUCCESS(err_code); m_ble_ps_c.conn_handle = BLE_CONN_HANDLE_INVALID; m_ble_ps_c.ps_handles.custom_handle = BLE_GATT_HANDLE_INVALID; m_ble_ps_c.ps_handles.legacy_handle = BLE_GATT_HANDLE_INVALID; m_ble_ps_c.ps_handles.proxy_handle = BLE_GATT_HANDLE_INVALID; m_ble_ps_c.p_gatt_queue = p_ble_ps_c_init->p_gatt_queue; m_ble_peeksmith_buffer_clear(); return NRF_SUCCESS; }
/**@brief Function for handling BLE Stack events that concern the central application. * * @details This function keeps the connection handles of the central application up-to-date. It * parses scanning reports, initiating a connection attempt to peripherals, * and manages connection parameter update requests. Additionally, it updates the status * of LEDs used to report the central application's activity. * * @note Since this function updates the connection handles, @ref BLE_GAP_EVT_DISCONNECTED events * should be dispatched to the target application before invoking this function. * * @param[in] p_ble_evt Bluetooth stack event. */ static void m_ble_central_event_handler(ble_evt_t const * p_ble_evt) { ret_code_t err_code; ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt; switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: { NRF_LOG_INFO("CENTRAL: GAP - Device Connected, handle %d", p_gap_evt->conn_handle); switch (m_current_cntrl_device) { case BLE_DEVICE_PEEKSMITH: { m_haptic_play_effect(DRV2605_EFF_LONG_DBL_CLK_STRONG_1); err_code = m_ble_peeksmith_c_assign_handle(p_gap_evt->conn_handle); APP_ERROR_CHECK(err_code); err_code = m_ble_peeksmith_discovery_start(); APP_ERROR_CHECK(err_code); } break; case BLE_DEVICE_TIME_DEVICE: { m_haptic_play_effect(DRV2605_EFF_LONG_DBL_CLK_STRONG_1); err_code = m_ble_tdevice_client_handles_assign(p_gap_evt->conn_handle, NULL); APP_ERROR_CHECK(err_code); err_code = ble_db_discovery_start(&m_db_disc[p_gap_evt->conn_handle], p_gap_evt->conn_handle); APP_ERROR_CHECK(err_code); } break; default: return; } // Assign connection handle to the QWR module. m_ble_multi_qwr_conn_handle_assign(p_ble_evt->evt.gap_evt.conn_handle); } break; case BLE_GAP_EVT_DISCONNECTED: { NRF_LOG_INFO("CENTRAL: Disconnected, handle %d, reason 0x%x.", p_ble_evt->evt.gap_evt.conn_handle, p_ble_evt->evt.gap_evt.params.disconnected.reason); switch (m_current_cntrl_device) { case BLE_DEVICE_PEEKSMITH: { // TODO: Implement PeekSmith Disconnect Event Handler } break; case BLE_DEVICE_TIME_DEVICE: { if (m_ble_tdevice_is_conn_handle(p_ble_evt->evt.gap_evt.conn_handle)) { NRF_LOG_INFO("CENTRAL: Time Device Disconnected"); if (m_tdevice_config->enabled && !m_ble_tdevice_is_connected() && !m_ble_tdevice_disconenct_forced()) { err_code = m_ble_client_auto_conn_enqueue(BLE_DEVICE_TIME_DEVICE); APP_ERROR_CHECK(err_code); } } } break; default: return; } m_leds_off(BLE_STATUS_LED); m_leds_flash(BLE_STATUS_LED, 1, false); m_ble_client_auto_conn_handler(); } break; case BLE_GAP_EVT_TIMEOUT: { // No timeout for scanning is specified, so only connection attemps can timeout. if (p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) { NRF_LOG_INFO("CENTRAL: GAP - Connection Request timed out."); } } break; case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { // Accept parameters requested by peer. err_code = sd_ble_gap_conn_param_update(p_ble_evt->evt.gap_evt.conn_handle, &p_ble_evt->evt.gap_evt.params.conn_param_update_request.conn_params); APP_ERROR_CHECK(err_code); } break; case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { NRF_LOG_DEBUG("CENTRAL: GAP - PHY update request."); ble_gap_phys_t const phys = { .rx_phys = BLE_GAP_PHY_AUTO, .tx_phys = BLE_GAP_PHY_AUTO, }; err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys); APP_ERROR_CHECK(err_code); } break; case BLE_GATTS_EVT_WRITE: { NRF_LOG_DEBUG("CENTRAL: GATT - Client Timeout."); } break; case BLE_GATTC_EVT_TIMEOUT: { NRF_LOG_DEBUG("CENTRAL: GATT - Client Timeout."); err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); APP_ERROR_CHECK(err_code); } break; case BLE_GATTS_EVT_TIMEOUT: { NRF_LOG_DEBUG("CENTRAL: GATT - Server Timeout."); err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); APP_ERROR_CHECK(err_code); } break; case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP: { NRF_LOG_DEBUG("CENTRAL: GATT - Primary Service Discovery Response."); if (m_current_cntrl_device == BLE_DEVICE_PEEKSMITH) { m_ble_peeksmith_primary_discovery_rsp(&(p_ble_evt->evt.gattc_evt)); } } break; case BLE_GATTC_EVT_CHAR_DISC_RSP: { NRF_LOG_DEBUG("CENTRAL: GATT - Characteristics Discovery Response."); if (m_current_cntrl_device == BLE_DEVICE_PEEKSMITH) { if (m_ble_peeksmith_characteristics_discovery_rsp(&(p_ble_evt->evt.gattc_evt))) { m_current_cntrl_device = BLE_DEVICE_NONE; m_leds_flash(BLE_STATUS_LED, 2, false); m_ble_peeksmith_send_logo(); if (m_advertising) { m_leds_flash(BLE_STATUS_LED, 0, false); } m_ble_client_auto_conn_handler(); } } } break; case BLE_GATTC_EVT_DESC_DISC_RSP: { NRF_LOG_DEBUG("CENTRAL: GATT - Discription Discovery Response."); //on_descriptor_discovery_rsp(&(p_ble_evt->evt.gattc_evt)); } break; default: break; } }