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

app_timer counter variable being interrupted/reset by BLE disconnection

I am using a repeated app timer to count the number of seconds that pass from the start of the NRF system powering on (or the code being flashed into the NRF). The code below also shows the current number of seconds (m_custom_value) that has passed in a custom characteristic value field when notifications are enabled. 

However, one issue I noticed is sometimes if I disable notifications I get an error 133(0x85): GATT error and then I get disconnected and get error 22(0x16) GATT CONN TERMINATE LOCAL HOST). I reconnect back to the NRF52 DK, but after I reconnect and turn on notifications I see that the m_custom_value has been reset back to 8 or a lower number than the number before the disconnection. I think the m_custom_value should only reset if the NRF system restarts. The m_custom_value should not be reset due to BLE disconnect. Is there anyway to fix or prevent this?

2. The second issue I am trying to figure out is how to save records of m_custom_value as it is being incremented. For example, if a sensor detects a change I want to save the current value of m_custom_value. I then want to pass the saved value as the custom characteristic's value (attr_char_value.p_value). I have tried doing this by using extern uint8_t global_time in both main.c and ble_cus.c. But when I read the custom characteristic value it is not the value of global_time. Am I using extern variable incorrectly, or is there something else wrong with my code?

Thanks!

// main.c
static uint8_t m_custom_value = 0;
static uint8_t notif_bool = 0;
uint8_t global_time; 

static void notification_timeout_handler(void * p_context)
{
    UNUSED_PARAMETER(p_context);
    ret_code_t err_code;
    
    // Increment the value of m_custom_value before notifying it.
    m_custom_value++;
    
    if (m_custom_value >= 3){
      global_time = 3;
    }
    if(notif_bool == 1){
       err_code = ble_cus_custom_value_update(&m_cus, m_custom_value);
       APP_ERROR_CHECK(err_code);
    }
}

static void on_cus_evt(ble_cus_t     * p_cus_service,
                       ble_cus_evt_t * p_evt)
{
    ret_code_t err_code;
    
    switch(p_evt->evt_type)
    {
        case BLE_CUS_EVT_NOTIFICATION_ENABLED:
            
             //err_code = app_timer_start(m_notification_timer_id, NOTIFICATION_INTERVAL, NULL);
             //APP_ERROR_CHECK(err_code);
             notif_bool = 1;
             break;

        case BLE_CUS_EVT_NOTIFICATION_DISABLED:

            //err_code = app_timer_stop(m_notification_timer_id);
            //APP_ERROR_CHECK(err_code);
            break;

        case BLE_CUS_EVT_CONNECTED:
            break;

        case BLE_CUS_EVT_DISCONNECTED:
              break;

        default:
              // No implementation needed.
              break;
    }
}



static void application_timers_start(void)
{
       ret_code_t err_code;
       err_code = app_timer_start(m_notification_timer_id, NOTIFICATION_INTERVAL, NULL);
       APP_ERROR_CHECK(err_code);
}

int main(void)
{
    bool erase_bonds;

    // Initialize.
    log_init();
    timers_init();
    application_timers_start();
    buttons_leds_init(&erase_bonds);
    power_management_init();
    ble_stack_init();
    gap_params_init();
    gatt_init();
    services_init();
    advertising_init();
    conn_params_init();
    peer_manager_init();

    // Start execution.
    NRF_LOG_INFO("Template example started.");
    //application_timers_start();

    advertising_start(erase_bonds);

    // Enter main loop.
    for (;;)
    {
        idle_state_handle();
    }
}


// ble_cus.c

extern uint8_t global_time; 

static uint32_t custom_value_char_add(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
{
    uint32_t            err_code;
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

    // Add Custom Value characteristic
    memset(&cccd_md, 0, sizeof(cccd_md));

    //  Read  operation on cccd should be possible without authentication.
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    
    cccd_md.write_perm = p_cus_init->custom_value_char_attr_md.cccd_write_perm;
    cccd_md.vloc       = BLE_GATTS_VLOC_STACK;

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

    char_md.char_props.read   = 1;
    char_md.char_props.write  = 1;
    char_md.char_props.notify = 1; 
    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         = &cccd_md; 
    char_md.p_sccd_md         = NULL;
		
    ble_uuid.type = p_cus->uuid_type;
    ble_uuid.uuid = CUSTOM_VALUE_CHAR_UUID;

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

    attr_md.read_perm  = p_cus_init->custom_value_char_attr_md.read_perm;
    attr_md.write_perm = p_cus_init->custom_value_char_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);
    
    if (global_time == 3){
      attr_char_value.p_value = &global_time;
    }
    err_code = sd_ble_gatts_characteristic_add(p_cus->service_handle, &char_md,
                                               &attr_char_value,
                                               &p_cus->custom_value_handles);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    return NRF_SUCCESS;
}

app_timer.zip

