This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Problem at advertisement restart with button in custom board using NUS: sd_ble_gap_adv_set_configure returns 0x4 NRF_ERROR_NO_MEM

Good morning,

I'm developing an app for NRF52832 SDK 15.3, SD 132 6.1.1 with NUS service. Moreover my app has DFU and Peer Manager with LESC JustWorks security and other FDS records, although now I'm in a more basic branch without these features so that I keep it simple in order to solve this issue. There is more information about my app in this link.

This is the issue:

My app is BLE Peripheral using NUS. It starts advertising and I can put the device to "sleep" (I just disconnect BLE, stop advertising and disable some features) with a long press of my button. This is the relevant code I'm using for disconnection:

#define APP_BLE_CONN_CFG_TAG            1                                           /**< A tag identifying the SoftDevice BLE configuration. */

#define DEVICE_NAME                     "XXX_Device v0.3"                           /**< Name of device. Will be included in the advertising data. */
#define NUS_SERVICE_UUID_TYPE           BLE_UUID_TYPE_VENDOR_BEGIN                  /**< UUID type for the Nordic UART Service (vendor specific). */

#define APP_BLE_OBSERVER_PRIO           3                                           /**< Application's BLE observer priority. You shouldn't need to modify this value. */

#define APP_ADV_INTERVAL                64                                          /**< The advertising interval (in units of 0.625 ms. This value corresponds to 40 ms). */

#define APP_ADV_DURATION                18000                                       /**< The advertising duration (180 seconds) in units of 10 milliseconds. */

#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(20, UNIT_1_25_MS)             /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(75, UNIT_1_25_MS)             /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */
#define SLAVE_LATENCY                   0                                           /**< Slave latency. */
#define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)             /**< Connection supervisory timeout (4 seconds), Supervision Timeout uses 10 ms units. */
#define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                       /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                      /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT    3                                           /**< Number of attempts before giving up the connection parameter negotiation. */

#define DEAD_BEEF                       0xDEADBEEF                                  /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */

BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT);                                   /**< BLE NUS service instance. */
NRF_BLE_GATT_DEF(m_gatt);                                                           /**< GATT module instance. */
NRF_BLE_QWR_DEF(m_qwr);                                                             /**< Context for the Queued Write module.*/
BLE_ADVERTISING_DEF(m_advertising);                                                 /**< Advertising module instance. */

static uint16_t   m_conn_handle          = BLE_CONN_HANDLE_INVALID;                 /**< Handle of the current connection. */
static uint16_t   m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3;            /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */
static ble_uuid_t m_adv_uuids[]          =                                          /**< Universally unique service identifier. */
{
    {BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}
};

/**@brief Function for stopping advertising.
 */
void ble_app_advertising_stop(void)
{
    ret_code_t err_code = NRF_SUCCESS;

    if (m_advertising.adv_handle != (uint8_t)BLE_GAP_ADV_SET_HANDLE_NOT_SET)
    {
        err_code = sd_ble_gap_adv_stop(m_advertising.adv_handle);
        if (err_code == NRF_SUCCESS){
			NRF_LOG_INFO("BLE advertising stopped");
		} else if (err_code != NRF_ERROR_INVALID_STATE){
			NRF_LOG_INFO("BLE not previously advertising");
            APP_ERROR_CHECK(err_code);
        }
        m_advertising.adv_handle = (uint8_t)BLE_GAP_ADV_SET_HANDLE_NOT_SET;
    } 
}

/**@brief Function for to disconnect from central.
 */
void ble_app_disconnect(void){
	ret_code_t err_code;

	if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
	{
		err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
		if (err_code == NRF_SUCCESS){
			NRF_LOG_INFO("BLE Disconnected");
		} else if (err_code != NRF_ERROR_INVALID_STATE){
			APP_ERROR_CHECK(err_code);
			NRF_LOG_INFO("BLE Not previously connected");
		}
	}

	ble_app_advertising_stop();
}

void button_off(){
    NRF_LOG_INFO("Device to Sleep");
    ble_app_disconnect();
    led_red_off();
    led_blue_off();
}

This part is working. The device disconnects and advertismen stops.

The next part is to start advertising again (or reconnect to device if possible) after another long press of the button. This is the code:

