BLE battery level server(peripheral)

Hi,

currently I am working for a ble battery level indication server. For that I followed one example called ble_app_bps.

And I configured  for battery level simulation. My question here is how can I send a battery level counter value(for example 0% to 20%) to the nrf connect app in smartphone?

means the real sensor(or battery level) value ,not the simulated value Please help me in this case.

//ble peripheral as advertiser/battery level Indication server


//header files
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#include "app_timer.h"
#include "bsp_btn_ble.h"

#include "nrf_pwr_mgmt.h"

#include "nrf_sdh.h"
#include "nrf_sdh_ble.h"
#include "nrf_sdh_soc.h"

#include "nrf_ble_qwr.h"
#include "nrf_ble_gatt.h"

#include "ble_advdata.h"
#include "ble_advertising.h"

#include "ble_bas.h"
#include "sensorsim.h"
#include "ble_srv_common.h"
#include "ble_conn_params.h"

//macros
#define APP_BLE_CONN_CFG_TAG  1
#define APP_BLE_OBSERVER_PRIO 3

#define DEVICE_NAME         "Satyabrata_BLE"
#define MIN_CON_INTERVAL    MSEC_TO_UNITS(100,UNIT_1_25_MS)//minimum conn interval(7.5ms to 4.0 secs)
#define MAX_CON_INTERVAL    MSEC_TO_UNITS(200,UNIT_1_25_MS)//maximum conn interval(7.5 ms to 4.0 secs)
#define SLAVE_LATENCY       0                              //no of times the slave skips the conn interval
#define CONN_SUP_TIMEOUT    MSEC_TO_UNITS(2000,UNIT_10_MS)//time between two succsessful conn events(100ms to 32s)

#define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)  
#define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)
#define MAX_CONN_PARAMS_UPDATE_COUNT    3

#define APP_ADV_INTERVAL    300
#define APP_ADV_DURATION    0

#define BATTERY_LEVEL_MEAS_INTERVAL     APP_TIMER_TICKS(2000)                   /**< Battery level measurement interval (ticks). */
#define MIN_BATTERY_LEVEL               81                                      /**< Minimum battery level as returned by the simulated measurement function. */
#define MAX_BATTERY_LEVEL               100                                     /**< Maximum battery level as returned by the simulated measurement function. */
#define BATTERY_LEVEL_INCREMENT         1                                       /**< Value by which the battery level is incremented/decremented for each call to the simulated measurement function. */




NRF_BLE_QWR_DEF(m_qwr);
NRF_BLE_GATT_DEF(m_gatt);
BLE_ADVERTISING_DEF(m_advertising);

APP_TIMER_DEF(m_battery_timer_id);                                      /**< Battery timer. */
BLE_BAS_DEF(m_bas);                                                     /**< Structure used to identify the battery service. */




//private variables
static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;
static sensorsim_cfg_t      m_battery_sim_cfg;                          /**< Battery Level sensor simulator configuration. */
static sensorsim_state_t    m_battery_sim_state;                        /**< Battery Level sensor simulator state. */



static ble_uuid_t m_adv_uuids[] =                                       /**< Universally unique service identifiers. */
{
    
    {BLE_UUID_BATTERY_SERVICE,            BLE_UUID_TYPE_BLE}
};




//private functions

//step-9 gatt initialization
static void gatt_init()
{
ret_code_t err_code = nrf_ble_gatt_init(&m_gatt,NULL);
APP_ERROR_CHECK(err_code);
}



//step-8 gap initialization
static void gap_params_init(void)
{
ret_code_t err_code;
ble_gap_conn_params_t    gap_conn_params;
ble_gap_conn_sec_mode_t  sec_mode;


BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
err_code = sd_ble_gap_device_name_set(&sec_mode,(const uint8_t *)DEVICE_NAME,strlen(DEVICE_NAME));
APP_ERROR_CHECK(err_code);

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

gap_conn_params.min_conn_interval = MIN_CON_INTERVAL;
gap_conn_params.max_conn_interval = MAX_CON_INTERVAL;
gap_conn_params.slave_latency     = SLAVE_LATENCY;
gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;

err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
APP_ERROR_CHECK(err_code);


}



