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;
}
}