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

Advertise battery level in beacon advertising packet

1. 
2. 
3. 


  1. I am using the ble_app_beacon example as the starting point for my project. I am working on a project where a number of beacons will advertise its identity and battery level to the central node. My question is : how to modify the beacon advertising packet to contain battery level information along with the unique device ID? Which portion of the code in this example shall I modify? I am trying to optimize this device for optimal battery performance as well.
  2. Also how can I read this advertising packet in the central node? What is the relevant example in the SDK to achieve this. I want these nodes to be read as soon as they are detected and then sent over to another module via UART?
  3. I have read somewhere that beacon is public and hence there are security issues involved. Can I instead use connectionless advertising and what is the relevant example in the SDK to achieve this?. I see most of the ble example assumes connection between the peripheral and central devices but this feature is irrelevant to my project.

I am using SDK 17.

I would highly appreciate any help or suggestions. Thank you so much.

  • Hello,

    Attached below is a modified version of the ble_app_beacon I made earlier to demonstrate how to update advertising payload on-the-fly. This may be a better starting point since you will need to update the packet every time there is a change in battery level.  You may also be interested in the demo sample I uploaded in this post: https://devzone.nordicsemi.com/f/nordic-q-a/71940/low-power-mode-using-saadc-with-ble_app_beacon  in case you're going to use the SAADC to sample the battery voltage.

    1. Each beacon will have it's own unique Bluetooth address so I guess you could use that as I unique identifier. The Bluetooth address will be derived from FICR.DEVICEADDR[n] unless you are explicitly setting a different in Bluetooth address in your code. You also have the APP_MAJOR_VALUE and APP_MINOR_VALUE fields in the beacon payload which you can use for identification.

    2. We do not have a Beacon scanner example included in the SDK, but here is some code may help you get started:

    /**
     * Copyright (c) 2014 - 2020, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    /**
     * @brief BLE LED Button Service central and client application main file.
     *
     * This file contains the source code for a sample client application using the LED Button service.
     */
    
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    #include "nrf_sdh.h"
    #include "nrf_sdh_ble.h"
    #include "nrf_sdh_soc.h"
    #include "nrf_pwr_mgmt.h"
    #include "app_timer.h"
    #include "boards.h"
    #include "bsp.h"
    #include "bsp_btn_ble.h"
    #include "ble.h"
    #include "ble_hci.h"
    #include "ble_advertising.h"
    #include "ble_conn_params.h"
    #include "ble_db_discovery.h"
    #include "nrf_ble_gatt.h"
    #include "nrf_ble_scan.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    
    #define CENTRAL_SCANNING_LED            BSP_BOARD_LED_0                     /**< Scanning LED will be on when the device is scanning. */
    
    
    #define APP_BLE_OBSERVER_PRIO           3                                   /**< Application's BLE observer priority. You shouldn't need to modify this value. */
    
    NRF_BLE_SCAN_DEF(m_scan);                                                   /**< Scanning module instance. */
    
    
    /**@brief Function to handle asserts in the SoftDevice.
     *
     * @details This function will be called in case of an assert in the SoftDevice.
     *
     * @warning This handler is an example only and does not fit a final product. You need to analyze
     *          how your product is supposed to react in case of Assert.
     * @warning On assert from the SoftDevice, the system can only recover on reset.
     *
     * @param[in] line_num     Line number of the failing ASSERT call.
     * @param[in] p_file_name  File name of the failing ASSERT call.
     */
    void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
    {
        app_error_handler(0xDEADBEEF, line_num, p_file_name);
    }
    
    
    
    /**@brief Function for the LEDs initialization.
     *
     * @details Initializes all LEDs used by the application.
     */
    static void leds_init(void)
    {
        bsp_board_init(BSP_INIT_LEDS);
    }
    
    
    /**@brief Function to start scanning.
     */
    static void scan_start(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_ble_scan_start(&m_scan);
        APP_ERROR_CHECK(err_code);
        bsp_board_led_on(CENTRAL_SCANNING_LED);
    }
    
    
    /**@brief Function for initializing the BLE stack.
     *
     * @details Initializes the SoftDevice and the BLE event interrupts.
     */
    static void ble_stack_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_sdh_enable_request();
        APP_ERROR_CHECK(err_code);
    
        // Configure the BLE stack using the default settings.
        // Fetch the start address of the application RAM.
        uint32_t ram_start = 0;
        err_code = nrf_sdh_ble_default_cfg_set(1, &ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Enable BLE stack.
        err_code = nrf_sdh_ble_enable(&ram_start);
        APP_ERROR_CHECK(err_code);
    }
    
    
    static void on_adv_report(ble_gap_evt_adv_report_t const * p_adv_report)
    {
        uint16_t length;
        uint16_t offset;
        
        length = ble_advdata_search(p_adv_report->data.p_data,
                                    p_adv_report->data.len,
                                    &offset,
                                    BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA);
        
    	/* Print manufacturer data if found */
        if (length > 0)
        {
            NRF_LOG_INFO("Data from manufacturer field");
            NRF_LOG_HEXDUMP_INFO(&p_adv_report->data.p_data[offset], length);
        }
    
        }
    }
    
    
    
    /**@brief Function for handling Scaning events.
     *
     * @param[in]   p_scan_evt   Scanning event.
     */
    static void scan_evt_handler(scan_evt_t const * p_scan_evt)
    {
    
        switch(p_scan_evt->scan_evt_id)
        {
            case NRF_BLE_SCAN_EVT_NOT_FOUND:
                on_adv_report(p_scan_evt->params.p_not_found);
                break;
            default:
              break;
        }
    }
    
    
    
    
    
    /**@brief Function for initializing the log.
     */
    static void log_init(void)
    {
        ret_code_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    }
    
    
    /**@brief Function for initializing the timer.
     */
    static void timer_init(void)
    {
        ret_code_t err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for initializing the Power manager. */
    static void power_management_init(void)
    {
        ret_code_t err_code;
        err_code = nrf_pwr_mgmt_init();
        APP_ERROR_CHECK(err_code);
    }
    
    
    static void scan_init(void)
    {
        ret_code_t          err_code;
        nrf_ble_scan_init_t init_scan;
        ble_gap_scan_params_t params;
    
        memset(&params, 0, sizeof(params));
    
        params.active  = 1;
        params.interval = NRF_BLE_SCAN_SCAN_INTERVAL;
        params.window = NRF_BLE_SCAN_SCAN_WINDOW;
        params.timeout = NRF_BLE_SCAN_SCAN_DURATION;
        params.filter_policy = BLE_GAP_SCAN_FP_ACCEPT_ALL;
        params.scan_phys = NRF_BLE_SCAN_SCAN_PHY;
    
        memset(&init_scan, 0, sizeof(init_scan));
    
        init_scan.conn_cfg_tag     = 1;
        init_scan.p_scan_param     = &params;
    
        err_code = nrf_ble_scan_init(&m_scan, &init_scan, scan_evt_handler);
        APP_ERROR_CHECK(err_code);
    
    }
    
    
    
    /**@brief Function for handling the idle state (main loop).
     *
     * @details Handle any pending log operation(s), then sleep until the next event occurs.
     */
    static void idle_state_handle(void)
    {
        NRF_LOG_FLUSH();
        nrf_pwr_mgmt_run();
    }
    
    
    int main(void)
    {
        // Initialize.
        log_init();
        timer_init();
        leds_init();
        power_management_init();
        ble_stack_init();
        scan_init();
    
        // Start execution.
        NRF_LOG_INFO("Beacon scanner example started.");
        scan_start();
    
        // Turn on the LED to signal scanning.
        bsp_board_led_on(CENTRAL_SCANNING_LED);
    
        // Enter main loop.
        for (;;)
        {
            idle_state_handle();
        }
    }

    You can copy paste this into the main.c file for the ble_central->ble_app_blinky_c example to try it out. This code should simply print the the manufacturer data for every beacon it finds.

    3. There is no encryption so everyone can read the advertising payload. Is that a security concern for your application?

    Attachment

    3058.nRF5_SDK17.0.2_ble_app_beacon_update.zip

    Best regards,

    Vidar

    Edit: There is a bug in the attached example that can lead to invalid data in the manufacturer field. The fix is to add the 'static' keyword to the manuf_specific_data variable defintion in advertising_init():

     static ble_advdata_manuf_data_t manuf_specific_data;

  • Thank you so much for the reply, Vidar. I tried the scanner example given here. It doesn't readily read the surrounding beacon after I compile and download it. It says "Beacon scanner example started." and then no beacon information is displayed. Only for once though (I don't know exactly why, I waited it out though), it displayed "Data from manufacturer field" and yet it only displayed information from one beacon. Do I have to change some configuration or rewrite the code so that more than one beacon information is displayed? Can you please suggest what configuration I must change or what code changes I must do? I guess the beacon information is advertised in hex (hence in binary) that's why it is scanned and read as hex. What if the beacon advertisement packet contained the text? Would this example then display the text as it is or would still display in hex regardless? I am asking this because this information will be used by the end-user.

  • You can use our nRF connect app on Android or iOS to inspect the Beacon payloads. Have you tried that to see if all of your beacons have the same packet format (i.e. do they all include the manufcaturer field)?

    ysuleiman said:
    What if the beacon advertisement packet contained the text? Would this example then display the text as it is or would still display in hex regardless?

    I'm only printing out the hex values of the manufacturer field inside the beacon payload. If you have text strings in the payload, then you will need to parse the packet first to find out where the string is located.

  • Thanks for the reply Vidar. The beacon information is read by nRF Connect Android app as soon as the beacons are supplied power. But the scanner example that you have given in the first reply doesn't readily read these beacons while it seems to be reading the beacon data from surrounding beacons from various wearables (I am guessing). But surprisingly it read one of my beacons for just one time and I don't know what made it read it. Can you please show me the right direction to pursue? Why doesn't this scanner example thoroughly read the my beacons as nRF Connect does?

  • Sorry, I had forgotten to zero initialize the offset argument in on_adv_report(). This is neccessary to make ble_advdata_search() start parsing the adv. paylod from from the beginning.

    /**
     * Copyright (c) 2014 - 2020, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    /**
     * @brief BLE LED Button Service central and client application main file.
     *
     * This file contains the source code for a sample client application using the LED Button service.
     */
    
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    #include "nrf_sdh.h"
    #include "nrf_sdh_ble.h"
    #include "nrf_sdh_soc.h"
    #include "nrf_pwr_mgmt.h"
    #include "app_timer.h"
    #include "boards.h"
    #include "bsp.h"
    #include "bsp_btn_ble.h"
    #include "ble.h"
    #include "ble_hci.h"
    #include "ble_advertising.h"
    #include "ble_conn_params.h"
    #include "ble_db_discovery.h"
    #include "nrf_ble_gatt.h"
    #include "nrf_ble_scan.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    
    #define CENTRAL_SCANNING_LED            BSP_BOARD_LED_0                     /**< Scanning LED will be on when the device is scanning. */
    #define APP_COMPANY_IDENTIFIER          {0x59, 0x00}                              /**< Company identifier for Nordic Semiconductor ASA. as per www.bluetooth.org. */
    
    #define APP_BLE_OBSERVER_PRIO           3                                   /**< Application's BLE observer priority. You shouldn't need to modify this value. */
    
    NRF_BLE_SCAN_DEF(m_scan);                                                   /**< Scanning module instance. */
    
    
    
    typedef PACKED_STRUCT
    {
        uint8_t device_type;
        uint8_t app_adv_data_length;
        ble_uuid128_t beacon_uuid;
        uint8_t  major_value[2];
        uint8_t  minor_value[2];
        uint8_t measured_rssi;
    } beacon_info_t;
    
    static beacon_info_t m_beacon_info;
    
    
    /**@brief Function to handle asserts in the SoftDevice.
     *
     * @details This function will be called in case of an assert in the SoftDevice.
     *
     * @warning This handler is an example only and does not fit a final product. You need to analyze
     *          how your product is supposed to react in case of Assert.
     * @warning On assert from the SoftDevice, the system can only recover on reset.
     *
     * @param[in] line_num     Line number of the failing ASSERT call.
     * @param[in] p_file_name  File name of the failing ASSERT call.
     */
    void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
    {
        app_error_handler(0xDEADBEEF, line_num, p_file_name);
    }
    
    
    
    /**@brief Function for the LEDs initialization.
     *
     * @details Initializes all LEDs used by the application.
     */
    static void leds_init(void)
    {
        bsp_board_init(BSP_INIT_LEDS);
    }
    
    
    /**@brief Function to start scanning.
     */
    static void scan_start(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_ble_scan_start(&m_scan);
        APP_ERROR_CHECK(err_code);
        bsp_board_led_on(CENTRAL_SCANNING_LED);
    }
    
    
    /**@brief Function for initializing the BLE stack.
     *
     * @details Initializes the SoftDevice and the BLE event interrupts.
     */
    static void ble_stack_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_sdh_enable_request();
        APP_ERROR_CHECK(err_code);
    
        // Configure the BLE stack using the default settings.
        // Fetch the start address of the application RAM.
        uint32_t ram_start = 0;
        err_code = nrf_sdh_ble_default_cfg_set(1, &ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Enable BLE stack.
        err_code = nrf_sdh_ble_enable(&ram_start);
        APP_ERROR_CHECK(err_code);
    }
    
    
    static void on_adv_report(ble_gap_evt_adv_report_t const * p_adv_report)
    {
        uint16_t length;
        uint16_t offset=0;
        uint16_t major_value;
    
        uint8_t company_identifier[] = APP_COMPANY_IDENTIFIER;
        
        length = ble_advdata_search(p_adv_report->data.p_data,
                                    p_adv_report->data.len,
                                    &offset,
                                    BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA);
        
    	/* Print manufacturer data if found */
        if (length > 0)
        {
            if (!(memcmp(&p_adv_report->data.p_data[offset], company_identifier, sizeof(company_identifier))))
            {
                memcpy(&m_beacon_info, &p_adv_report->data.p_data[offset + sizeof(company_identifier)],
                    sizeof(m_beacon_info));
    
                major_value = (m_beacon_info.major_value[1]) | ( m_beacon_info.major_value[0] << 8);
                NRF_LOG_INFO("Major value: 0x%x", major_value);
            }
        }
    
    }
    
    
    
    /**@brief Function for handling Scaning events.
     *
     * @param[in]   p_scan_evt   Scanning event.
     */
    static void scan_evt_handler(scan_evt_t const * p_scan_evt)
    {
    
        switch(p_scan_evt->scan_evt_id)
        {
            case NRF_BLE_SCAN_EVT_NOT_FOUND:
                on_adv_report(p_scan_evt->params.p_not_found);
                break;
            default:
              break;
        }
    }
    
    
    
    
    
    /**@brief Function for initializing the log.
     */
    static void log_init(void)
    {
        ret_code_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    }
    
    
    /**@brief Function for initializing the timer.
     */
    static void timer_init(void)
    {
        ret_code_t err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for initializing the Power manager. */
    static void power_management_init(void)
    {
        ret_code_t err_code;
        err_code = nrf_pwr_mgmt_init();
        APP_ERROR_CHECK(err_code);
    }
    
    
    static void scan_init(void)
    {
        ret_code_t          err_code;
        nrf_ble_scan_init_t init_scan;
        ble_gap_scan_params_t params;
    
        memset(&params, 0, sizeof(params));
    
        params.active  = 1;
        params.interval = NRF_BLE_SCAN_SCAN_INTERVAL;
        params.window = NRF_BLE_SCAN_SCAN_WINDOW;
        params.timeout = NRF_BLE_SCAN_SCAN_DURATION;
        params.filter_policy = BLE_GAP_SCAN_FP_ACCEPT_ALL;
        params.scan_phys = NRF_BLE_SCAN_SCAN_PHY;
    
        memset(&init_scan, 0, sizeof(init_scan));
    
        init_scan.conn_cfg_tag     = 1;
        init_scan.p_scan_param     = &params;
    
        err_code = nrf_ble_scan_init(&m_scan, &init_scan, scan_evt_handler);
        APP_ERROR_CHECK(err_code);
    
    }
    
    
    
    /**@brief Function for handling the idle state (main loop).
     *
     * @details Handle any pending log operation(s), then sleep until the next event occurs.
     */
    static void idle_state_handle(void)
    {
        NRF_LOG_FLUSH();
        nrf_pwr_mgmt_run();
    }
    
    
    int main(void)
    {
        // Initialize.
        log_init();
        timer_init();
        leds_init();
        power_management_init();
        ble_stack_init();
        scan_init();
    
        // Start execution.
        NRF_LOG_INFO("Beacon scanner example started.");
        scan_start();
    
        // Turn on the LED to signal scanning.
        bsp_board_led_on(CENTRAL_SCANNING_LED);
    
        // Enter main loop.
        for (;;)
        {
            idle_state_handle();
        }
    }

Related