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

ATT_MTU_Throughput how does NRF_ERROR_RESOURCES work?

Hi,

I am trying to understand one thing about the att_mtu_throughput example.

What it does in the char_notification_send() function, which is shown below, is that if it has not sent 1Mbits yet, it tries to send some random data, and increments the counter until for a NRF_ERROR_RESOURCES event is returned, in other words until the queue is full. When the NRF_ERROR_RESOURCES is returned, it sets the busy= true and exits the while loop. 

static void char_notification_send(nrf_ble_amts_t * p_ctx)
{
    uint8_t            data[256];
    uint16_t           payload_len = p_ctx->max_payload_len;
    nrf_ble_amts_evt_t evt;

    if (p_ctx->bytes_sent >= AMT_BYTE_TRANSFER_CNT)
    {
        evt.bytes_transfered_cnt = p_ctx->bytes_sent;
        evt.evt_type             = NRF_BLE_AMTS_EVT_TRANSFER_FINISHED;

        p_ctx->evt_handler(evt);

        p_ctx->busy        = false;
        p_ctx->bytes_sent  = 0;
        p_ctx->kbytes_sent = 0;

        return;
    }

    ble_gatts_hvx_params_t const hvx_param =
    {
        .type   = BLE_GATT_HVX_NOTIFICATION,
        .handle = p_ctx->amts_char_handles.value_handle,
        .p_data = data,
        .p_len  = &payload_len,
    };

    uint32_t err_code = NRF_SUCCESS;
    while (err_code == NRF_SUCCESS)
    {
        (void)uint32_encode(p_ctx->bytes_sent, data);

        err_code = sd_ble_gatts_hvx(p_ctx->conn_handle, &hvx_param);

        if (err_code == NRF_ERROR_RESOURCES)
        {
            // Wait for BLE_GATTS_EVT_HVN_TX_COMPLETE.
            p_ctx->busy = true;
            break;
        }
        else if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_ERROR("sd_ble_gatts_hvx() failed: 0x%x", err_code);
        }

        p_ctx->bytes_sent += payload_len;

        if (p_ctx->kbytes_sent != (p_ctx->bytes_sent / 1024))
        {
            p_ctx->kbytes_sent = (p_ctx->bytes_sent / 1024);

            evt.evt_type             = NRF_BLE_AMTS_EVT_TRANSFER_1KB;
            evt.bytes_transfered_cnt = p_ctx->bytes_sent;
            p_ctx->evt_handler(evt);
        }
    }
}

Now it is expected to go to wait for a BLE_GATTS_EVT_HVN_TX_COMPLETE event, which means there is available space in the queue. And the switch case again calls char_notification_send() in the on_tx_complete() function after setting the busy=false. The same procedure loops around until 1Mbit is sent. 

void nrf_ble_amts_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
    nrf_ble_amts_t * p_ctx = (nrf_ble_amts_t *)p_context;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            on_connect(p_ctx, p_ble_evt);
            break;

        case BLE_GAP_EVT_DISCONNECTED:
            on_disconnect(p_ctx, p_ble_evt);
            break;

        case BLE_GATTS_EVT_WRITE:
            on_write(p_ctx, p_ble_evt);
            break;

        case BLE_GATTS_EVT_HVN_TX_COMPLETE:
            on_tx_complete(p_ctx);
            break;

        default:
            break;
    }
}



static void on_tx_complete(nrf_ble_amts_t * p_ctx)
{
    if (p_ctx->busy)
    {
        p_ctx->busy = false;
        char_notification_send(p_ctx);
    }
}

The thing I do not get is, when NRF_ERROR_RESOURCES is returned and the program exits the while loop, how come and where does it wait for a BLE_GATTS_EVT_HVN_TX_COMPLETE event? When NRF_ERROR_RESOURCES  is returned, I expect the program to go back to where char_notification_send() is initially called. However it does not. Is there something the code does in the background that I did not notice?

Parents
  • Hi,

    The BLE stack and SDK are event-based, so the BLE_GATTS_EVT_HVN_TX_COMPLETE will be received by the BLE event handlers. In the ATT_MTU_Throughput example in SDK 15.3 this is implemented in examples\ble_central_and_peripheral\experimental\ble_app_att_mtu_throughput\amts.c. There you can see that a BLE_GATTS_EVT_HVN_TX_COMPLETE event causes a call to on_tx_complete(), which calls char_notification_send() if the busy flag was set. And then we are back in the loop Slight smile

  • Hi Einer,

    I guess I mentioned this loop stuff myself in my question already :) The thing I do not understand is; when we set busy = true and get out of the while loop, the code has to wait somewhere until BLE_GATTS_EVT_HVN_TX_COMPLETE event occurs and on_tx_complete() is called, is this right? Where does it wait?

Reply Children
  • Hi,

    Den said:
    The thing I do not understand is; when we set busy = true and get out of the while loop, the code has to wait somewhere until BLE_GATTS_EVT_HVN_TX_COMPLETE event occurs and on_tx_complete() is called, is this right? Where does it wait?

    There is no special logic needed for waiting in this case, since the BLE stack is event-driven. It just sets the busy flag to true, and breaks out of the loop, and then out of the char_notification_send(). Then the char_notification_send() is called again in the handling of the BLE_GATTS_EVT_HVN_TX_COMPLETE only if busy is true.

    After all interrupts are services and nothing else happens, then the main loop will run, which calls idle_state_handle() in main.c of the example. Essentially it will just put the CPU to sleep in this case, waiting for an event/interrupt.

    In detail:

    static void idle_state_handle(void)
    {
        cli_process();
    
        if (is_test_ready())
        {
            NRF_LOG_INFO("Test started");
            m_run_test = true;
            test_run();
        }
    
        if (NRF_LOG_PROCESS() == false)
        {
            nrf_pwr_mgmt_run();
        }
    }

    If a current test is in progress (which will be the case here), then m_run_test is true, so is_test_ready() will return false. Therefore the only thing that happens is first processing of CLI (if needed), then processing of logs if needed. Then the CPU enters sleep by calling nrf_pwr_mgmt_run().

  • So once busy = true is set, it waits for the BLE_GATTS_EVT_HVN_TX_COMPLETE event in sleep. Now it makes sense. I missed that bit. Thanks very much for the clarification EinarThumbsup

Related