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

Battery level characteristics not updated

I want to add a battery level characteristics to a custom service. Here is my approach:

In main.c, I add a timer to update battery level periodically

void data_update_timer_timeout_handler(void * p_context)
{
	 battery_level_update(&m_sss);
}

static void timers_init(void)
{
    // Initialize timer module, making it use the scheduler
    APP_TIMER_APPSH_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS,
  APP_TIMER_OP_QUEUE_SIZE, true);
	
	  uint32_t err_code = app_timer_create(&m_data_update_timer,
  APP_TIMER_MODE_REPEATED,
  data_update_timer_timeout_handler);
    APP_ERROR_CHECK(err_code);
}

The function "battery_level_update" is

uint32_t battery_level_update(ble_sss_t * p_sss)
{
	  uint8_t battery_level = battery_level_get();
	
	  uint32_t err_code = NRF_SUCCESS;
    ble_gatts_value_t gatts_value;

    if (battery_level != p_sss->battery_level_last)
    {
        // Initialize value struct.
        memset(&gatts_value, 0, sizeof(gatts_value));

        gatts_value.len     = sizeof(uint8_t);
        gatts_value.offset  = 0;
        gatts_value.p_value = (uint8_t *)(&battery_level);

        // Save new battery value.
        p_sss->battery_level_last = battery_level;

        // Update database.
        err_code = sd_ble_gatts_value_set(p_sss->conn_handle,
                                          p_sss->battery_level_char_handles.value_handle,
                                          &gatts_value);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
}

which further depends on "battery_level_get" as follows:

uint8_t battery_level_get(void)
{
    // Configure ADC
    NRF_ADC->CONFIG     = (ADC_CONFIG_RES_8bit                        << ADC_CONFIG_RES_Pos)     |
                          (ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos)  |
                          (ADC_CONFIG_REFSEL_VBG                      << ADC_CONFIG_REFSEL_Pos)  |
                          (ADC_CONFIG_PSEL_Disabled                   << ADC_CONFIG_PSEL_Pos)    |
                          (ADC_CONFIG_EXTREFSEL_None                  << ADC_CONFIG_EXTREFSEL_Pos);
    NRF_ADC->EVENTS_END = 0;
    NRF_ADC->ENABLE     = ADC_ENABLE_ENABLE_Enabled;

    NRF_ADC->EVENTS_END  = 0;    // Stop any running conversions.
    NRF_ADC->TASKS_START = 1;
    
    while (!NRF_ADC->EVENTS_END)
    {
    }
    
    uint16_t vbg_in_mv = 1200;
    uint8_t adc_max = 255;
    uint16_t vbat_current_in_mv = (NRF_ADC->RESULT * 3 * vbg_in_mv) / adc_max;
    
    NRF_ADC->EVENTS_END     = 0;
    NRF_ADC->TASKS_STOP     = 1;
    
    return (uint8_t) ((vbat_current_in_mv * 100) / VBAT_MAX_IN_MV);
}

Here is how I add battery level characteristic:

static uint32_t battery_level_char_add(ble_sss_t * p_sss, const ble_sss_init_t * p_sss_init) {

ble_gatts_char_md_t char_md;   // characteristic metadata
ble_gatts_attr_t    attr_char_value;   // the value of characteristics (GATT attribute type)
ble_uuid_t          ble_uuid;
ble_gatts_attr_md_t attr_md;  // attribute metadata
uint8_t battery_level = p_sss_init->battery_level;

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

char_md.char_props.read   = 1;
char_md.char_props.write  = 0;
char_md.p_char_user_desc  = NULL;
char_md.p_char_pf         = NULL;
char_md.p_user_desc_md    = NULL;
char_md.p_cccd_md         = NULL;
char_md.p_sccd_md         = NULL;

ble_uuid.type = p_sss->uuid_type;
ble_uuid.uuid = SSS_UUID_CHAR_BATTERY_LEVEL;

	// Setting the 2nd attribute (the value of characteristics)
	// First set up the attribute metadata, and then add it to attribute
memset(&attr_md, 0, sizeof(attr_md));

BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.write_perm);
attr_md.vloc       = BLE_GATTS_VLOC_STACK;
attr_md.rd_auth    = 0;
attr_md.wr_auth    = 0;
attr_md.vlen       = 0;

memset(&attr_char_value, 0, sizeof(attr_char_value));
	
attr_char_value.p_uuid       = &ble_uuid;
attr_char_value.p_attr_md    = &attr_md;
attr_char_value.init_len     = sizeof(uint8_t);
attr_char_value.init_offs    = 0;
attr_char_value.max_len      = sizeof(uint8_t);  
attr_char_value.p_value      = (uint8_t *)(&battery_level);

return sd_ble_gatts_characteristic_add(p_sss->service_handle, &char_md,
                                           &attr_char_value,
                                           &p_sss->battery_level_char_handles);

}

I initially pass value 50 to the battery_level variable in "battery_level_char_add" (via p_sss_init) so the initial value is 50 (or whatever the initial value I set). I expect it to be updated when the timer times out in main.c. However, it doesn't happen --- the value just stays 50 when I read the characteristic with Master Control Panel app.

I also test "battery_level_get" by calling it in BLE advertising initialization and send it out in advertising packet. The value is 87 which is very reasonable. So "battery_level_get" works.

Related