Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

NRF_EVT_RADIO_BLOCKED then NRF_ERROR_FORBIDDEN when requesting next earliest timeslot

I'm running into an issue when trying to use the timeslot API for advertising while concurrently scanning. Scanning is using a 100 ms interval with 50 ms window and initialized and started using 

sd_ble_gap_scan_start(&m_scan_param, &m_scan_buffer);

The timeslot API is used to advertise when the radio is idle between scan windows. The relevant timeslot code is:

void timeslot_evt_signal_handler(uint32_t evt_id) {

    switch (evt_id) {
    case NRF_EVT_RADIO_SIGNAL_CALLBACK_INVALID_RETURN:
        break;
    case NRF_EVT_RADIO_SESSION_IDLE:
uint32_t err_code = sd_radio_session_close();
APP_ERROR_CHECK(err_code);
break;
case NRF_EVT_RADIO_SESSION_CLOSED:
break;
case NRF_EVT_RADIO_BLOCKED:
case NRF_EVT_RADIO_CANCELED:
uint32_t err_code = m_request_earliest(NRF_RADIO_PRIORITY_NORMAL, total_distance); // 2-10 times original distance
APP_ERROR_CHECK(err_code);
break;
default:
break;
}
}


static uint32_t m_request_earliest(enum NRF_RADIO_PRIORITY priority, uint32_t timeout_ms) {
    m_btle_timeslot_request.request_type = NRF_RADIO_REQ_TYPE_EARLIEST;
    m_btle_timeslot_request.params.earliest.hfclk = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
    m_btle_timeslot_request.params.earliest.priority = priority;
    m_btle_timeslot_request.params.earliest.length_us = BTLE_ADV_SLOT_LENGTH; // 5500 us;
    m_btle_timeslot_request.params.earliest.timeout_us = timeout_ms * 1000;
    return sd_radio_request(&m_btle_timeslot_request);
}


nrf_radio_signal_callback_return_param_t *timeslot_radio_callback(uint8_t signal_type) {
    switch (signal_type) {
    case NRF_RADIO_CALLBACK_SIGNAL_TYPE_START:
        if (m_timeslot_active_mode & TIMESLOT_MODE_ADV) {
            btle_adv_evt_start(&m_signal_callback_return_param);
        }
        break;

    case NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO:
        if (m_timeslot_active_mode & TIMESLOT_MODE_ADV) {
            btle_adv_evt_radio(&m_signal_callback_return_param);
        }
        break;

    case NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0:
        break;
    case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED:
        break;
    case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED:
        break;
    default:
        break;
    }
    return (&m_signal_callback_return_param);
}

void btle_adv_evt_start(nrf_radio_signal_callback_return_param_t *param) {
    param->params.request.p_next = NULL;
    param->callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE;
}

void btle_adv_evt_radio(nrf_radio_signal_callback_return_param_t *param) {
// Computations for distance removed
param->params.request_type = NRF_RADIO_REQ_TYPE_NORMAL;
param->params.params.normal.hfclk = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
param->params.params.normal.priority = NRF_RADIO_PRIORITY_NORMAL;
param->params.params.normal.distance_us = distance;
param->params.params.normal.length_us = BTLE_ADV_SLOT_LENGTH; // 5500 us;
}

Everything works fine if there is no scanning or if distance is a multiple of 100 ms, but timeslot_evt_signal_handler gets a NRF_EVT_RADIO_BLOCKED event for other timing intervals. All of the examples show trying to request the next earliest timeslot when a blocked event occurs (which is what is done here), but that results in a NRF_ERROR_FORBIDDEN response.

What should be the proper handling and/or setup of the timeslots to handle a NRF_EVT_RADIO_BLOCKED event?

Thanks!

  • After some further investigation, it appears that the second blocked event wasn't from the request next earliest. Two blocked events are happening for the NRF_RADIO_REQ_TYPE_NORMAL request. This causes two immediate requests for the next earliest timeslots, so the the NRF_ERROR_FORBIDDEN occurred. There appears to be a bug in the SDK that's causing two blocked events instead of just one. I was able to work around this by using a flag to prevent two simultaneous timeslot requests.

  • I cannot see the timing between events in the code snippets you have included. However, based on what you write it does seem like there simply is no available time slots, so you get NRF_EVT_RADIO_BLOCKED. Then for some reason you end up requesting a timeslot again even if the session is not idle (due to the previous request). There is one thing that puzzles me, though. You write that everything works fine if scanning distance is a multiple of 100 ms. Does it really have to be a multiple, or is it enough that the scanning distance is >= 100 ms (which is what I would expect)?

  • I'm expecting the NRF_EVT_RADIO_BLOCKED event since some of the timeslots are too close together (our algorithm is trying to interleave advertisements with different intervals while simultaneously scanning). 100 ms was an example of one that did not cause a blocked event, but it depends the specific intervals that are requested.

    The problem isn't the blocked event per se. It's that I'm getting duplicate events regardless of requesting an additional timeslot after getting blocked. Note that the additional request was for the next earliest with a long timeout. I figured out that I was getting duplicate BLOCKED events because I would disable the additional request and still get 2 blocked events. With additional testing, I found that this was happening for the NRF_EVT_RADIO_SESSION_IDLE event and possibly other events as well. I ended up with the following code to prevent duplicate event handling.

    This is occurring on v15.0.0 of the SDK with a PCA10040 S132 target. I don't know if this happens on earlier versions of the SDK.

    static uint32_t m_last_evt = NRF_EVT_NUMBER_OF_EVTS + 1;
    
    void timeslot_evt_signal_handler(uint32_t evt_id) {
    
        if (m_last_evt != evt_id) {
            m_last_evt = evt_id;
            switch (evt_id) {
            case NRF_EVT_RADIO_SIGNAL_CALLBACK_INVALID_RETURN:
                break;
            case NRF_EVT_RADIO_SESSION_IDLE:
                handle_radio_idle();
                break;
            case NRF_EVT_RADIO_SESSION_CLOSED:
                break;
            case NRF_EVT_RADIO_BLOCKED:
            case NRF_EVT_RADIO_CANCELED:
                handle_radio_blocked_or_canceled();
                break;
            default:
                break;
            }
        }
    }
    
  • Would it be possible to upload a working project that demonstrate the duplicate events? (You can do it in a private case if you don't want to share your private code with the community).

  • Thank you. I opened a private support ticket with sample code.

Related