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

Init/deinit openthread stack causes a failed assert

Hello,

for our application i need to periodically enable and disable the openthread stack on the nRF52840 chip.

For testing purposes i've modified the Thread CLI example so that the main loop keeps doing the following:

  1. Wait for a timer to expire (when it does, a flag is set): ~ 7 seconds
  2. Enable the thread stack and connect to the configured thread network. This part calls the thread_init() function in "thread_utils.c" in the examples.
  3. After the connection is established (device is connected as a child or router), waits for about 1.5 seconds (as before, using a timer and a flag).
  4. After this period, turn off the thread stack. This part also calls the thread_deinit() function in "thread_utils.c".
  5. Loop from 1) over and over.

Of course in the main loop the "thread_process" function is called at every iteration while the thread stack is initialized.

Everything works fine for a while. However, after a random time, a failed assertion causes the system to either enter a break condition (if the JLink debugger is connected), or reset.

The failed assertion is in the nrf_802154_csma_ca_start() function, called by otPlatRadioTransmit(). By looking at the source code of the _ca_start() function, it appears that the only way it can lead to a failed assert is that it is called before the previous operation ends. In particular:

void nrf_802154_csma_ca_start(const uint8_t * p_data)
{
    assert(!procedure_is_running());
    
    mp_data = p_data;
    m_nb = 0;
    m_be = NRF_802154_CSMA_CA_MIN_BE;
    m_is_running = true;
    
    random_backoff_start();
}

I've also attached the complete call stack when the failed assert happens:

Can anybody help with this? Am I doing something wrong or should I do something different for initializing/deinitializing the openthread stack?

Thank you!

  • Hi,

    Yes, it looks like something isn't finished from the previous operation. Could you perhaps share your code with us to review and see what you are doing?

    Best regards,

    Marjeris

  • Hello,

    sure, i'll try to share you the relevant parts. However, this is not my application code, but simply a modified Thread CLI example that i did in order to reproduce the problem.

    The main function creates a timer, "connect_timer_id", used to decide when to connect. In the main loop, this is the code:

        while(true) 
        {
            if (connect_timer_expired)
            {
                connect_timer_expired = 0;
                if (thread_state == STATE_DISCONNECTED)
                {
                    thread_state = STATE_CONNECTING;
                    thread_network_up();
                }
                else if (thread_state == STATE_CONNECTED)
                {
                    thread_state = STATE_DISCONNECTED;
                    thread_network_down();
                    err = app_timer_start(connect_timer_id, APP_TIMER_TICKS(7000), (uint32_t *)0x1);
                    APP_ERROR_CHECK(err);
                }
            }
            
            if (thread_instance_initialized) {
                thread_process();
            }
            
            NRF_LOG_PROCESS();
        }

    connect_timer_expired is set in interrupt context upon timer expiration.

    The thread_network_up() function configures the thread network parameters and starts a radio scan using otLinkActiveScan. Once the scan finds the configured thread network, the system enables the remaining thread stack by calling thread_interface_enable(), defined as:

    static void thread_interface_enable(bool enabled)
    {
        otError error;
        /* Start the Thread network interface (CLI cmd > ifconfig up) */
        otInstance *instance = thread_ot_instance_get();
    
        error = otIp6SetEnabled(instance, enabled);
        APP_ERROR_CHECK(error);
    
        /* Start the Thread stack (CLI cmd > thread start) */
        error = otThreadSetEnabled(instance, enabled);
        APP_ERROR_CHECK(error);
        
        NRF_LOG_INFO("Thread interface %s", enabled ? "enabled" : "disabled");
    }

    Once the device is connected to the thread network, the timer is started again to schedule a disconnection. This is part of the thread_state_changed_callback function:

    static void thread_state_changed_callback(uint32_t flags, void * p_context)
    {
        otDeviceRole role = otThreadGetDeviceRole(p_context);
        NRF_LOG_INFO("State changed! Flags: 0x%08x Current role: %d\r\n",
                     flags, role);
        
        [...]
        if (role >= OT_DEVICE_ROLE_CHILD)
        {
            thread_state = STATE_CONNECTED;
            uint32_t err = app_timer_start(connect_timer_id, APP_TIMER_TICKS(1500), (uint32_t *)0x1);
            APP_ERROR_CHECK(err);    
        }
        
        [...]
    }

    Basically this is it.

  • Hi,

    I am so sorry for not getting back to you on this one, it looks like I forgot to reply. I don't see anything it your code snippets that seem unormal to me. Are you still struggling with this? Could you give me an update on how it's going with your project?

    Again I am so sorry I didn't see this ticket wasn't reply to before now...

    Best regards,

    Marjeris

Related