This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Major problems with cts_c

Hi

I'm having major problems implementing current time service client (cts_c). I've run ble_app_cts_c on my device and it works fine. I copied the relevant parts of the code into my own project, but I get the following behavior:

  1. ble_cts_c works fine on the first power one/directly after pairing. I can push button four on the PCA10040 and I get a printout of the correct date and time.

  2. One the next power on, I always get error code 5 (NRF_ERROR_NOT_FOUND) from advertising_start(). I don't get any errors in the init functions.

  3. My other services (battery service and a custom service) no longer update parameters on nRFConnect. Timeout handlers, data simulators, ect. still work fine. sd_ble_gatts_hvx returns error code 8, which is NRF_ERROR_INVALID_STATE. According to documentation, this could indicate one of three things: Invalid Connection State, Notifications and/or indications not enabled in the CCCD, or An ATT_MTU exchange is ongoing. I have enabled notifications in nRFConnect for this service, so that's not the problem. The device is already paired/bonded at this point (cts_c works), so I don't think that is the issue. What is an ATT_MTU exchange?

I'm happy to upload my project or any other code snippets that would be of use. I'm having trouble debugging because there's about 200 lines of code that have to be put in together to enable ble_cts_c, many of which are soft device related.

NRF52832 (PCA10040) SDK13 nRFConnect for iOS

The following are the functions that I have added or heavily modified according to ble_app_cts_c:

current_time_print()

peer_list_get():

static void peer_list_get(pm_peer_id_t * p_peers, uint32_t * p_size)
{
    pm_peer_id_t peer_id;
    uint32_t     peers_to_copy;

    peers_to_copy = (*p_size < BLE_GAP_WHITELIST_ADDR_MAX_COUNT) ?
                     *p_size : BLE_GAP_WHITELIST_ADDR_MAX_COUNT;

    peer_id = pm_next_peer_id_get(PM_PEER_ID_INVALID);
    *p_size = 0;

    while ((peer_id != PM_PEER_ID_INVALID) && (peers_to_copy--))
    {
        p_peers[(*p_size)++] = peer_id;
        peer_id = pm_next_peer_id_get(peer_id);
    }
}

advertising_start():

void advertising_start(bool erase_bonds)
{
    if (erase_bonds == true)
    {
        delete_bonds();
        // Advertising is started by PM_EVT_PEERS_DELETE_SUCCEEDED event.
    }
    else
    {
        ret_code_t err_code;


        memset(m_whitelist_peers, PM_PEER_ID_INVALID, sizeof(m_whitelist_peers));
		m_whitelist_peer_cnt = (sizeof(m_whitelist_peers) / sizeof(pm_peer_id_t));

		peer_list_get(m_whitelist_peers, &m_whitelist_peer_cnt);

		err_code = pm_whitelist_set(m_whitelist_peers, m_whitelist_peer_cnt);
		APP_ERROR_CHECK(err_code);

		// Setup the device identies list.
		// Some SoftDevices do not support this feature.
		err_code = pm_device_identities_list_set(m_whitelist_peers, m_whitelist_peer_cnt);
		if (err_code != NRF_ERROR_NOT_SUPPORTED)
		{
			APP_ERROR_CHECK(err_code);
		}

		m_is_wl_changed = false;


        err_code = ble_advertising_start(BLE_ADV_MODE_FAST); //BLE_ADV_MODE_FAST
        APP_ERROR_CHECK(err_code);
    }
}

advertising_start():

void advertising_start(bool erase_bonds)
{
    if (erase_bonds == true)
    {
        delete_bonds();
        // Advertising is started by PM_EVT_PEERS_DELETE_SUCCEEDED event.
    }
    else
    {
        ret_code_t err_code;


        memset(m_whitelist_peers, PM_PEER_ID_INVALID, sizeof(m_whitelist_peers));
		m_whitelist_peer_cnt = (sizeof(m_whitelist_peers) / sizeof(pm_peer_id_t));

		peer_list_get(m_whitelist_peers, &m_whitelist_peer_cnt);

		err_code = pm_whitelist_set(m_whitelist_peers, m_whitelist_peer_cnt);
		APP_ERROR_CHECK(err_code);

		// Setup the device identies list.
		// Some SoftDevices do not support this feature.
		err_code = pm_device_identities_list_set(m_whitelist_peers, m_whitelist_peer_cnt);
		if (err_code != NRF_ERROR_NOT_SUPPORTED)
		{
			APP_ERROR_CHECK(err_code);
		}

		m_is_wl_changed = false;


        err_code = ble_advertising_start(BLE_ADV_MODE_FAST); //BLE_ADV_MODE_FAST
        APP_ERROR_CHECK(err_code);
    }
}

pm_evt_handler():

