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

High power draw when sending multiple packets during one connection, waiting for BLE_GATTS_EVT_HVN_TX_COMPLETE event to send consecutive packets

Hey! I am using nRF52 DK, SDK 15.2.0, and softdevice s112. I have an application that reads some data with the saadc, advertises, sends data if a connection is made or stores data in a buffer if no connection is made, then goes to sleep and repeats. If a connection is not made and data is stored in a buffer, then all the buffered data and the most recently collected piece of data are sent during the connection. So, if 5 connections were missed, then 6 data points will be sent if the next advertising event results in a connection. 

Thanks to the help of this awesome community and the great Nordic engineers, I was able to get the application to super low power consumption for regular operation (no data buffering). My application can happily read data, advertise, connect send, sleep, repeat with a consistent 2.3uA draw. Now, whenever I need to send buffered data during one connection interval, the "median" power consumption jumps to about 5.5mA. The power goes back to 2.3uA when the application is done and returns to sleep. You can see what I mean in the following images - when I send just one piece of data, the lowest power draw in between "spikes" is 2.3uA, but when I need to send multiple packets the lowest power draw jumps to about 5.5mA.

To send this buffered data, I had to change my code slightly. I tried to just repeatedly call "ble_nus_data_send" in a loop until I got through the buffered data. However, this threw a NRF resources error. Even when I use the "do { ... err_code = ble_nus_data_send ... } while (err_code == NRF_ERROR_RESOURCES)" trick, my application would just hang in the loop forever. I know this has something to do with interrupt priorities, but I found a solution by putting the following code in main:

/* Snippet of main */

int main(void)
{

    // Inits and stuff here

    while (true)
    {
        idle_state_handle();
        // If data has been buffered then send accordingly, 
        // waiting for BLE_GATTS_EVT_HVN_TX_COMPLETE in 
        // between packets so the tx buffer is not filled
        if (SEND_BUFFERED_DATA) {
            while(HVN_TX_EVT_COMPLETE == false) { break; }
            send_next_packet_in_buffer();
        }
    } 
}

I basically use a flag to indicate when I need to send buffered data, and another flag to indicate when BLE_GATTS_EVT_HVN_TX_COMPLETE event occurs. This way, I just send one packet per BLE_GATTS_EVT_HVN_TX_COMPLETE event and never experience the NRF_ERROR_RESOURCES issue. This works pretty good, because it is not critical I send this data as fast as absolute possible in this application. For reference, here is what my "send_next_packet_in_buffer" function looks like:

bool SEND_BUFFERED_DATA;
uint16_t PACK_CTR = 0;
uint16_t TOTAL_DATA_IN_BUFFERS;

void send_next_packet_in_buffer()
{
     uint32_t err_code;
     send_buffered_data();
     PACK_CTR++;
     HVN_TX_EVT_COMPLETE = false;
     // Reset buffers, variables, and disconnect when finished 
     if (PACK_CTR == TOTAL_DATA_IN_BUFFERS) {
         reset_data_buffers();
         SEND_BUFFERED_DATA = false;
       err_code = 
            sd_ble_gap_disconnect(m_conn_handle, 
                                  BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
       APP_ERROR_CHECK(err_code);
      }
}

void send_buffered_data(void)
{
    uint32_t err_code;
    HVN_COUNTER = 0;

    // Create packet
    create_bluetooth_packet((uint32_t)ph_mv[PACK_CTR], 
                            (uint32_t)batt_mv[PACK_CTR], 
                            (uint32_t)temp_mv[PACK_CTR], 
                             ph_cal[PACK_CTR], total_packet);
    do {
        err_code = ble_nus_data_send(&m_nus, total_packet, 
                                     &total_size, m_conn_handle);
        if ((err_code != NRF_ERROR_INVALID_STATE) &&
            (err_code != NRF_ERROR_RESOURCES) &&
            (err_code != NRF_ERROR_NOT_FOUND))
        {   
            APP_ERROR_CHECK(err_code);              
        }
    } while (err_code == NRF_ERROR_RESOURCES);
}

Kinda messy, but I use a global counter variable to iterate through my data buffer arrays every time a BLE_GATTS_EVT_HVN_TX_COMPLETE event occurs. I have tried putting my idle_state_handle() function inside the "while(HVN_TX_EVT_COMPLETE == false) { break; }" in main, and have also tried to call the idle_state_handle() function after I receive a  BLE_GATTS_EVT_HVN_TX_COMPLETE event. However when I do either of these things, the program just stalls and doesn't do anything or send any more buffered data. 

I think the power draw has to be caused by the "while(HVN_TX_EVT_COMPLETE == false) { break; }". I've tried so many other ways to send the buffered data but this is the only way that I have been able to make work. I've messed around with the tx_queue default size and theNRF_SDH_BLE_GAP_EVENT_LENGTH variables, but had no luck. Perhaps there is another more elegant solution to the sending buffered data. Either way, it would be great if someone could let me know if I'm stuck with the 5.5mA draw during buffered data sending in this solution, or if there is something I can do to bring the minimum power draw back down to 2.3uA even while sending the buffered data. Thanks!

Related