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 ?

Parents
  • I have been working on this with SDK13.   

    We want to advertise sensor data using the nRF52840 so I need to use SDK15.  

    I have been trying what you suggested and here is what works for me.

    I set the advertising interval to the same as my update interval.

    then in the timer function I call advertisement_update()

    #define NON_CONNECTABLE_ADV_INTERVAL    MSEC_TO_UNITS(2000, UNIT_0_625_MS) 
    APP_TIMER_DEF(m_update_timer_id);
    #define UPDATE_TIMER_INTERVAL			APP_TIMER_TICKS(2000)
    
    void update_timer_handler(void * p_context){
    	
    
    
    	NRF_LOG_INFO("timer handler\r\n");
     
    	advertisement_update();
    	
    	
    }
    
    uint8_t sensor_data = 0;
    
    
    static void advertisement_update(void){
     ret_code_t err_code = sd_ble_gap_adv_stop(m_adv_handle);
      raw_adv_data_buffer1[7] = 0x23;
      raw_adv_data_buffer1[8] = sensor_data;
      ++ sensor_data;
        err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &adv_data1, &m_adv_params);
        APP_ERROR_CHECK(err_code);
         err_code = sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);
        APP_ERROR_CHECK(err_code);
      }
    

    If you need more code I can clean up what I am working with and post the whole thing.  But that would be In a day or two

  • Hi Ranjeesh,

    I am developing on nrf52840 DK, S140, SDK15.2

    Assumptions: I want to update the advertisement data at every advertisement at 1.0 second interval. Assuming the data is contained within the manufacturing specific data at index 0 , right after the company identifier. I am only changing 1 byte for the sake of explanation.

    Background:

    The advertising_init(void) function contains the following for the advert data:

    memset(&advdata, 0, sizeof(advdata));


    //advdata.name_type = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance = true;
    advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;

    adv_manuf_data.data.p_data = adv_manuf_data_data;
    adv_manuf_data.data.size = sizeof(adv_manuf_data_data);
    adv_manuf_data.company_identifier = 0x0059; //Nordic's company ID
    advdata.p_manuf_specific_data = &adv_manuf_data;

    err_code = ble_advdata_encode(&advdata, m_adv_data.adv_data.p_data, &m_adv_data.adv_data.len);
    APP_ERROR_CHECK(err_code);

    --------------------------------

    the advertisement data is stored in m_adv_data pointer (array), which itself points to m_enc_advdata array at initialization:

    static ble_gap_adv_data_t m_adv_data =
    {
    .adv_data =
    {
    .p_data = m_enc_advdata,
    .len = BLE_GAP_ADV_SET_DATA_SIZE_MAX
    },
    .scan_rsp_data =
    {
    .p_data = m_enc_scan_response_data,
    .len = BLE_GAP_ADV_SET_DATA_SIZE_MAX

    }
    };

    The m_enc_advdata is defined as:

    static uint8_t m_enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX] 

    Actual part:

    General rule of thumb is to avoid updating the pointer array while it is being accessed. In our case that translates to making sure the adv data is not being accessed by advertising when we update the data. To ensure this, setup radio notifications as described here:

    https://devzone.nordicsemi.com/tutorials/b/software-development-kit/posts/radio-notification

    Put the following code in advertising_init(void) function instead of instead of what is mentioned in the link provided above:

    err_code = radio_notification_init(3, NRF_RADIO_NOTIFICATION_TYPE_INT_ON_INACTIVE, NRF_RADIO_NOTIFICATION_DISTANCE_NONE);
    APP_ERROR_CHECK(err_code);

    This is generate an interrupt at the end of each advertisement. Now we are safe to update adv_data as:

    /**@brief Software interrupt 1 IRQ Handler, handles radio notification interrupts.
    */
    void SWI1_IRQHandler(bool radio_evt)
    {
    if (radio_evt)
    {
    bsp_board_led_invert(BSP_BOARD_LED_1);   //just to provide a visual indication
    m_enc_advdata[11] += 1;
    m_adv_data.adv_data.p_data = m_enc_advdata;
    // nrf_gpio_pin_toggle(BSP_LED_2); //Toggle the status of the LED on each radio notification event
    }
    }

    This works well for me and it is really fast. There is no need to encode the adv_data again as long as you know the index of the data byte you want to update. In my case, it is the data byte right after the company identifier, which for me is index 11 of the m_enc_advdata array.

    Important

    I actually defined the m_enc_advdata array as per below since I know exactly what my advertisement data looks like:

    static uint8_t m_enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX] =
    {
    03, 0x19, 0x00, 0x00, //flags
    02, 0x01, 0x06, //apperance
    13, 0xFF, 0x59, 0x00, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 //manufacturer specific data
    };

    Then i commented out the following code in advertising_init(void) functions since I do not want to construct the advertisement data any more:

    /*
    //advdata.name_type = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance = true;
    advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;

    adv_manuf_data.data.p_data = adv_manuf_data_data;
    adv_manuf_data.data.size = sizeof(adv_manuf_data_data);
    adv_manuf_data.company_identifier = 0x0059; //Nordic's company ID
    advdata.p_manuf_specific_data = &adv_manuf_data;

    err_code = ble_advdata_encode(&advdata, m_adv_data.adv_data.p_data, &m_adv_data.adv_data.len);
    APP_ERROR_CHECK(err_code);
    */

    This allows me to use only one adv_data buffer and not have to encode the data everytime i need to update it.

    Hope this helps!

  • Hello Stratosphere, 

    Thank you for the brief explanation to your approach. I have one question; Let's assume that you define advertising data as described:

    static uint8_t m_enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX] =
    {
    03, 0x19, 0x00, 0x00, //flags
    02, 0x01, 0x06, //apperance
    13, 0xFF, 0x59, 0x00, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 //manufacturer specific data
    };

     I can do it also, since I know what my adv packet looks like. But how you cope with the initialization of the advertising instance? I mean, in the classic approach for the function advertising_init(), we have 

    ble_advertising_init_t init;
    
    
    typedef struct
    {
        ble_advdata_t           advdata;       /**< Advertising data: name, appearance, discovery flags, and more. */
        ble_advdata_t           srdata;        /**< Scan response data: Supplement to advertising data. */
        ble_adv_modes_config_t  config;        /**< Select which advertising modes and intervals will be utilized.*/
        ble_adv_evt_handler_t   evt_handler;   /**< Event handler that will be called upon advertising events. */
        ble_adv_error_handler_t error_handler; /**< Error handler that will propogate internal errors to the main applications. */
    } ble_advertising_init_t;
    

    structure, which holds all parameters for the advertising module. We should define the advertising data through the ble_advdata_t structure. How you cope with this?

Reply
  • Hello Stratosphere, 

    Thank you for the brief explanation to your approach. I have one question; Let's assume that you define advertising data as described:

    static uint8_t m_enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX] =
    {
    03, 0x19, 0x00, 0x00, //flags
    02, 0x01, 0x06, //apperance
    13, 0xFF, 0x59, 0x00, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 //manufacturer specific data
    };

     I can do it also, since I know what my adv packet looks like. But how you cope with the initialization of the advertising instance? I mean, in the classic approach for the function advertising_init(), we have 

    ble_advertising_init_t init;
    
    
    typedef struct
    {
        ble_advdata_t           advdata;       /**< Advertising data: name, appearance, discovery flags, and more. */
        ble_advdata_t           srdata;        /**< Scan response data: Supplement to advertising data. */
        ble_adv_modes_config_t  config;        /**< Select which advertising modes and intervals will be utilized.*/
        ble_adv_evt_handler_t   evt_handler;   /**< Event handler that will be called upon advertising events. */
        ble_adv_error_handler_t error_handler; /**< Error handler that will propogate internal errors to the main applications. */
    } ble_advertising_init_t;
    

    structure, which holds all parameters for the advertising module. We should define the advertising data through the ble_advdata_t structure. How you cope with this?

Children
Related