static void pm_evt_handler(pm_evt_t const * p_evt)
{
    ret_code_t err_code;

    switch (p_evt->evt_id)
    {
        case PM_EVT_BONDED_PEER_CONNECTED:
        {
            NRF_LOG_INFO("Connected to a previously bonded device.\r\n");
        } break;

        case PM_EVT_CONN_SEC_SUCCEEDED:
		{
			NRF_LOG_INFO("Connection secured: role: %d, conn_handle: 0x%x, procedure: %d.\r\n",
						 ble_conn_state_role(p_evt->conn_handle),
						 p_evt->conn_handle,
						 p_evt->params.conn_sec_succeeded.procedure);

			m_peer_id = p_evt->peer_id;

			// Discover peer's services.
			memset(&m_ble_db_discovery, 0x00, sizeof(m_ble_db_discovery));
			err_code  = ble_db_discovery_start(&m_ble_db_discovery, p_evt->conn_handle);
			APP_ERROR_CHECK(err_code);

			err_code = pm_peer_rank_highest(p_evt->peer_id);
			if (err_code != NRF_ERROR_BUSY)
			{
				APP_ERROR_CHECK(err_code);
			}

			// Note: You should check on what kind of white list policy your application should use.
			if (p_evt->params.conn_sec_succeeded.procedure == PM_LINK_SECURED_PROCEDURE_BONDING)
			{
				NRF_LOG_DEBUG("New Bond, add the peer to the whitelist if possible\r\n");
				NRF_LOG_DEBUG("\tm_whitelist_peer_cnt %d, MAX_PEERS_WLIST %d\r\n",
							   m_whitelist_peer_cnt + 1,
							   BLE_GAP_WHITELIST_ADDR_MAX_COUNT);
				NRF_LOG_PROCESS();
				NRF_LOG_FLUSH();
				if (m_whitelist_peer_cnt < BLE_GAP_WHITELIST_ADDR_MAX_COUNT)
				{
					// Bonded to a new peer, add it to the whitelist.
					m_whitelist_peers[m_whitelist_peer_cnt++] = m_peer_id;
					m_is_wl_changed = true;
				}
			}
		} break;

^^^^^^I'm leaving out other cases that aren't relevant here.

db_disc_handler():

static void db_disc_handler(ble_db_discovery_evt_t * p_evt)
{
    ble_cts_c_on_db_disc_evt(&m_cts_c, p_evt);
}

sec_req_timeout_handler():

static void sec_req_timeout_handler(void * p_context)
{
    ret_code_t           err_code;
    pm_conn_sec_status_t status;
    NRF_LOG_DEBUG("sec_req_timeout_handler called");
    NRF_LOG_PROCESS();
    NRF_LOG_FLUSH();
    if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
    {
        err_code = pm_conn_sec_status_get(m_conn_handle, &status);
        APP_ERROR_CHECK(err_code);

        // If the link is still not secured by the peer, initiate security procedure.
        if (!status.encrypted)
        {
            err_code = pm_conn_secure(m_conn_handle, false);
            APP_ERROR_CHECK(err_code);
        }
    }
}

on_cts_c_evt(): static void on_cts_c_evt(ble_cts_c_t * p_cts, ble_cts_c_evt_t * p_evt) { ret_code_t err_code;

    switch (p_evt->evt_type)
    {
        case BLE_CTS_C_EVT_DISCOVERY_COMPLETE:
            NRF_LOG_INFO("Current Time Service discovered on server.\r\n");
            err_code = ble_cts_c_handles_assign(&m_cts_c,
                                                p_evt->conn_handle,
                                                &p_evt->params.char_handles);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_CTS_C_EVT_DISCOVERY_FAILED:
            NRF_LOG_INFO("Current Time Service not found on server. \r\n");
            // CTS not found in this case we just disconnect. There is no reason to stay
            // in the connection for this simple app since it all wants is to interact with CT
            if (p_evt->conn_handle != BLE_CONN_HANDLE_INVALID)
            {
                err_code = sd_ble_gap_disconnect(p_evt->conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
            }
            break;

        case BLE_CTS_C_EVT_DISCONN_COMPLETE:
            NRF_LOG_INFO("Disconnect Complete.\r\n");
            break;

        case BLE_CTS_C_EVT_CURRENT_TIME:
            NRF_LOG_INFO("Current Time received.\r\n");
            current_time_print(p_evt);
            break;

        case BLE_CTS_C_EVT_INVALID_TIME:
            NRF_LOG_INFO("Invalid Time received.\r\n");
            break;

        default:
            break;
    }
}

on_adv_evt():

static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
    ret_code_t err_code;

    switch (ble_adv_evt)
    {
        case BLE_ADV_EVT_FAST:
            NRF_LOG_INFO("Fast advertising.\r\n");
            err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_ADV_EVT_FAST_WHITELIST:
			NRF_LOG_INFO("Fast advertising with WhiteList\r\n");
			err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_WHITELIST);
			APP_ERROR_CHECK(err_code);
			break;

        case BLE_ADV_EVT_WHITELIST_REQUEST:
		{
			ble_gap_addr_t whitelist_addrs[BLE_GAP_WHITELIST_ADDR_MAX_COUNT];
			ble_gap_irk_t  whitelist_irks[BLE_GAP_WHITELIST_ADDR_MAX_COUNT];
			uint32_t       addr_cnt = BLE_GAP_WHITELIST_ADDR_MAX_COUNT;
			uint32_t       irk_cnt  = BLE_GAP_WHITELIST_ADDR_MAX_COUNT;

			err_code = pm_whitelist_get(whitelist_addrs, &addr_cnt,
										whitelist_irks,  &irk_cnt);
			APP_ERROR_CHECK(err_code);
			NRF_LOG_DEBUG("pm_whitelist_get returns %d addr in whitelist and %d irk whitelist\r\n",
						   addr_cnt,
						   irk_cnt);

			// Apply the whitelist.
			err_code = ble_advertising_whitelist_reply(whitelist_addrs, addr_cnt,
													   whitelist_irks,  irk_cnt);
			APP_ERROR_CHECK(err_code);
		}
		break;

        case BLE_ADV_EVT_IDLE:
            sleep_mode_enter();
            break;

        default:
            break;
    }
}

on_ble_evt():

static void on_ble_evt(ble_evt_t * p_ble_evt)
{
    ret_code_t err_code;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            NRF_LOG_INFO("Connected.\r\n");
            err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
            APP_ERROR_CHECK(err_code);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            err_code = app_timer_start(m_sec_req_timer_id, SECURITY_REQUEST_DELAY, NULL);
            APP_ERROR_CHECK(err_code);
            break; // BLE_GAP_EVT_CONNECTED

        case BLE_GAP_EVT_DISCONNECTED:
            NRF_LOG_INFO("Disconnected, reason %d.\r\n",
                          p_ble_evt->evt.gap_evt.params.disconnected.reason);
            m_conn_handle = BLE_CONN_HANDLE_INVALID;

            if (p_ble_evt->evt.gap_evt.conn_handle == m_cts_c.conn_handle)
			{
				m_cts_c.conn_handle = BLE_CONN_HANDLE_INVALID;
			}

			if (m_is_wl_changed)
			{
				// The whitelist has been modified, update it in the Peer Manager.
				err_code = pm_whitelist_set(m_whitelist_peers, m_whitelist_peer_cnt);
				APP_ERROR_CHECK(err_code);

				err_code = pm_device_identities_list_set(m_whitelist_peers, m_whitelist_peer_cnt);
				if (err_code != NRF_ERROR_NOT_SUPPORTED)
				{
					APP_ERROR_CHECK(err_code);
				}

				m_is_wl_changed = false;
			}
            break; // BLE_GAP_EVT_DISCONNECTED

        case BLE_GATTC_EVT_TIMEOUT:
            // Disconnect on GATT Client timeout event.
            NRF_LOG_DEBUG("GATT Client Timeout.\r\n");
            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; // BLE_GATTC_EVT_TIMEOUT

        case BLE_GATTS_EVT_TIMEOUT:
            // Disconnect on GATT Server timeout event.
            NRF_LOG_DEBUG("GATT Server Timeout.\r\n");
            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; // BLE_GATTS_EVT_TIMEOUT

        case BLE_EVT_USER_MEM_REQUEST:
            err_code = sd_ble_user_mem_reply(m_conn_handle, NULL);
            APP_ERROR_CHECK(err_code);
            break; // BLE_EVT_USER_MEM_REQUEST

        case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
        {
            ble_gatts_evt_rw_authorize_request_t  req;
            ble_gatts_rw_authorize_reply_params_t auth_reply;

            req = p_ble_evt->evt.gatts_evt.params.authorize_request;

            if (req.type != BLE_GATTS_AUTHORIZE_TYPE_INVALID)
            {
                if ((req.request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ)     ||
                    (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) ||
                    (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL))
                {
                    if (req.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
                    {
                        auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
                    }
                    else
                    {
                        auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
                    }
                    auth_reply.params.write.gatt_status = APP_FEATURE_NOT_SUPPORTED;
                    err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle,
                                                               &auth_reply);
                    APP_ERROR_CHECK(err_code);
                }
            }
        } break; // BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST

        case BLE_GAP_EVT_CONN_PARAM_UPDATE:
        {
        } break;

        default:
            break;
    }
}