//step-13 error handler for queue writer
static void nrf_qwr_error_handler(uint32_t nrf_error)
{

APP_ERROR_HANDLER(nrf_error);

}


//step-12 initialize services
static void services_init(void)
{

ret_code_t err_code;
ble_bas_init_t     bas_init;
nrf_ble_qwr_init_t qwr_init = {0};

qwr_init.error_handler = nrf_qwr_error_handler;
err_code = nrf_ble_qwr_init(&m_qwr,&qwr_init);
APP_ERROR_CHECK(err_code);

  // Initialize Battery Service.
    memset(&bas_init, 0, sizeof(bas_init));

    // Here the sec level for the Battery Service can be changed/increased.
    bas_init.bl_rd_sec        = SEC_OPEN;
    bas_init.bl_cccd_wr_sec   = SEC_OPEN;
    bas_init.bl_report_rd_sec = SEC_OPEN;

    bas_init.evt_handler          = NULL;
    bas_init.support_notification = true;
    bas_init.p_report_ref         = NULL;
    bas_init.initial_batt_level   = 100;

    err_code = ble_bas_init(&m_bas, &bas_init);
    APP_ERROR_CHECK(err_code);


}


//step-16 create an event handler for connection parameters update
static void on_conn_params_evt(ble_conn_params_evt_t *p_evt)
{
ret_code_t err_code;
if(p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
{
err_code = sd_ble_gap_disconnect(m_conn_handle,BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
APP_ERROR_CHECK(err_code);
}

if(p_evt->evt_type == BLE_CONN_PARAMS_EVT_SUCCEEDED)
{

}

}

//step-15 create an error handler for connection parameters update

static void conn_params_error_handler(uint32_t nrf_error)
{

APP_ERROR_HANDLER(nrf_error);
}





//step-14 connection params init
static void conn_params_init(void)
{
ret_code_t err_code;

ble_conn_params_init_t cp_init;
memset(&cp_init,0,sizeof(cp_init));
cp_init.p_conn_params  = NULL;
cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
cp_init.start_on_notify_cccd_handle    = BLE_GATT_HANDLE_INVALID;
cp_init.disconnect_on_fail             = false;
cp_init.error_handler = conn_params_error_handler;
cp_init.evt_handler   = on_conn_params_evt;

err_code = ble_conn_params_init(&cp_init);
APP_ERROR_CHECK(err_code);


}


//step-11 create advertisement event handler
static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
ret_code_t err_code;

switch(ble_adv_evt)
{
case BLE_ADV_EVT_FAST:
NRF_LOG_INFO("fast advertising....");
err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
APP_ERROR_CHECK(err_code);
break;

case BLE_ADV_EVT_IDLE:
err_code = bsp_indication_set(BSP_INDICATE_IDLE);
APP_ERROR_CHECK(err_code);

break;

default:
break;

}

}




//step-10 advertising init
static void advertising_init(void)
{
ret_code_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 = true;
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);
}



//step-7 ble event handler
static void ble_evt_handler(ble_evt_t const *p_ble_evt,void *p_context)
{
ret_code_t err_code = NRF_SUCCESS;
switch(p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("device is disconnected");
break;

case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("device is connected");
err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
APP_ERROR_CHECK(err_code);

m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr,m_conn_handle);
APP_ERROR_CHECK(err_code);
break;



case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
NRF_LOG_DEBUG("phy update request");
ble_gap_phys_t const phys = 
{
.rx_phys = BLE_GAP_PHY_AUTO,
.tx_phys = BLE_GAP_PHY_AUTO,
};

err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle,&phys);
APP_ERROR_CHECK(err_code);

break;



}//switch case end

}





//step-6 ble stack init
static void ble_stack_init()
{
ret_code_t err_code;
err_code = nrf_sdh_enable_request();
APP_ERROR_CHECK(err_code);


uint32_t ram_start = 0;

err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG,&ram_start);
APP_ERROR_CHECK(err_code);

err_code = nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);

NRF_SDH_BLE_OBSERVER(m_ble_observer,APP_BLE_OBSERVER_PRIO,ble_evt_handler,NULL);

}





//step-4 init power management
static void pwr_management_init(void)
{
ret_code_t err_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(err_code);

}