Parents
  • Hi,

    1. The counter is reset to 0 because the device is being reset. This happens because when there is no connection, you return NRF_ERROR_INVALID_STATE form ble_cus_custom_value_update(). This return value is checked on line 226 in your main.c, and since it is not NRF_SUCCESS, the error handler will reset the device (unless you have modified the default behavior). If this is not an error state, you should not return an error value. Alternatively, return an error value that you check for explicitly, and only call APP_ERROR_CHECK() for other errors.

    2. The extern part is OK, since it is declared "normally" in main.c and as extern in ble_cus.c. So you have the same variable in both files. However, I am having problems understanding what you try to do with the global_time variable. The following is what will happen to it as far as I can see:

    • At startup, global_time is 0 since it is the default value for global variables. m_custom_value is also 0
    • When m_custom_value is in incremented to 1 and 2 in notification_timeout_handler(), nothing happens to global_time, so it stays 0.
    • When m_custom_value is incremented to 3, the if check on 221 is true, so global_time is set to 3. The if check will always be valid, so for every other iteration, global_time will be 3 (until the device is reset).
  • Hi I am trying to use global_time to pass the value of m_custom_value to the custom characteristic's value attr_char_value.p_value. I am just using the if conditional statement to make sure the characteristic value when read in the nrf_connect app is "3". But currently when I read the value it is not 3.

    Another thing I have tried is simply assigning a copy of the value of m_custom_value to global_time. But that doesn't work either. 

     

    static void notification_timeout_handler(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        ret_code_t err_code;
        
        // Increment the value of m_custom_value before notifying it.
        m_custom_value++;
        
        
        global_time = m_custom_value;
        
        if(notif_bool == 1){
           err_code = ble_cus_custom_value_update(&m_cus, m_custom_value);
           APP_ERROR_CHECK(err_code);
        }
    }

    extern uint8_t global_time; 
    
    static uint32_t custom_value_char_add(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
    {
        uint32_t            err_code;
        ble_gatts_char_md_t char_md;
        ble_gatts_attr_md_t cccd_md;
        ble_gatts_attr_t    attr_char_value;
        ble_uuid_t          ble_uuid;
        ble_gatts_attr_md_t attr_md;
    
        // Add Custom Value characteristic
        memset(&cccd_md, 0, sizeof(cccd_md));
    
        //  Read  operation on cccd should be possible without authentication.
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
        
        cccd_md.write_perm = p_cus_init->custom_value_char_attr_md.cccd_write_perm;
        cccd_md.vloc       = BLE_GATTS_VLOC_STACK;
    
        memset(&char_md, 0, sizeof(char_md));
    
        char_md.char_props.read   = 1;
        char_md.char_props.write  = 1;
        char_md.char_props.notify = 1; 
        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         = &cccd_md; 
        char_md.p_sccd_md         = NULL;
    		
        ble_uuid.type = p_cus->uuid_type;
        ble_uuid.uuid = CUSTOM_VALUE_CHAR_UUID;
    
        memset(&attr_md, 0, sizeof(attr_md));
    
        attr_md.read_perm  = p_cus_init->custom_value_char_attr_md.read_perm;
        attr_md.write_perm = p_cus_init->custom_value_char_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 = &global_time;
        
        err_code = sd_ble_gatts_characteristic_add(p_cus->service_handle, &char_md,
                                                   &attr_char_value,
                                                   &p_cus->custom_value_handles);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        return NRF_SUCCESS;
    }

  • Hi,

    Looking at the implementation of notification_timeout_handler() and ble_cus_custom_value_update() in app_timer.zip, I see that essentially you are providing a pointer to the static m_custom_value to sd_ble_gatts_value_set() and sd_ble_gatts_hvx(), so this looks sensible. Note that since you are passing m_custom_value and not global_time, you are passing the value of the counter. Therefore this value is not expected to stay 3 (even though global_time does). Does this not match with what you see? I mean, is the notified values counting upwards (as it seems it should), or do you see another pattern?

Reply
  • Hi,

    Looking at the implementation of notification_timeout_handler() and ble_cus_custom_value_update() in app_timer.zip, I see that essentially you are providing a pointer to the static m_custom_value to sd_ble_gatts_value_set() and sd_ble_gatts_hvx(), so this looks sensible. Note that since you are passing m_custom_value and not global_time, you are passing the value of the counter. Therefore this value is not expected to stay 3 (even though global_time does). Does this not match with what you see? I mean, is the notified values counting upwards (as it seems it should), or do you see another pattern?

Children
  • Hi, 

    In my code from app_timer.zip (part of which I have below), my goal was to assign global_time the value of 3, which should stay constant, after the counter m_custom_value increments to 3 and continues higher. But when I assign &global_time to attr_char_value.p_value the custom characteristic value I read from the nrf_connect_app is not 3 but another number. 

    //main.c
    static void notification_timeout_handler(void * p_context)
    {
        m_custom_value++;
        
        if (m_custom_value >= 3){
          global_time = 3;
        }
    }
    
    
    //ble_cus.c
    if (global_time == 3){
          attr_char_value.p_value = &global_time;
        }

    What I am essentially trying to do is have the user see the current counter value when the user reads the characteristic value, without needing to have to turn on notifications. Since the counter keeps incrementing and notifications are disabled the user would have to click the read button every time the user wants the current counter value. But in the code below, even when I pass in the counter value to attr_char_value.p_value I get a different value (not the counter value) when I read the characteristic value with the nrf_connect app. For example after I flash the code and wait 30 seconds, the value I read is 0. I think that sd_ble_gatts_value_set() is the only way to update the characteristic value to be the current counter value when there is a read request, but right now I am trying to figure out a way to handle characteristic read events. 

    static void notification_timeout_handler(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        ret_code_t err_code;
        
        // Increment the value of m_custom_value before notifying it.
        m_custom_value++;
        
        
        global_time = m_custom_value;
        //global_Time = &m_custom_value; //also tried this, no difference from = m_custom_value
        
        if(notif_bool == 1){
           err_code = ble_cus_custom_value_update(&m_cus, m_custom_value);
           APP_ERROR_CHECK(err_code);
        }
    }

    extern uint8_t global_time; 
    
    static uint32_t custom_value_char_add(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
    {
        uint32_t            err_code;
        ble_gatts_char_md_t char_md;
        ble_gatts_attr_md_t cccd_md;
        ble_gatts_attr_t    attr_char_value;
        ble_uuid_t          ble_uuid;
        ble_gatts_attr_md_t attr_md;
    
        // Add Custom Value characteristic
        memset(&cccd_md, 0, sizeof(cccd_md));
    
        //  Read  operation on cccd should be possible without authentication.
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
        
        cccd_md.write_perm = p_cus_init->custom_value_char_attr_md.cccd_write_perm;
        cccd_md.vloc       = BLE_GATTS_VLOC_STACK;
    
        memset(&char_md, 0, sizeof(char_md));
    
        char_md.char_props.read   = 1;
        char_md.char_props.write  = 1;
        char_md.char_props.notify = 1; 
        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         = &cccd_md; 
        char_md.p_sccd_md         = NULL;
    		
        ble_uuid.type = p_cus->uuid_type;
        ble_uuid.uuid = CUSTOM_VALUE_CHAR_UUID;
    
        memset(&attr_md, 0, sizeof(attr_md));
    
        attr_md.read_perm  = p_cus_init->custom_value_char_attr_md.read_perm;
        attr_md.write_perm = p_cus_init->custom_value_char_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 = &global_time;
        
        err_code = sd_ble_gatts_characteristic_add(p_cus->service_handle, &char_md,
                                                   &attr_char_value,
                                                   &p_cus->custom_value_handles);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        return NRF_SUCCESS;
    }

  • Hi,

    myNrfDoesntWork said:
    In my code from app_timer.zip (part of which I have below), my goal was to assign global_time the value of 3, which should stay constant, after the counter m_custom_value increments to 3 and continues higher. But when I assign &global_time to attr_char_value.p_value the custom characteristic value I read from the nrf_connect_app is not 3 but another number. 

    I am having some problems understanding the link between the questions and the code snippets. Can you include all your current code in the next reply (like the app_timer.zip file from the initial question), and also please explain what value you do see (when it is not 3). Looking at the code it should be 3 after a few iterations, as discussed. But note that in the code you have uploaded in the zip, ble_cus_custom_value_update() is only called when notif_bool is 1, which means that the counter is not incremented unless you have enabled notifications.

    myNrfDoesntWork said:
    What I am essentially trying to do is have the user see the current counter value when the user reads the characteristic value, without needing to have to turn on notifications.

    Then you need to call  sd_ble_gatts_value_set() every time the counter is incremented, regardless of weather notifications are enabled or not.

    myNrfDoesntWork said:
    But in the code below, even when I pass in the counter value to attr_char_value.p_value I get a different value (not the counter value) when I read the characteristic value with the nrf_connect app. For example after I flash the code and wait 30 seconds, the value I read is 0.

    Please share the complete code when this occurs (or when something else occurs, and with a detailed description). Currently, I find it difficult to match code and behavior since I see and have commented on issues in your code that I don't know if you have fixed or not before testing.

    myNrfDoesntWork said:
    I think that sd_ble_gatts_value_set() is the only way to update the characteristic value to be the current counter value when there is a read request

    Yes.

    myNrfDoesntWork said:
    but right now I am trying to figure out a way to handle characteristic read events.

    How does this relate to the issue? In any case, there are no events for ordinary read operations. There are events for authorized read operations, though (which is a given, since you must know about it to authorize it).

Related