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

Confusion about new style of manufacturer specific data updating

Hi,

I have a project which was originally written using SDK12, and have updated it to SDK16, I am sorry I know this question seems to have been asked in many different ways already.

I have one problem left.

I need to dynamically update manufacture specific data for the advertisement, and the code below used to work fine.

I know things have changed now and I have to use the ble_advdata_encode function, but it is not clear how exactly and I cannot find any direct examples.

The searched turn up hundreds of results from the forum which are all slightly different, some use ble_advertising_init, others sd_ble_gap_adv_start and a ton inbetweeners.

The migration guide is also not clear on the matter.

I am using short names and fast advertising, I previously had the following init function and an update function - could someone indicate to me, or point me to a proper example of how I can change this code from SDK12 style to SDK16 and achieve exactly the same result with as little change as possible?

I think I have to continue to use the ble_advertising_init due to the fast advertising options, I would also prefer not to have to stop and start the advertisement every time I want to update it (i believe I will also have to update the interval times as per the change of units mentioned in the migration guide)

BLE_ADVERTISING_DEF(m_advertising); /**< Advertising module instance. */
static ble_manufacturer_data_t m_manufacturer_data;
static ble_uuid_t m_adv_uuids[] = /**< Universally unique service identifiers. */
{
{ BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE } //,
		//{BLE_UUID_CALLPOINT_SERVICE, BLE_UUID_TYPE_VENDOR_BEGIN}
};




/**@brief Function for initializing the Advertising functionality.
 */
static void advertising_init(void)
{
	ret_code_t err_code;

	ble_advdata_manuf_data_t adv_manuf_data;
	uint8_array_t adv_manuf_data_array;
	uint8_t adv_manuf_data_data[sizeof(ble_manufacturer_data_t) - 2];

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

	m_adv_init.advdata.name_type = BLE_ADVDATA_SHORT_NAME;
	m_adv_init.advdata.short_name_len = 3;
	m_adv_init.advdata.include_appearance = true;
	m_adv_init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
	m_adv_init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
	m_adv_init.advdata.uuids_complete.p_uuids = m_adv_uuids;

	// Configuration of manufacturer specific data
	memcpy((uint8_t*) adv_manuf_data_data, (uint8_t*) &m_manufacturer_data.DeviceType,
			sizeof(ble_manufacturer_data_t) - 2);

	adv_manuf_data_array.p_data = adv_manuf_data_data;
	adv_manuf_data_array.size = sizeof(ble_manufacturer_data_t) - 2;

	adv_manuf_data.company_identifier = LI_COMPANY;
	adv_manuf_data.data = adv_manuf_data_array;

	m_adv_init.advdata.p_manuf_specific_data = &adv_manuf_data;

	m_adv_init.config.ble_adv_fast_enabled = true;
	m_adv_init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
	m_adv_init.config.ble_adv_fast_timeout = APP_ADV_TIMEOUT_IN_SECONDS;

	m_adv_init.evt_handler = on_adv_evt;

	err_code = ble_advertising_init(&m_advertising, &m_adv_init);
	APP_ERROR_CHECK(err_code);

	ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}

static void update_advertisment(void)
{

	ret_code_t err_code;

	ble_advdata_manuf_data_t adv_manuf_data;
	uint8_array_t adv_manuf_data_array;
	uint8_t adv_manuf_data_data[sizeof(ble_manufacturer_data_t) - 2];

	// Configuration of manufacturer specific data
	memcpy((uint8_t*) adv_manuf_data_data, (uint8_t*) &m_manufacturer_data.DeviceType,
			sizeof(ble_manufacturer_data_t) - 2);

	adv_manuf_data_array.p_data = adv_manuf_data_data;
	adv_manuf_data_array.size = sizeof(ble_manufacturer_data_t) - 2;

	adv_manuf_data.company_identifier = LI_COMPANY;
	adv_manuf_data.data = adv_manuf_data_array;

	m_adv_init.advdata.p_manuf_specific_data = &adv_manuf_data;

	err_code = ble_advdata_set(&m_adv_init.advdata, NULL);
	APP_ERROR_CHECK(err_code);
}

Many thanks