//step-3 init bsp leds for ble 
static void leds_init(void)
{
ret_code_t err_code = bsp_init(BSP_INIT_LEDS,NULL);
APP_ERROR_CHECK(err_code);
}



/**@brief Function for performing battery measurement and updating the Battery Level characteristic
 *        in Battery Service.
 */
static void battery_level_update(void)
{
    ret_code_t err_code;
    uint8_t  battery_level=0;

   // battery_level = (uint8_t)sensorsim_measure(&m_battery_sim_state, &m_battery_sim_cfg);
      battery_level = battery_level + 1;
      if(battery_level == 8) battery_level = 0;
    err_code = ble_bas_battery_level_update(&m_bas, battery_level, BLE_CONN_HANDLE_ALL);
    if ((err_code != NRF_SUCCESS) &&
        (err_code != NRF_ERROR_INVALID_STATE) &&
        (err_code != NRF_ERROR_RESOURCES) &&
        (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
       )
    {
        APP_ERROR_HANDLER(err_code);
    }
}




/**@brief Function for handling the Battery measurement timer timeout.
 *
 * @details This function will be called each time the battery level measurement timer expires.
 *
 * @param[in]   p_context   Pointer used for passing some arbitrary information (context) from the
 *                          app_start_timer() call to the timeout handler.
 */
static void battery_level_meas_timeout_handler(void * p_context)
{
    UNUSED_PARAMETER(p_context);
    battery_level_update();
}



//step-2 init app timer
static void timers_init(void)
{
ret_code_t err_code = app_timer_init();
APP_ERROR_CHECK(err_code);

 // Create timers.
    err_code = app_timer_create(&m_battery_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                battery_level_meas_timeout_handler);
    APP_ERROR_CHECK(err_code);

}




//1st step initialize the logger
static void log_init()
{
ret_code_t err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
}


//step-5 idle state handle
static void idle_state_handle(void)
{
if(NRF_LOG_PROCESS() == false)
{
   nrf_pwr_mgmt_run();
}

}


//step-17 start the advertisement
static void advertising_start(void)
{

ret_code_t err_code = ble_advertising_start(&m_advertising,BLE_ADV_MODE_FAST);
APP_ERROR_CHECK(err_code);
}


/**@brief Function for initializing the sensor simulators.
 */
static void sensor_simulator_init(void)
{
    m_battery_sim_cfg.min          = MIN_BATTERY_LEVEL;
    m_battery_sim_cfg.max          = MAX_BATTERY_LEVEL;
    m_battery_sim_cfg.incr         = BATTERY_LEVEL_INCREMENT;
    m_battery_sim_cfg.start_at_max = true;

    sensorsim_init(&m_battery_sim_state, &m_battery_sim_cfg);


}





/**@brief Function for starting application timers.
 */
static void application_timers_start(void)
{
    ret_code_t err_code;

    // Start application timers.
    err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
    APP_ERROR_CHECK(err_code);
}

/**@brief Function for application main entry.
 */
int main(void)
{
   log_init();//done
   timers_init();//done
   //leds_init();//done

  pwr_management_init();//done

  ble_stack_init();//done
  gap_params_init();//done
  gatt_init();//done
  advertising_init();//done
  services_init();//done
  //sensor_simulator_init();//done
  conn_params_init();//done

  NRF_LOG_INFO("Battery Level Server application started.....");
  application_timers_start();//done
  advertising_start();//done


 //enter main loop  
for(;;)
{
    idle_state_handle();
}
}



Here is the main.c file attached.

SDK - nrf5_SDK_17.1.0

DK - nrf52840

IDE - Segger embedded studio

Thanks & Regards

Satyabrata Senapati

Parents
  • Hi!

    You send the value with ble_bas_battery_level_update().

    Take a look at the ble_app_proximity example to see how it done there, the ADC is used there.

  • Satyabrata Senapati said:
    Actually when I am sending some static battery level value using ble_bas_battery_level_update() ,it is displaying perfectly .But its not working for a counter value lets for testing purpose.means simply it should display the counter value

    1) Could you post some pictures that shows how it display things when it's working , and when it's not working.

    2) And also post some code snippet that shows how you are doing this.

  • ok. Let me send.

    This is the screen shot of nrf_connect app.

    And the code snippet is given below 

    /**@brief Function for performing battery measurement and updating the Battery Level characteristic
    * in Battery Service.
    */
    static void battery_level_update(void)
    {
    ret_code_t err_code;
    uint8_t battery_level;

    // battery_level = (uint8_t)sensorsim_measure(&m_battery_sim_state, &m_battery_sim_cfg);
    // sensorsim_increment(&m_battery_sim_state, &m_battery_sim_cfg);
    //battery_level = battery_level + 1;
    // if(battery_level == 8) battery_level = 0;
    battery_level = 10;
    err_code = ble_bas_battery_level_update(&m_bas, battery_level, BLE_CONN_HANDLE_ALL);
    if ((err_code != NRF_SUCCESS) &&
    (err_code != NRF_ERROR_INVALID_STATE) &&
    (err_code != NRF_ERROR_RESOURCES) &&
    (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
    )
    {
    APP_ERROR_HANDLER(err_code);
    }
    }


    /**@brief Function for handling the Battery measurement timer timeout.
    *
    * @details This function will be called each time the battery level measurement timer expires.
    *
    * @param[in] p_context Pointer used for passing some arbitrary information (context) from the
    * app_start_timer() call to the timeout handler.
    */
    static void battery_level_meas_timeout_handler(void * p_context)
    {
    UNUSED_PARAMETER(p_context);
    battery_level_update();
    }


    //step-2 init app timer
    static void timers_init(void)
    {
    ret_code_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);

    // Create timers.
    err_code = app_timer_create(&m_battery_timer_id,
    APP_TIMER_MODE_REPEATED,
    battery_level_meas_timeout_handler);
    APP_ERROR_CHECK(err_code);

    }


    /**@brief Function for starting application timers.
    */
    static void application_timers_start(void)
    {
    ret_code_t err_code;

    // Start application timers.
    err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
    APP_ERROR_CHECK(err_code);
    }

    And in main()


    /**@brief Function for application main entry.
    */
    int main(void)
    {
    log_init();//done
    timers_init();//done
    //leds_init();//done

    pwr_management_init();//done

    ble_stack_init();//done
    gap_params_init();//done
    gatt_init();//done
    advertising_init();//done
    services_init();//done
    //sensor_simulator_init();//done
    conn_params_init();//done

    NRF_LOG_INFO("Battery Level Server application started.....");
    application_timers_start();//done
    advertising_start();//done


    //enter main loop
    for(;;)
    {
    idle_state_handle();
    }
    }

    Thats it.

  • It looks to be working.

    You are sending battery_level = 10

    battery_level = 10;
    err_code = ble_bas_battery_level_update(&m_bas, battery_level, BLE_CONN_HANDLE_ALL);

    and this is also shown in the app

  • Hi sigurd, 

    Actually this is working. But my case is if I am sending the counter value its not working. Its not showing the counted value in nrfconnect app.you can see I have commented in the code snippet. 

    //battery_level = battery_level + 1;
    // if(battery_level == 8) battery_level = 0;