void ble_app_advertising_restart(void){
	ret_code_t ret;

	ret = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
	if(ret != NRF_SUCCESS))
	{
		APP_ERROR_CHECK(err_code);
	}
}

void button_press_reconnect(){
    ble_app_advertising_restart();
}

This code doesn't work and I've tracked the error to following lines of ble_advertising.c:

uint32_t ble_advertising_start(ble_advertising_t * const p_advertising,
                               ble_adv_mode_t            advertising_mode)
{
    uint32_t ret;
    
    //{...}

    if (p_advertising->adv_mode_current != BLE_ADV_MODE_IDLE)
    {

        ret = sd_ble_gap_adv_set_configure(&p_advertising->adv_handle, p_advertising->p_adv_data, &p_advertising->adv_params);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
        ret = sd_ble_gap_adv_start(p_advertising->adv_handle, p_advertising->conn_cfg_tag);

        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    }

    if (p_advertising->evt_handler != NULL)
    {
        p_advertising->evt_handler(p_advertising->adv_evt);
    }

    return NRF_SUCCESS;
}

Function sd_ble_gap_adv_set_configure in line 7 returns error 0x04, what I think is NRF_ERROR_NO_MEM. I've search all around the forum but I've found no related solution, only some about two advertisement sets. Documentation says: Not enough memory to configure a new advertising handle. Update an existing advertising handle instead.

What is weird is that, when it's connected to a central device (e.g. nRF Connect DK) and I disconnect from that central device, the module restarts advertisement by itself through "on_disconnected" function:

static void on_disconnected(ble_advertising_t * const p_advertising, ble_evt_t const * p_ble_evt)
{
    uint32_t ret;

    p_advertising->whitelist_temporarily_disabled = false;

    if (p_ble_evt->evt.gap_evt.conn_handle == p_advertising->current_slave_link_conn_handle &&
        p_advertising->adv_modes_config.ble_adv_on_disconnect_disabled == false)
    {
       ret = ble_advertising_start(p_advertising, BLE_ADV_MODE_DIRECTED_HIGH_DUTY);
       if ((ret != NRF_SUCCESS) && (p_advertising->error_handler != NULL))
       {
           p_advertising->error_handler(ret);
       }
    }
}

void ble_advertising_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
    //{...}

        // Upon disconnection, whitelist will be activated and direct advertising is started.
        case BLE_GAP_EVT_DISCONNECTED:
            on_disconnected(p_advertising, p_ble_evt);
            break;
    //{...}
}

Since this function works, and it's not about "update an existing advertising handle" as the documentation says. I've tried to modify my ble_app_advertisement_restart with the code from the on_disconnected function but didn't work. So far, my only solution was to reset the module so that I can start advertising again.

