Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Dynamically updating Advertising data in SDK 15

(side note : it would have been handy if the migration guide mentioned that the advertising timeout configs have changed from seconds to 10's of ms)

It seems to have become harder to dynamically update the advertising packet in SDK15. In SDK14, using ble_advertising, I could just update the manufacturer data used by my instance of ble_advertising_t and call ble_advdata_set and be done. Then whenever ble_advertising did something later, it would continue with the latest data (eg auto restart advertising, change speed etc). Advertising would not be restarted and if you use fast and slow advertising, then that timing and transition would not be affected if you changed the manufacturer data.

Now, we need to use sd_ble_gap_adv_set_configure instead of ble_advdata_set, but the new call wont allow changing the encoded data contents and using the same buffer. You need to provide a new buffer if its busy advertising. Providing a new long lived buffer for each update causes many other issues. One way to deal with this is to stop and restart advertising with the same but updated buffer. But that affects the fast to slow advertising timing and transitions : say you update the data every 1 minute, and your fast advertising goes slow after 2 minutes, then you'll always be in fast mode.

Is there an easy and minimally impactful way to update just the manufacturer data in SDK15 ?

  • I was beginning to have the same sentiments as you, regarding the need to provide a new buffer to update the advertising data while advertising, until I read through the s140_nrf52_6.0.0_migration_document.pdf. You should be able to find this in your <sdk>/components/softdevice/s140/doc folder.

    It seems that you are to be expected to have 2 sets of buffers allocated in order to update your advertising data on-the-fly.

    Here is the code snippet from the migration doc...

    static uint8_t raw_adv_data_buffer1[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
    static uint8_t raw_scan_rsp_data_buffer1[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
    static ble_gap_adv_data_t adv_data1 = {
        .adv_data.p_data = raw_adv_data_buffer1,
        .adv_data.len = sizeof(raw_adv_data_buffer1),
        .scan_rsp_data.p_data = raw_scan_rsp_data_buffer1,
        .scan_rsp_data.len = sizeof(raw_scan_rsp_data_buffer1)
    };
    
    /* A second advertising data buffer for later updating advertising data while advertising */
    static uint8_t raw_adv_data_buffer2[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
    static uint8_t raw_scan_rsp_data_buffer2[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
    static ble_gap_adv_data_t adv_data2 = {
        .adv_data.p_data = raw_adv_data_buffer2,
        .adv_data.len = sizeof(raw_adv_data_buffer2),
        .scan_rsp_data.p_data = raw_scan_rsp_data_buffer2,
        .scan_rsp_data.len = sizeof(raw_scan_rsp_data_buffer2)
    };
    
    int main(void)
    {
        uint8_t adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
        ble_gap_adv_params_t adv_params = {
            .properties = {
                .type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED
            },
            .interval = BLE_GAP_ADV_INTERVAL_MAX,
            .duration = BLE_GAP_ADV_TIMEOUT_LIMITED_MAX,
            .channel_mask = {0}, /* Advertising on all the primary channels */
            .max_adv_evts = 0,
            .filter_policy = BLE_GAP_ADV_FP_ANY,
            .primary_phy = BLE_GAP_PHY_AUTO,
            .scan_req_notification = 1
        };
        /* Enable the BLE Stack */
        sd_ble_enable(...);
        [...]
        sd_ble_gap_adv_set_configure(&adv_handle, &adv_data1, &adv_params);
        /* Start advertising */
        sd_ble_gap_adv_start(adv_handle, BLE_CONN_CFG_TAG_DEFAULT);
        [...]
        /* Update advertising data while advertising */
        sd_ble_gap_adv_set_configure(&adv_handle, &adv_data2, NULL);
        [...]
        /* Stop advertising */
        sd_ble_gap_adv_stop(adv_handle);
        [...]
    }

    I am still in the middle of migrating so I have not had a chance to implement this solution and try it out.

  • I have not seen that doc before (only the online version). That is somewhat helpful but that approach wont work well if you are using the ble_advertising module.

    Switching buffers yourself means that when ble_advertising gets to do the next thing (like changing from fast to slow advertising), it will use its own buffer which will be old and out of sync of your new buffer.

    Also, I suspect when you switch buffers, you'll get a BLE_GAP_EVT_ADV_SET_TERMINATED event for the previous buffer thats now released, and that event will cause ble_advertising to go to the next mode.

    Basically, where before you could easily use ble_advertising and update the packet, now thats not possible. You'll need to remove the use of ble_advertising and roll your own. 

  • that approach wont work well if you are using the ble_advertising module.

    I believe you are correct - I found this out myself when trying to implement the dual buffers. You would have to write your own callback handler and perform the necessary actions. I'm plodding forward with my migration and implementation of this still and will provide some more feedback when I get around to testing it.

    Any Nordic people able to chime in with some words of wisdom?

  • Well, I have not been able to successfully get the advertising data to update dynamically. I was originally using the ble_advertising module as my API and tried updating the contents of m_advertising.adv_data between 2 unique structures pointing to different encoding buffers. But any subsequent calls to sd_ble_gap_adv_set_configure(...) resulted in an error code of NRF_ERROR_INVALID_PARAM.

    I then decided to remove all calls to the ble_advertising API and replace it with direct ble_gap calls, and ended up with the exact same error when calling _set_configure(...).

    static uint8_t enc_adv_data_buffer1[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
    static uint8_t enc_adv_data_buffer2[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
    
    
    #define ADV_BUFFERS_USED  	(2)
    static ble_gap_adv_data_t m_gapData[ADV_BUFFERS_USED] = 
    {
    	{ 
    		.adv_data.p_data = enc_adv_data_buffer1,
    		.adv_data.len = sizeof(enc_adv_data_buffer1),
    		.scan_rsp_data.p_data = NULL,
    		.scan_rsp_data.len = 0
    	},
    	{ 
    		.adv_data.p_data = enc_adv_data_buffer2,
    		.adv_data.len = sizeof(enc_adv_data_buffer2),
    		.scan_rsp_data.p_data = NULL,
    		.scan_rsp_data.len = 0
    	},
    };
    
    void updateAdvertisingData(void)
    {
        static uint8_t uiBuffIndex = 0;
        
    	// Encode the advertising packet to the other buffer
    	errCode = ble_advdata_encode( &advData, m_gapData[ uiBuffIndex ].adv_data.p_data, (uint16_t *)&m_gapData[ uiBuffIndex ].adv_data.len );
    	APP_ERROR_CHECK(errCode);
    	
    	errCode = sd_ble_gap_adv_set_configure(&m_advHandle, &m_gapData[ uiBuffIndex ], NULL);
    	APP_ERROR_CHECK( errCode );
    		
    	if( errCode == NRF_SUCCESS )
    	{
    		uiBuffIndex++;
    		uiBuffIndex = uiBuffIndex % ADV_BUFFERS_USED;
    	}
    }
    

    The function updateAdvertisingData() is called periodically to update the advertising data.

    None of the examples update data dynamically, so I am not sure if this sequence is exactly what is expected.

Related