Parents
  • It is SO much easier to update now!  Here's how I do it.  Change the options in new_data to match your init function.

    /**@brief Function for updating manufacturer data in the Advertising payload.
     */
    static void advertising_update_mfg_data(void)
    {
        ret_code_t             err_code;
    
        static ble_advdata_t new_data; // SDK 16.x.x implementation is similar to advertising_init
    
        new_data.name_type               = BLE_ADVDATA_FULL_NAME;
        new_data.include_appearance      = false;
        new_data.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
        new_data.uuids_complete.uuid_cnt = 0;
            
        // Variables used for manufacturer specific data
        ble_advdata_manuf_data_t p_manuf_specific_data;
        uint8_array_t            adv_manuf_data_array;
        uint8_t                  adv_manuf_data_data[2];
    
        // Configuration of manufacturer specific data
        adv_manuf_data_data[0] = <<<<your first byte here>>>>;
        adv_manuf_data_data[1] = <<<<your second byte here>>>>;
    
        adv_manuf_data_array.p_data = adv_manuf_data_data;
        adv_manuf_data_array.size = sizeof(adv_manuf_data_data);
    
        p_manuf_specific_data.company_identifier = BLE_COMPANY_IDENTIFIER;
        p_manuf_specific_data.data = adv_manuf_data_array;
        
        new_data.p_manuf_specific_data = &p_manuf_specific_data;
    
        //SDK16.x.x implementation will handle all buffering and encoding inside the update function
        err_code = ble_advertising_advdata_update(&m_advertising, &new_data, NULL);  
        APP_ERROR_CHECK(err_code);
    
    }

  • Hello, thanks for the solution for the newbie here. However, could you elaborate how I can use this function? Where should I locate this function?

  • Sure! 

    - Where to locate ... I'd put it right below your advertising_init() function.  Edit your advertising_init() function with the same manufacturer data as advertising_update_mfg_data().

    - How to use... depends on what manufacturer data you are advertising.  Lets say you're measuring temperature and going to send it in two bytes (say in a signed int16).  You have a timer which reads the temperature and saves it to a static location (m_temperature) every minute.  Right after you read temperature, call advertising_update_mfg_data() to copy the new reading into the advertising data.  Watch the bytes being sent in the advertisement change with temperature.

    Replacing <<<<<your first byte here>>>>>> .... would be something like:

    // Configuration of manufacturer specific data
        adv_manuf_data_data[0] = ( m_temperature >> 8 );   //high byte
        adv_manuf_data_data[1] = ( m_temperature & 0xFF ); //low byte

    - Rob

  • Hello Rob,

    I added advertising_update_mfg_data(void) function right below the advertising_init(). But it doesn't seem to update the new data by time.

    I have a beacon that advertises the current time every 187.5ms(no reason) loaded on manufacturer specific data field. I scanned this beacon through an app, but the data seemed to fixed to 590000 (0x0059 is Nordic manufacturer) and did not showed the updated second data.

    Could you look at my code? Below is advertising_init() and advertising_update_mfg_data()

    static void advertising_init(void)
    {
        ret_code_t             err_code;
        ble_advertising_init_t init;     // Struct containing advertising parameters
        // Build advertising data struct to pass into @ref ble_advertising_init.
        memset(&init, 0, sizeof(init));
        
        ble_advdata_manuf_data_t                  manuf_data; // Variable to hold manufacturer specific data
        uint8_t eg_data[]                         = {0x12, 0x34, 0x56};  // Our initial data to advertise then update with current time data.
        manuf_data.company_identifier             = 0x0059;   // Nordics company ID
        manuf_data.data.p_data                    = eg_data;
        manuf_data.data.size                      = sizeof(eg_data);
        init.advdata.p_manuf_specific_data        = &manuf_data;
        
        init.advdata.name_type               = BLE_ADVDATA_SHORT_NAME;  // Use a shortened name
        init.advdata.short_name_len          = 5; // Advertise only first 8 letters of name
        init.advdata.include_appearance      = false;
        init.advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
        init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
        init.advdata.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);
    }
    
    
    /** Function for updating Timestamp data **/
    
    static void advertising_update_mfg_data(void)
    {
        ret_code_t             err_code;
    
        static ble_advdata_t new_data; // SDK 16.x.x implementation is similar to advertising_init
    
        // Variables used for manufacturer specific data
        ble_advdata_manuf_data_t p_manuf_specific_data;
        uint8_array_t            adv_manuf_data_array;
        uint8_t                  adv_manuf_data_data[1];
        
    
    
        // Configuration of manufacturer specific data
        adv_manuf_data_data[0] = nrf_cal_get_time()->tm_sec; // For the testing purpose, I only put the second data
        
        
        adv_manuf_data_array.p_data      = adv_manuf_data_data;
        adv_manuf_data_array.size        = sizeof(adv_manuf_data_data);
    
        p_manuf_specific_data.company_identifier = 0x0059;
        p_manuf_specific_data.data       = adv_manuf_data_array; 
          
        new_data.p_manuf_specific_data   = &p_manuf_specific_data;
    
        new_data.name_type               = BLE_ADVDATA_SHORT_NAME;
        new_data.short_name_len          = 5;
        new_data.include_appearance      = false;
        new_data.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
        new_data.uuids_complete.uuid_cnt = 0;
        new_data.uuids_complete.p_uuids  = m_adv_uuids;
    
        //SDK16.x.x implementation will handle all buffering and encoding inside the update function
        err_code = ble_advertising_advdata_update(&m_advertising, &new_data, NULL);  
        APP_ERROR_CHECK(err_code);
    
    }
    

    And my main function just looks like this below.

    int main(void)
    {   
        bool erase_bonds;
        
        /** Calendar example merging starts from here **/
        uint8_t uart_byte;
        uint32_t year, month, day, hour, minute, second;
        NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
        NRF_CLOCK->TASKS_HFCLKSTART = 1;
        while(NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
        
        
        nrf_cal_init();
        nrf_cal_set_callback(calendar_updated, 4);
        uart_init();
        
        /** Ble example code starts from here **/
        // Initialize.
        timers_init();
        buttons_leds_init(&erase_bonds);
        power_management_init();
        ble_stack_init();
        gap_params_init();
    
        gatt_init();
        advertising_init();
        advertising_update_mfg_data();
        services_init();
        conn_params_init();
        peer_manager_init();
        
        // Start execution.
        //NRF_LOG_INFO("Template example started.");
        application_timers_start();
        advertising_start(erase_bonds);
        
        /** Calendar example **/
        printf("\r\nCalendar demo\r\n\n");
        printf("s - Set time\r\n");
        printf("g - Get time\r\n");
        printf("r - Run continuous time updates\r\n\n");
        
        while (true)
        {
            if(app_uart_get(&uart_byte) == NRF_SUCCESS)
            {
                switch(uart_byte)
                {
                    case 's':
                        run_time_updates = false;
                    
                        year = (uint32_t)uart_get_parameter("Enter year", 1970, 2100);
                        month = (uint32_t)uart_get_parameter("Enter month", 0, 11);
                        day = (uint32_t)uart_get_parameter("Enter day", 1, 31);
                        hour = (uint32_t)uart_get_parameter("Enter hour", 0, 23);
                        minute = (uint32_t)uart_get_parameter("Enter minute", 0, 59);
                        second = (uint32_t)uart_get_parameter("Enter second", 0, 59);
                        
                        nrf_cal_set_time(year, month, day, hour, minute, second);
                        
                        printf("Time set: ");
                        printf("%s", nrf_cal_get_time_string(false));
                        printf("\r\n\n");
                        break;
                    
                    case 'g':
                        //print_current_time();
                        printf("%d\r\n", nrf_cal_get_time()->tm_sec);
                        printf("%d\r\n", (*nrf_cal_get_time()).tm_min);
                        
                        break;
                    
                   
                }
            }
        }
    
        
      
        // Enter main loop.
        for (;;)
        {
            idle_state_handle();
        }
    }

    Also thanks for the prompt reply yesterday !

Reply
  • Hello Rob,

    I added advertising_update_mfg_data(void) function right below the advertising_init(). But it doesn't seem to update the new data by time.

    I have a beacon that advertises the current time every 187.5ms(no reason) loaded on manufacturer specific data field. I scanned this beacon through an app, but the data seemed to fixed to 590000 (0x0059 is Nordic manufacturer) and did not showed the updated second data.

    Could you look at my code? Below is advertising_init() and advertising_update_mfg_data()

    static void advertising_init(void)
    {
        ret_code_t             err_code;
        ble_advertising_init_t init;     // Struct containing advertising parameters
        // Build advertising data struct to pass into @ref ble_advertising_init.
        memset(&init, 0, sizeof(init));
        
        ble_advdata_manuf_data_t                  manuf_data; // Variable to hold manufacturer specific data
        uint8_t eg_data[]                         = {0x12, 0x34, 0x56};  // Our initial data to advertise then update with current time data.
        manuf_data.company_identifier             = 0x0059;   // Nordics company ID
        manuf_data.data.p_data                    = eg_data;
        manuf_data.data.size                      = sizeof(eg_data);
        init.advdata.p_manuf_specific_data        = &manuf_data;
        
        init.advdata.name_type               = BLE_ADVDATA_SHORT_NAME;  // Use a shortened name
        init.advdata.short_name_len          = 5; // Advertise only first 8 letters of name
        init.advdata.include_appearance      = false;
        init.advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
        init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
        init.advdata.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);
    }
    
    
    /** Function for updating Timestamp data **/
    
    static void advertising_update_mfg_data(void)
    {
        ret_code_t             err_code;
    
        static ble_advdata_t new_data; // SDK 16.x.x implementation is similar to advertising_init
    
        // Variables used for manufacturer specific data
        ble_advdata_manuf_data_t p_manuf_specific_data;
        uint8_array_t            adv_manuf_data_array;
        uint8_t                  adv_manuf_data_data[1];
        
    
    
        // Configuration of manufacturer specific data
        adv_manuf_data_data[0] = nrf_cal_get_time()->tm_sec; // For the testing purpose, I only put the second data
        
        
        adv_manuf_data_array.p_data      = adv_manuf_data_data;
        adv_manuf_data_array.size        = sizeof(adv_manuf_data_data);
    
        p_manuf_specific_data.company_identifier = 0x0059;
        p_manuf_specific_data.data       = adv_manuf_data_array; 
          
        new_data.p_manuf_specific_data   = &p_manuf_specific_data;
    
        new_data.name_type               = BLE_ADVDATA_SHORT_NAME;
        new_data.short_name_len          = 5;
        new_data.include_appearance      = false;
        new_data.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
        new_data.uuids_complete.uuid_cnt = 0;
        new_data.uuids_complete.p_uuids  = m_adv_uuids;
    
        //SDK16.x.x implementation will handle all buffering and encoding inside the update function
        err_code = ble_advertising_advdata_update(&m_advertising, &new_data, NULL);  
        APP_ERROR_CHECK(err_code);
    
    }
    

    And my main function just looks like this below.

    int main(void)
    {   
        bool erase_bonds;
        
        /** Calendar example merging starts from here **/
        uint8_t uart_byte;
        uint32_t year, month, day, hour, minute, second;
        NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
        NRF_CLOCK->TASKS_HFCLKSTART = 1;
        while(NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
        
        
        nrf_cal_init();
        nrf_cal_set_callback(calendar_updated, 4);
        uart_init();
        
        /** Ble example code starts from here **/
        // Initialize.
        timers_init();
        buttons_leds_init(&erase_bonds);
        power_management_init();
        ble_stack_init();
        gap_params_init();
    
        gatt_init();
        advertising_init();
        advertising_update_mfg_data();
        services_init();
        conn_params_init();
        peer_manager_init();
        
        // Start execution.
        //NRF_LOG_INFO("Template example started.");
        application_timers_start();
        advertising_start(erase_bonds);
        
        /** Calendar example **/
        printf("\r\nCalendar demo\r\n\n");
        printf("s - Set time\r\n");
        printf("g - Get time\r\n");
        printf("r - Run continuous time updates\r\n\n");
        
        while (true)
        {
            if(app_uart_get(&uart_byte) == NRF_SUCCESS)
            {
                switch(uart_byte)
                {
                    case 's':
                        run_time_updates = false;
                    
                        year = (uint32_t)uart_get_parameter("Enter year", 1970, 2100);
                        month = (uint32_t)uart_get_parameter("Enter month", 0, 11);
                        day = (uint32_t)uart_get_parameter("Enter day", 1, 31);
                        hour = (uint32_t)uart_get_parameter("Enter hour", 0, 23);
                        minute = (uint32_t)uart_get_parameter("Enter minute", 0, 59);
                        second = (uint32_t)uart_get_parameter("Enter second", 0, 59);
                        
                        nrf_cal_set_time(year, month, day, hour, minute, second);
                        
                        printf("Time set: ");
                        printf("%s", nrf_cal_get_time_string(false));
                        printf("\r\n\n");
                        break;
                    
                    case 'g':
                        //print_current_time();
                        printf("%d\r\n", nrf_cal_get_time()->tm_sec);
                        printf("%d\r\n", (*nrf_cal_get_time()).tm_min);
                        
                        break;
                    
                   
                }
            }
        }
    
        
      
        // Enter main loop.
        for (;;)
        {
            idle_state_handle();
        }
    }

    Also thanks for the prompt reply yesterday !

Children
  • I agree you're seeing the Nordic Semi ID, but is is actually 0x0059 (sent LSB first).  The next byte you're seeing is probably your seconds reading 0x00.

    1. I would start by commenting out your main.c line 27 (advertising_update_mfg_data();).  Confirm you see the starting data 0x123456 (in addition to the Nordic ID, that is: (0x59, 0x00, 0x12, 0x34, 0x56).

    2. Now, you want to call advertising_update_mfg_data() repeatedly after boot.  I think you'll need to add it to the callback for whatever application timer handles updating the calendar.  This should cause your seconds value to increment.

    Rob

Related