What is the problem with the 0x4 error from sd_ble_gap_adv_set_configure? Thanks in advance.

  • Hello,

    Do you use the same instance of m_advertising in your ble_app_advertising_restart?

    Try to print out the advertising handle at these points:

    1: After ble_advertising_init()

    2: Before ble_advertising_start();

    3: Before you restart your advertising in your custom ble_app_advertising_restart()

    You can print it like this:

    NRF_LOG_INFO("advertising handle: %d", m_advertising.adv_handle);

    What is the advertising handle at the different scenarios?

    BR,

    Edvin

  • Hi ,

    all my three advertising funtions (ble_advertising_init(), ble_advertising_start() and ble_app_advertising_restart()) are placed in a file, which I've named ble_app.c. On top of that file I define m_advertising instance. Here I show you this code:

    BLE_ADVERTISING_DEF(m_advertising);                                                 /**< Advertising module instance. */
    
    /**@brief Function for initializing the Advertising functionality.
     */
    void ble_app_advertising_init(void)
    {
        uint32_t               err_code;
        ble_advertising_init_t init;
    
        memset(&init, 0, sizeof(init));
    
        init.advdata.name_type          = BLE_ADVDATA_FULL_NAME;
        init.advdata.include_appearance = false;
        init.advdata.flags              = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
    
        init.srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
        init.srdata.uuids_complete.p_uuids  = m_adv_uuids;
    
        init.config.ble_adv_fast_enabled  = true;
        init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
        init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
        init.evt_handler = on_adv_evt;
    
        err_code = ble_advertising_init(&m_advertising, &init);
        APP_ERROR_CHECK(err_code);
    
        ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
    	NRF_LOG_INFO("After AdvInit, advertising handle: %d", m_advertising.adv_handle);
    }
    
    /**@brief Function for starting advertising.
     */
    void ble_app_advertising_start(void)
    {
        uint32_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
        APP_ERROR_CHECK(err_code);
    	NRF_LOG_INFO("After AdvStart, advertising handle: %d", m_advertising.adv_handle);
    }
    
    
    /**@brief Function for stopping advertising.
     */
    void ble_app_advertising_stop(void)
    {
        ret_code_t err_code = NRF_SUCCESS;
    
        if (m_advertising.adv_handle != (uint8_t)BLE_GAP_ADV_SET_HANDLE_NOT_SET)
        {
            err_code = sd_ble_gap_adv_stop(m_advertising.adv_handle);
            if (err_code == NRF_SUCCESS){
    			NRF_LOG_INFO("BLE advertising stopped");
    		} else if (err_code != NRF_ERROR_INVALID_STATE){
    			NRF_LOG_INFO("BLE not previously advertising");
                APP_ERROR_CHECK(err_code);
            }
            m_advertising.adv_handle = (uint8_t)BLE_GAP_ADV_SET_HANDLE_NOT_SET;
        } 
    }
    
    void ble_app_advertising_restart(void){
    	ret_code_t err_code;
    	NRF_LOG_INFO("Before AdvRestart, advertising handle: %d", m_advertising.adv_handle);
    	err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
    	if(err_code != NRF_SUCCESS)
    	{
    		APP_ERROR_CHECK(err_code);
    	}
    }

    Here is the result that you requested:

    <info> app: After AdvInit, advertising handle: 0
    
    <info> app: After AdvStart, advertising handle: 0
    
    <info> app: Before AdvRestart, advertising handle: 255
    
    <error> app: Fatal error

    It is the same result if there is a connection with a central device and if there is not (in this case the module is advertising which I stop with ble_app_advertising_stop).

  • So you set the advertising handle to 0xFF when you stop advertising. Is there a particular reason for this? I suspect that if you comment out this line in ble_app_advertising_stop it will work as you intend:

            m_advertising.adv_handle = (uint8_t)BLE_GAP_ADV_SET_HANDLE_NOT_SET;

  • Of course, it makes sense. Thanks! Truth be told, I don't know what is that line doing there. I guess the previous developer copied that from some example without knowing.

    I have removed it and now I can restart advertising... only when not connected to the central device.

    This is the log of that situation:

    Button ON-OFF pressed
    
    Sleep!
    
    <info> app: BLE Disconnected
    
    <info> app: Disconnected
    
    Button ON-OFF released 
    
    Button ON-OFF pressed 
    
    Wake up!
    
    Button ON-OFF released 
    
    <info> app: Before AdvRestart, advertising handle: 0
    
    <error> app: Fatal error

    I've tried to change the code in my ble_app_disconnect() function to only stop advertising when not connected, but result was the same.

    void ble_app_disconnect(void){
    	ret_code_t err_code;
    
    	if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
    	{
    		err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
    		if (err_code == NRF_SUCCESS){
    			NRF_LOG_INFO("BLE Disconnected");
    		} else if (err_code != NRF_ERROR_INVALID_STATE){
    			APP_ERROR_CHECK(err_code);
    			NRF_LOG_INFO("BLE Not previously connected");
    		}
    	} else {
    		ble_app_advertising_stop();
    	}
    }

    With my debug lines I see that when advertising/disconnected, my log prints <info> app: BLE advertising stopped, from function ble_app_advertising_stop. When not advertising/connected, it prints <info> app: BLE Disconnected from function ble_app_disconnect.

    This seems to be an additional problem. Any idea? If you need any other debug information please tell me.

  • Whenever you see "Fatal error" in your log, you should go to your preprocessor definitions and add "DEBUG" as a preprocessor define. If you do this, then the log will tell you what APP_ERROR_CHECK(err_code) that received an err_code != 0. Try to do that. If you are not sure how to change your preprocessor definitions, please let me know what IDE you are using.

    I guess you are trying to restart advertising twice, and the last one returns NRF_ERROR_INVALID_STATE (8).

    I can only guess what might happen, but the fastest way is for you to check what causes the fatal error by checking the log with the DEBUG preprocessor define

Related