Reply Children
  • Hi Satyabrata, 

    Sigurd is on vacation so sorry for the long waiting time here. 

    Have you had any progress or do you still require help ? 

    Regards,
    Jonathan

  • ok sir no problem.

    Actually I need some clarification for the above discussion.

    could you please help.

    Thanks & Regards

    Satyabrata Senapati

  • Hi!

    Are you still having issues with the counter value?

    If yes, could you post your main.c file?

  • yes,

    I am sending the main.c file.

    //ble peripheral as advertiser/battery level Indication server
    
    
    //header files
    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #include "app_timer.h"
    #include "bsp_btn_ble.h"
    
    #include "nrf_pwr_mgmt.h"
    
    #include "nrf_sdh.h"
    #include "nrf_sdh_ble.h"
    #include "nrf_sdh_soc.h"
    
    #include "nrf_ble_qwr.h"
    #include "nrf_ble_gatt.h"
    
    #include "ble_advdata.h"
    #include "ble_advertising.h"
    
    #include "ble_bas.h"
    #include "sensorsim.h"
    #include "ble_srv_common.h"
    #include "ble_conn_params.h"
    
    
    #include "peer_manager.h"
    #include "peer_manager_handler.h"
    
    //macros
    #define APP_BLE_CONN_CFG_TAG  1
    #define APP_BLE_OBSERVER_PRIO 3
    
    #define DEVICE_NAME         "VASMED_BLE"
    #define MIN_CON_INTERVAL    MSEC_TO_UNITS(100,UNIT_1_25_MS)//minimum conn interval(7.5ms to 4.0 secs)
    #define MAX_CON_INTERVAL    MSEC_TO_UNITS(200,UNIT_1_25_MS)//maximum conn interval(7.5 ms to 4.0 secs)
    #define SLAVE_LATENCY       0                              //no of times the slave skips the conn interval
    #define CONN_SUP_TIMEOUT    MSEC_TO_UNITS(2000,UNIT_10_MS)//time between two succsessful conn events(100ms to 32s)
    
    #define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)  
    #define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)
    #define MAX_CONN_PARAMS_UPDATE_COUNT    3
    
    #define APP_ADV_INTERVAL    300
    #define APP_ADV_DURATION    0
    
    #define BATTERY_LEVEL_MEAS_INTERVAL     APP_TIMER_TICKS(2000)                   /**< Battery level measurement interval (ticks). */
    #define MIN_BATTERY_LEVEL               81                                      /**< Minimum battery level as returned by the simulated measurement function. */
    #define MAX_BATTERY_LEVEL               100                                     /**< Maximum battery level as returned by the simulated measurement function. */
    #define BATTERY_LEVEL_INCREMENT         1                                       /**< Value by which the battery level is incremented/decremented for each call to the simulated measurement function. */
    
    
    #define SEC_PARAM_BOND                  1                                       /**< Perform bonding. */
    #define SEC_PARAM_MITM                  0                                       /**< Man In The Middle protection not required. */
    #define SEC_PARAM_LESC                  0                                       /**< LE Secure Connections not enabled. */
    #define SEC_PARAM_KEYPRESS              0                                       /**< Keypress notifications not enabled. */
    #define SEC_PARAM_IO_CAPABILITIES       BLE_GAP_IO_CAPS_NONE                    /**< No I/O capabilities. */
    #define SEC_PARAM_OOB                   0                                       /**< Out Of Band data not available. */
    #define SEC_PARAM_MIN_KEY_SIZE          7                                       /**< Minimum encryption key size. */
    #define SEC_PARAM_MAX_KEY_SIZE          16                                      /**< Maximum encryption key size. */
    
    
    
    NRF_BLE_QWR_DEF(m_qwr);
    NRF_BLE_GATT_DEF(m_gatt);
    BLE_ADVERTISING_DEF(m_advertising);
    
    APP_TIMER_DEF(m_battery_timer_id);                                      /**< Battery timer. */
    BLE_BAS_DEF(m_bas);                                                     /**< Structure used to identify the battery service. */
    
    
    
    
    //private variables
    static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;
    static sensorsim_cfg_t      m_battery_sim_cfg;                          /**< Battery Level sensor simulator configuration. */
    static sensorsim_state_t    m_battery_sim_state;                        /**< Battery Level sensor simulator state. */
    
    
    
    static ble_uuid_t m_adv_uuids[] =                                       /**< Universally unique service identifiers. */
    {
        
        {BLE_UUID_BATTERY_SERVICE,            BLE_UUID_TYPE_BLE}
    };
    
    
    //private function declaration
    static void advertising_start(bool erase_bonds);
    
    
    
    
    //private functions
    
    //step-9 gatt initialization
    static void gatt_init()
    {
    ret_code_t err_code = nrf_ble_gatt_init(&m_gatt,NULL);
    APP_ERROR_CHECK(err_code);
    }
    
    
    
    //step-8 gap initialization
    static void gap_params_init(void)
    {
    ret_code_t err_code;
    ble_gap_conn_params_t    gap_conn_params;
    ble_gap_conn_sec_mode_t  sec_mode;
    
    
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
    err_code = sd_ble_gap_device_name_set(&sec_mode,(const uint8_t *)DEVICE_NAME,strlen(DEVICE_NAME));
    APP_ERROR_CHECK(err_code);
    
    memset(&gap_conn_params,0,sizeof(gap_conn_params));
    
    gap_conn_params.min_conn_interval = MIN_CON_INTERVAL;
    gap_conn_params.max_conn_interval = MAX_CON_INTERVAL;
    gap_conn_params.slave_latency     = SLAVE_LATENCY;
    gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;
    
    err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
    APP_ERROR_CHECK(err_code);
    
    
    }
    
    
    
    //step-13 error handler for queue writer
    static void nrf_qwr_error_handler(uint32_t nrf_error)
    {
    
    APP_ERROR_HANDLER(nrf_error);
    
    }
    
    
    //step-12 initialize services
    static void services_init(void)
    {
    
    ret_code_t err_code;
    ble_bas_init_t     bas_init;
    nrf_ble_qwr_init_t qwr_init = {0};
    
    qwr_init.error_handler = nrf_qwr_error_handler;
    err_code = nrf_ble_qwr_init(&m_qwr,&qwr_init);
    APP_ERROR_CHECK(err_code);
    
      // Initialize Battery Service.
        memset(&bas_init, 0, sizeof(bas_init));
    
        // Here the sec level for the Battery Service can be changed/increased.
        bas_init.bl_rd_sec        = SEC_OPEN;
        bas_init.bl_cccd_wr_sec   = SEC_OPEN;
        bas_init.bl_report_rd_sec = SEC_OPEN;
    
        bas_init.evt_handler          = NULL;
        bas_init.support_notification = true;
        bas_init.p_report_ref         = NULL;
        bas_init.initial_batt_level   = 100;
    
        err_code = ble_bas_init(&m_bas, &bas_init);
        APP_ERROR_CHECK(err_code);
    
    
    }
    
    
    //step-16 create an event handler for connection parameters update
    static void on_conn_params_evt(ble_conn_params_evt_t *p_evt)
    {
    ret_code_t err_code;
    if(p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
    {
    err_code = sd_ble_gap_disconnect(m_conn_handle,BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
    APP_ERROR_CHECK(err_code);
    }
    
    if(p_evt->evt_type == BLE_CONN_PARAMS_EVT_SUCCEEDED)
    {
    
    }
    
    }
    
    //step-15 create an error handler for connection parameters update
    
    static void conn_params_error_handler(uint32_t nrf_error)
    {
    
    APP_ERROR_HANDLER(nrf_error);
    }
    
    
    
    
    
    //step-14 connection params init
    static void conn_params_init(void)
    {
    ret_code_t err_code;
    
    ble_conn_params_init_t cp_init;
    memset(&cp_init,0,sizeof(cp_init));
    cp_init.p_conn_params  = NULL;
    cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
    cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
    cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
    cp_init.start_on_notify_cccd_handle    = BLE_GATT_HANDLE_INVALID;
    cp_init.disconnect_on_fail             = false;
    cp_init.error_handler = conn_params_error_handler;
    cp_init.evt_handler   = on_conn_params_evt;
    
    err_code = ble_conn_params_init(&cp_init);
    APP_ERROR_CHECK(err_code);
    
    
    }
    
    
    //step-11 create advertisement event handler
    static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
    {
    ret_code_t err_code;
    
    switch(ble_adv_evt)
    {
    case BLE_ADV_EVT_FAST:
    NRF_LOG_INFO("fast advertising....");
    err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
    APP_ERROR_CHECK(err_code);
    break;
    
    case BLE_ADV_EVT_IDLE:
    err_code = bsp_indication_set(BSP_INDICATE_IDLE);
    APP_ERROR_CHECK(err_code);
    
    break;
    
    default:
    break;
    
    }
    
    }
    
    
    
    
    //step-10 advertising init
    static void advertising_init(void)
    {
    ret_code_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 = true;
    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);
    }
    
    
    
    //step-7 ble event handler
    static void ble_evt_handler(ble_evt_t const *p_ble_evt,void *p_context)
    {
    ret_code_t err_code = NRF_SUCCESS;
    switch(p_ble_evt->header.evt_id)
    {
    case BLE_GAP_EVT_DISCONNECTED:
    NRF_LOG_INFO("device is disconnected");
    break;
    
    case BLE_GAP_EVT_CONNECTED:
    NRF_LOG_INFO("device is connected");
    err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
    APP_ERROR_CHECK(err_code);
    
    m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
    err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr,m_conn_handle);
    APP_ERROR_CHECK(err_code);
    break;
    
    
    
    case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
    NRF_LOG_DEBUG("phy update request");
    ble_gap_phys_t const phys = 
    {
    .rx_phys = BLE_GAP_PHY_AUTO,
    .tx_phys = BLE_GAP_PHY_AUTO,
    };
    
    err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle,&phys);
    APP_ERROR_CHECK(err_code);
    
    break;
    
    
    
    }//switch case end
    
    }
    
    
    
    
    
    //step-6 ble stack init
    static void ble_stack_init()
    {
    ret_code_t err_code;
    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);
    
    
    uint32_t ram_start = 0;
    
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG,&ram_start);
    APP_ERROR_CHECK(err_code);
    
    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);
    
    NRF_SDH_BLE_OBSERVER(m_ble_observer,APP_BLE_OBSERVER_PRIO,ble_evt_handler,NULL);
    
    }
    
    
    
    
    
    //step-4 init power management
    static void pwr_management_init(void)
    {
    ret_code_t err_code = nrf_pwr_mgmt_init();
    APP_ERROR_CHECK(err_code);
    
    }
    
    
    
    
    //step-3 init bsp leds for ble 
    static void leds_init(void)
    {
    ret_code_t err_code = bsp_init(BSP_INIT_LEDS,NULL);
    APP_ERROR_CHECK(err_code);
    }
    
    
    
    /**@brief Function for performing battery measurement and updating the Battery Level characteristic
     *        in Battery Service.
     */
    static void battery_level_update(void)
    {
        ret_code_t err_code;
        uint8_t  battery_level=0;
    
         // battery_level = (uint8_t)sensorsim_measure(&m_battery_sim_state, &m_battery_sim_cfg);
          // sensorsim_increment(&m_battery_sim_state, &m_battery_sim_cfg);
          //battery_level = battery_level + 1;
          //if(battery_level == 8) battery_level = 0;
          battery_level = 11;
        err_code = ble_bas_battery_level_update(&m_bas, battery_level, BLE_CONN_HANDLE_ALL);
        if ((err_code != NRF_SUCCESS) &&
            (err_code != NRF_ERROR_INVALID_STATE) &&
            (err_code != NRF_ERROR_RESOURCES) &&
            (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
           )
        {
            APP_ERROR_HANDLER(err_code);
        }
    }
    
    
    
    
    /**@brief Function for handling the Battery measurement timer timeout.
     *
     * @details This function will be called each time the battery level measurement timer expires.
     *
     * @param[in]   p_context   Pointer used for passing some arbitrary information (context) from the
     *                          app_start_timer() call to the timeout handler.
     */
    static void battery_level_meas_timeout_handler(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        battery_level_update();
       //NRF_LOG_INFO("update battery level value");
    }
    
    
    
    //step-2 init app timer
    static void timers_init(void)
    {
    ret_code_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);
    
     // Create timers.
        err_code = app_timer_create(&m_battery_timer_id,
                                    APP_TIMER_MODE_REPEATED,
                                    battery_level_meas_timeout_handler);
        APP_ERROR_CHECK(err_code);
    
    }
    
    
    
    
    //1st step initialize the logger
    static void log_init()
    {
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);
    NRF_LOG_DEFAULT_BACKENDS_INIT();
    }
    
    
    //step-5 idle state handle
    static void idle_state_handle(void)
    {
    if(NRF_LOG_PROCESS() == false)
    {
       nrf_pwr_mgmt_run();
    }
    
    }
    
    
    /**@brief Function for handling Peer Manager events.
     *
     * @param[in] p_evt  Peer Manager event.
     */
    static void pm_evt_handler(pm_evt_t const * p_evt)
    {
        pm_handler_on_pm_evt(p_evt);
        pm_handler_disconnect_on_sec_failure(p_evt);
        pm_handler_flash_clean(p_evt);
    
        switch (p_evt->evt_id)
        {
            case PM_EVT_PEERS_DELETE_SUCCEEDED:
                advertising_start(false);
                break;
    
            default:
                break;
        }
    }
    
    
    /**@brief Function for the Peer Manager initialization.
     */
    static void peer_manager_init(void)
    {
        ble_gap_sec_params_t sec_param;
        ret_code_t           err_code;
    
        err_code = pm_init();
        APP_ERROR_CHECK(err_code);
    
        memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));
    
        // Security parameters to be used for all security procedures.
        sec_param.bond           = SEC_PARAM_BOND;
        sec_param.mitm           = SEC_PARAM_MITM;
        sec_param.lesc           = SEC_PARAM_LESC;
        sec_param.keypress       = SEC_PARAM_KEYPRESS;
        sec_param.io_caps        = SEC_PARAM_IO_CAPABILITIES;
        sec_param.oob            = SEC_PARAM_OOB;
        sec_param.min_key_size   = SEC_PARAM_MIN_KEY_SIZE;
        sec_param.max_key_size   = SEC_PARAM_MAX_KEY_SIZE;
        sec_param.kdist_own.enc  = 1;
        sec_param.kdist_own.id   = 1;
        sec_param.kdist_peer.enc = 1;
        sec_param.kdist_peer.id  = 1;
    
        err_code = pm_sec_params_set(&sec_param);
        APP_ERROR_CHECK(err_code);
    
        err_code = pm_register(pm_evt_handler);
        APP_ERROR_CHECK(err_code);
    }
    
    
    
    /**@brief Clear bond information from persistent storage.
     */
    static void delete_bonds(void)
    {
        ret_code_t err_code;
    
        NRF_LOG_INFO("Erase bonds!");
    
        err_code = pm_peers_delete();
        APP_ERROR_CHECK(err_code);
    }
    
    
    //step-17 starting advertising.
    
    static void advertising_start(bool erase_bonds)
    {
        if (erase_bonds == true)
        {
            delete_bonds();
            // Advertising is started by PM_EVT_PEERS_DELETE_SUCCEEDED event.
        }
        else{
            ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
    
            APP_ERROR_CHECK(err_code);
        }
    }
    
    
    
    
    /**@brief Function for initializing the sensor simulators.
     */
    static void sensor_simulator_init(void)
    {
        m_battery_sim_cfg.min          = MIN_BATTERY_LEVEL;
        m_battery_sim_cfg.max          = MAX_BATTERY_LEVEL;
        m_battery_sim_cfg.incr         = BATTERY_LEVEL_INCREMENT;
        m_battery_sim_cfg.start_at_max = true;
    
        sensorsim_init(&m_battery_sim_state, &m_battery_sim_cfg);
    
    
    }
    
    
    
    
    
    /**@brief Function for starting application timers.
     */
    static void application_timers_start(void)
    {
        ret_code_t err_code;
    
        // Start application timers.
        err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for application main entry.
     */
    int main(void)
    {
       bool erase_bonds;
       log_init();//done
       timers_init();//done
       //leds_init();//done
    
      pwr_management_init();//done
    
      ble_stack_init();//done
      gap_params_init();//done
      gatt_init();//done
      advertising_init();//done
      services_init();//done
      //sensor_simulator_init();//done
      conn_params_init();//done
    
      NRF_LOG_INFO("Battery Level Server application started.....");
      application_timers_start();//done
      advertising_start(erase_bonds);//done
    
    
     //enter main loop  
    for(;;)
    {
        idle_state_handle();
    }
    }
    
    
    
    

  • Hi!

    I had a look at the code. What's happening is that the battery level characteristic is set to 11%, and this happens before the phone connects. 

    When the central/phone connects, and you enable notification for the battery level characteristic, the ble_bas_battery_level_update() function will only send a notification to the phone if the old value, that is 11%, changes to a new value. Snippet from the ble_bas_battery_level_update() function:

    if (battery_level != p_bas->battery_level_last)

    Since you have hardcoded it to 11%, then no notifications are send. The phone can still read the battery level characteristic "manually" with a GATT read, and receive that the value is 11%. But as long as the battery level does not change, then no notifications are send either.

Related