bsp_evt_handler(): where I call ble_cts_c_current_time_read();

ble_evt_dispatch():

static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
    ble_conn_state_on_ble_evt(p_ble_evt);
    pm_on_ble_evt(p_ble_evt);
    ble_bas_on_ble_evt(&m_bas, p_ble_evt);
    ble_conn_params_on_ble_evt(p_ble_evt);
    bsp_btn_ble_on_ble_evt(p_ble_evt);
    on_ble_evt(p_ble_evt);
    ble_advertising_on_ble_evt(p_ble_evt);
    nrf_ble_gatt_on_ble_evt(&m_gatt, p_ble_evt);
    ble_degree_on_ble_evt(&m_degree, p_ble_evt);
    ble_cts_c_on_ble_evt(&m_cts_c, p_ble_evt);
    ble_db_discovery_on_ble_evt(&m_ble_db_discovery, p_ble_evt);
}

db_discovery_init():

static void db_discovery_init(void)
{
   ret_code_t err_code = ble_db_discovery_init(db_disc_handler);
   APP_ERROR_CHECK(err_code);
}

peer_manager_init():

static void peer_manager_init(void)
{
    ble_gap_sec_params_t sec_param;
    ret_code_t           err_code;

    err_code = pm_init();
    APP_ERROR_CHECK(err_code);

    memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));

    // Security parameters to be used for all security procedures.
    sec_param.bond           = SEC_PARAM_BOND;
    sec_param.mitm           = SEC_PARAM_MITM;
    sec_param.mitm			 = SEC_PARAM_LESC;
    sec_param.keypress		 = SEC_PARAM_KEYPRESS;
    sec_param.io_caps        = SEC_PARAM_IO_CAPABILITIES;
    sec_param.oob            = SEC_PARAM_OOB;
    sec_param.min_key_size   = SEC_PARAM_MIN_KEY_SIZE;
    sec_param.max_key_size   = SEC_PARAM_MAX_KEY_SIZE;
    sec_param.kdist_own.enc  = 1;
    sec_param.kdist_own.id   = 1;
    sec_param.kdist_peer.enc = 1;
    sec_param.kdist_peer.id  = 1;

    err_code = pm_sec_params_set(&sec_param);
    APP_ERROR_CHECK(err_code);

    err_code = pm_register(pm_evt_handler);
    APP_ERROR_CHECK(err_code);

    err_code = fds_register(fds_evt_handler);
    APP_ERROR_CHECK(err_code);
}

advertising_init():

static void advertising_init(void)
{
    ret_code_t             err_code;
    ble_advdata_t          advdata;
    ble_adv_modes_config_t options;

    ble_uuid_t m_adv_uuids[] = { //{BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE},
                                       {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}};
                                       //{DEGREE_UUID_SERVICE, m_degree.uuid_type}}; /**< Universally unique service identifiers. */

    // Build advertising data struct to pass into @ref ble_advertising_init.
    memset(&advdata, 0, sizeof(advdata));

    advdata.name_type               = BLE_ADVDATA_SHORT_NAME; //BLE_ADVDATA_FULL_NAME;
    advdata.short_name_len          = 1;
    advdata.include_appearance      = true;
    advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE; //BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    advdata.uuids_complete.p_uuids  = m_adv_uuids;
    advdata.uuids_solicited.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    advdata.uuids_solicited.p_uuids  = m_adv_uuids;

    memset(&options, 0, sizeof(options));
    options.ble_adv_whitelist_enabled   = true;
    options.ble_adv_fast_enabled        = true;
    options.ble_adv_fast_interval       = APP_ADV_FAST_INTERVAL;
    options.ble_adv_fast_timeout        = APP_ADV_FAST_TIMEOUT;

    ble_advdata_t srdata; //scan response data
    memset(&srdata, 0, sizeof(srdata));
    srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    srdata.uuids_complete.p_uuids = m_adv_uuids;

    err_code = ble_advertising_init(&advdata, NULL, &options, on_adv_evt, NULL);
    APP_ERROR_CHECK(err_code);

    ble_advertising_conn_cfg_tag_set(CONN_CFG_TAG);
}
    1. Are you sure that NRF_ERROR_NOT_FOUND is returned from advertising_start()? in the SDK example it looks like this is only returned if the Current Time Service is not discovered, when you try to read it.

    2. ATT_MTU exchange is a procedure to change the size of the ATT MTU's, i.e. increase the data payload length.

    Are you reconnecting to a known, already bonded peer? Do you successfully encrypt in this case? Any sniffer log?

  • I've changed this code so much since posting this question that I can't check where NRF_ERROR_NOT_FOUND is returned from.

    I'm developing on a mac so I don't have the sniffer since it's only windows

    I've decided to port my code to sdk14 since this project hit a dead end; with that in mind I'll close this question.

Related