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

802.15.4 sys_sleep_request_ms does not work

Hi all,

I'm using NRF SDK v17.0.2 and IEEE 802.15.4 stack library. I want to implement sleeping device. I think I should use sys_sleep_request_ms function to make the device go to sleep but calling it does nothing. If I use  sys_sleep_approver_register to register callbacks for SYS_EVENT_FALLING_ASLEEP and SYS_EVENT_WAKE_UP events (and call sys_sleep_approve in SYS_EVENT_FALLING_ASLEEP event) I see that the callback are called but the SYS_EVENT_WAKE_UP event is emitted immediately after SYS_EVENT_FALLING_ASLEEP event.

So I digged deeper and dissassembled the components/802_15_4/raw/802_15_4_lib_gcc.a library (and all other variants of this library ) to see if there are any calls to wfi or wfe instructions and to my supprise there are none.

This is the dissassembly of the hal_sleep function:

00000000 <hal_sleep>:
   0:	2000      	movs	r0, #0
   2:	4770      	bx	lr

It looks like this function is just returning value 0, what would translate to UNKNOWN_INTERRUPT enum. Do I need to override this function (it is not declared extern so I don't think so) ?

So my question is this library supports sleeping devices ? I would imagine that this library would allow me to implement a device that would wakeup from sleep before the beacon arrival, then go to sleep after the beacon if no data tranfers are pending. Is that possible with this library ?

Nest regards,

Marek Czerski

  • I can't find any references to sys_sleep_request_ms in the SDK. Where did you find this?

    If you are still planning on learning about 802.15.4 before you start developing using zigbee, please be aware that the Zigbee stack from the SDK uses a different 802.15.4 stack, and that you don't use the 802.15.4 stack directly when using the zboss stack (the name of the Zigbee stack).

  • Ok,

    Assuming you are using raw 802.15.4 and don't intend to use Zigbee then, right?

    What do you see if you don't use sys_sleep_approver_register()? Does the device go to sleep for the correct amount of time when you call sys_sleep_request_ms()?

    Did you remember to call sys_sleep_init() before calling sys_sleep_request_ms()?

    What is the sys_sleep_wakeup_reason() after you wake up immediately after calling sys_sleep_request_ms()

    As I mentinoned in one of your previous tickets, this library is not much used, and noone currently working in Nordic is particularly familiar with it. When I ask our network group about this library they will point us to the 802.15.4 driver used in the thread and zigbee SDK, so I guess they will do the same if I ask them about this.

    I have never tested the sleep implementation in the 802154 library. Do you have an example where you have set it up that I can test?

    BR,
    Edvin

  • Hi Edvin,

    Thanks for the response.

    Assuming you are using raw 802.15.4 and don't intend to use Zigbee then, right?

    yes.

    What do you see if you don't use sys_sleep_approver_register()? Does the device go to sleep for the correct amount of time when you call sys_sleep_request_ms()?

    No effect, the device is not going to sleep.

    Did you remember to call sys_sleep_init() before calling sys_sleep_request_ms()?

    Yes, at the application startup. But I guess that sys_sleep_init is already called by the sys_init.

    What is the sys_sleep_wakeup_reason() after you wake up immediately after calling sys_sleep_request_ms()

    It is 0.

    As I mentinoned in one of your previous tickets, this library is not much used, and noone currently working in Nordic is particularly familiar with it.

    Up until now this library is working good for me. The only problem is the documentation which does not describe very important things like resource ownership, api calls invariants, etc. This would be no problem if the source code was available.

    on this page (https://www.nordicsemi.com/Software-and-tools/Software/802-15-4-software) I can read:

    "For those who wish to utilize another protocol stack, or implement their own, we offer complete PHY and MAC support for the nRF52840 multiprotocol SoC."

    Is it refering to 802.15.4 library that I use or the other piece of software ?

    When I ask our network group about this library they will point us to the 802.15.4 driver used in the thread and zigbee SDK, so I guess they will do the same if I ask them about this.

    As I understand, the 802.15.4 driver You mention is this (https://github.com/NordicSemiconductor/nRF-IEEE-802.15.4-radio-driver) and it is not a proper 802.15.4 mac layer implementation, so I would need to implement a lot more code. I can't find any documentation on it also.

    I have never tested the sleep implementation in the 802154 library. Do you have an example where you have set it up that I can test?

    Below is my test code and log output. It assumes that there is PAN coordinator on the other side, and the sys_sleep_request_ms is called only after succesfull association.

    #include "config.h"
    #include "ral_irq_handlers.h"
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "nrf_delay.h"
    #include "app_usbd.h"
    #include "bsp.h"
    
    #include "nrf_drv_clock.h"
    
    #include "mac_mlme_pib.h"
    #include "mac_mlme_reset.h"
    #include "mac_mlme_scan.h"
    #include "mac_mlme_associate.h"
    #include "mac_mlme_sync.h"
    #include "mac_mcps_data.h"
    #include "sys_init.h"
    #include "sys_memory_manager.h"
    #include "sys_sleep.h"
    #include "sys_task_scheduler.h"
    #include "boards.h"
    
    static mac_pan_descriptor_t pan_descriptor;
    uint16_t my_addr = 0xFFFF;
    static bool allow_sleep = false;
    static sys_sleep_approver_id_t approver_id;
    
    void mcps_data_ind(mcps_data_ind_t * p_ind)
    {
        bool addresses_match = p_ind->dst_pan_id == pan_descriptor.coord_pan_id &&
                               p_ind->dst_addr_mode == pan_descriptor.coord_addr_mode &&
                               p_ind->dst_addr.short_address == my_addr &&
                               p_ind->src_pan_id == pan_descriptor.coord_pan_id;
    
        NRF_LOG_INFO("%d received success from %x", sys_time_get(), p_ind->src_addr.short_address);
        if (addresses_match && p_ind->msdu_length > 0)
        {
            if (bsp_board_led_state_get(CONFIG_DOWNSTREAM_PIN))
                bsp_board_led_off(CONFIG_DOWNSTREAM_PIN);
            else
                bsp_board_led_on(CONFIG_DOWNSTREAM_PIN);
        }
        mac_mem_msdu_free(&p_ind->msdu);
    }
    
    static void mcps_data_conf(mcps_data_conf_t * conf)
    {
        if (conf->status == MAC_SUCCESS)
        {
            NRF_LOG_INFO("%d send success", sys_time_get());
            bsp_board_led_off(CONFIG_UPSTREAM_PIN);
        }
        else
        {
            NRF_LOG_INFO("%d send failed", sys_time_get());
        }
    }
    
    static void send_data()
    {
        static uint8_t m_radio_tx_buffer[PHY_MAX_PACKET_SIZE + MAC_MAX_MHR_SIZE];
        static mcps_data_req_t m_data_req;
        NRF_LOG_INFO("%d send_data", sys_time_get());
    
        m_radio_tx_buffer[MAC_MAX_MHR_SIZE] = 'x';
    
        m_data_req.dst_addr_mode = pan_descriptor.coord_addr_mode;
        m_data_req.dst_addr = pan_descriptor.coord_address;
        m_data_req.dst_pan_id = pan_descriptor.coord_pan_id;
        m_data_req.src_addr_mode = MAC_ADDR_SHORT;
        m_data_req.msdu = (uint8_t *)&m_radio_tx_buffer[MAC_MAX_MHR_SIZE];
        m_data_req.msdu_length = 1;
        m_data_req.msdu_handle++;
        m_data_req.tx_options.ack = true;
        m_data_req.tx_options.gts = false;
        m_data_req.tx_options.indirect = false;
        mcps_data_req(&m_data_req, mcps_data_conf);
    
        bsp_board_led_on(CONFIG_UPSTREAM_PIN);
    }
    
    static void ext_addr_set()
    {
        const pib_id_t pib_id =
        {
            .mlme_id = MAC_EXTENDED_ADDRESS,
        };
        uint64_t address = CONFIG_IEEE_ADDRESS + CONFIG_DEVICE_SHORT_ADDRESS;
        mac_status_t result = mlme_set(pib_id, 0, &address);
        NRF_LOG_INFO("ext_addr_set: %d", result);
    }
    
    //static void auto_request_set()
    //{
    //    const pib_id_t pib_id =
    //    {
    //        .mlme_id = MAC_AUTO_REQUEST,
    //    };
    //    uint8_t auto_request = 0;
    //    mac_status_t result = mlme_set(pib_id, 0, &auto_request);
    //    NRF_LOG_INFO("auto_request_set: %d", result);
    //}
    
    static void beacon_order_set(uint8_t beacon_order)
    {
        const pib_id_t pib_id =
        {
            .mlme_id = MAC_BEACON_ORDER,
        };
        mac_status_t result = mlme_set(pib_id, 0, &beacon_order);
        NRF_LOG_INFO("beacon_order_set: %d", result);
    }
    
    static void mlme_associate_conf_cb(mlme_associate_conf_t * conf)
    {
        NRF_LOG_INFO("%d mlme_associate_conf: %d", sys_time_get(), conf->status);
        if (conf->status == MAC_SUCCESS)
            bsp_board_led_on(CONFIG_INIT_DONE_PIN);
        else
            bsp_board_led_off(CONFIG_INIT_DONE_PIN);
    
        my_addr = conf->assoc_short_address;
        NRF_LOG_INFO("short_address: %x", my_addr);
        allow_sleep = true;
    }
    
    static void log_mac_pan_descriptor(const mac_pan_descriptor_t *mac_pan_descriptor)
    {
        NRF_LOG_INFO("coord_addr_mode: %d", mac_pan_descriptor->coord_addr_mode);
        NRF_LOG_INFO("coord_pan_id: %x", mac_pan_descriptor->coord_pan_id);
        NRF_LOG_INFO("coord_address.short_address: %x", mac_pan_descriptor->coord_address.short_address);
        NRF_LOG_INFO("coord_address.long_address: %x", mac_pan_descriptor->coord_address.long_address);
        NRF_LOG_INFO("logical_channel: %d", mac_pan_descriptor->logical_channel);
        NRF_LOG_INFO("superframe_spec.beacon_order %d", mac_pan_descriptor->superframe_spec.beacon_order);
        NRF_LOG_INFO("superframe_spec.superframe_order %d", mac_pan_descriptor->superframe_spec.superframe_order);
        NRF_LOG_INFO("superframe_spec.final_cap_slot %d", mac_pan_descriptor->superframe_spec.final_cap_slot);
        NRF_LOG_INFO("superframe_spec.battery_life_extension %d", mac_pan_descriptor->superframe_spec.battery_life_extension);
        NRF_LOG_INFO("superframe_spec.pan_coordinator %d", mac_pan_descriptor->superframe_spec.pan_coordinator);
        NRF_LOG_INFO("superframe_spec.association_permit %d", mac_pan_descriptor->superframe_spec.association_permit);
        NRF_LOG_INFO("gts_permit %d", mac_pan_descriptor->gts_permit);
        NRF_LOG_INFO("link_quality %d", mac_pan_descriptor->link_quality);
        NRF_LOG_INFO("timestamp %d", mac_pan_descriptor->timestamp);
    }
    
    static void mlme_scan_conf_cb(mlme_scan_conf_t * conf)
    {
        NRF_LOG_INFO("%d mlme_scan_conf: %d", sys_time_get(), conf->status);
        if (conf->status != MAC_SUCCESS)
            return;
        NRF_LOG_INFO("unscanned channels: %x", conf->unscanned_channels);
        NRF_LOG_INFO("results: %d",conf->result_list_size);
        if (conf->result_list_size == 0)
            return;
        NRF_LOG_INFO("first result");
        pan_descriptor = conf->pan_descriptor_list[0];
        log_mac_pan_descriptor(&pan_descriptor);
    
        {
            beacon_order_set(pan_descriptor.superframe_spec.beacon_order);
            static mlme_sync_req_t req;
            req = (mlme_sync_req_t){
                .logical_channel = pan_descriptor.logical_channel,
                .track_beacon = true
            };
            NRF_LOG_INFO("syncing to channel %d", req.logical_channel);
            mlme_sync_req(&req);
        }
    
        static mlme_associate_req_t req;
        req = (mlme_associate_req_t){
            .logical_channel = pan_descriptor.logical_channel,
            .coord_addr_mode = pan_descriptor.coord_addr_mode,
            .coord_pan_id = pan_descriptor.coord_pan_id,
            .coord_address = pan_descriptor.coord_address,
            .capability_information = {
                .alternate_pan_coordinator = 0,
                .device_type = 0,
                .power_source = 0,
                .rx_on_when_idle = 0,
                .reserved = 0,
                .security_capability = 0,
                .allocate_address = 1
            }
        };
        mlme_associate_req(&req, mlme_associate_conf_cb);
    }
    
    void mlme_reset_conf_cb(mlme_reset_conf_t *conf)
    {
        NRF_LOG_INFO("%d mlme_reset_conf: %d", sys_time_get(), conf->status);
        if (conf->status != MAC_SUCCESS) {
            bsp_board_led_off(CONFIG_INIT_DONE_PIN);
            return;
        }
    
        ext_addr_set();
    
        static mac_pan_descriptor_t pan_descriptors_buf[2];
        static mlme_scan_req_t req = {
            .scan_type = ACTIVE_SCAN,
            .scan_channels = (1 << 15), //PHY_CHANNEL_SUPPORTED,
            .scan_duration = 8,
            .pan_descriptors_buf_size = sizeof(pan_descriptors_buf) / sizeof(pan_descriptors_buf[0]),
            .pan_descriptors_buf = pan_descriptors_buf,
            .energy_detect_buf_size = 0,
            .energy_detect_buf = NULL,
        };
        mlme_scan_req(&req, mlme_scan_conf_cb);
    }
    
    static void out_of_memory_callback(const void * data)
    {
        bsp_board_led_on(CONFIG_ERROR_PIN);
    }
    
    static void memory_freed_callback(const void * data)
    {
        bsp_board_led_off(CONFIG_ERROR_PIN);
    }
    
    static void falling_asleep_callback(const void * data)
    {
        NRF_LOG_INFO("%d falling_asleep_callback", sys_time_get());
        sys_sleep_approve(approver_id);
    }
    
    static void wake_up_callback(const void * data)
    {
        NRF_LOG_INFO("%d wake_up_callback", sys_time_get());
    }
    
    /**@brief   Application task initialization for GPIO test.
     */
    static void app_task_init(void)
    {
        static sys_event_desc_t m_out_of_memory_desc = {
            .event_id = SYS_EVENT_OUT_OF_MEMORY,
            .callback = out_of_memory_callback,
        };
        static sys_event_desc_t m_memory_freed_desc = {
            .event_id = SYS_EVENT_MEMORY_FREED,
            .callback = memory_freed_callback,
        };
        static sys_event_desc_t m_falling_asleep_desc = {
            .event_id = SYS_EVENT_FALLING_ASLEEP,
            .callback = falling_asleep_callback,
        };
        static sys_event_desc_t m_wake_up_desc = {
            .event_id = SYS_EVENT_WAKE_UP,
            .callback = wake_up_callback,
        };
        static uint8_t __ALIGN(ALIGN_VALUE) m_heap[CONFIG_POOL_SIZE];
        static const size_t m_heap_size = CONFIG_POOL_SIZE;
    
        sys_init(m_heap, m_heap_size);
        sys_sleep_init();
        approver_id = sys_sleep_approver_register(&m_falling_asleep_desc, &m_wake_up_desc);
        NRF_LOG_INFO("aprover id is %d", approver_id);
    
        sys_event_subscribe(&m_out_of_memory_desc);
        sys_event_subscribe(&m_memory_freed_desc);
        //sys_event_subscribe(&m_falling_asleep_desc);
        //sys_event_subscribe(&m_wake_up_desc);
    
        static mlme_reset_req_t req = {
            .set_default_pib = true
        };
        mlme_reset_req(&req, mlme_reset_conf_cb);
    }
    
    void mlme_sync_loss_ind(mlme_sync_loss_ind_t * ind)
    {
        NRF_LOG_INFO("%d mlme_sync_loss for PAN ID: %x on channel %d", sys_time_get(), ind->pan_id, ind->logical_channel);
        NRF_LOG_INFO("reason: %d", ind->loss_reason);
    }
    
    void mlme_beacon_notify_ind(mlme_beacon_notify_ind_t *ind)
    {
        NRF_LOG_INFO("%d mlme_beacon_notify_ind bsn: %d", sys_time_get(), ind->bsn); 
        mac_mem_msdu_free(&ind->sdu);
    }
    
    static void clock_init(void)
    {
        ret_code_t err_code = nrf_drv_clock_init();
        ASSERT((err_code == NRF_SUCCESS) || (err_code == NRF_ERROR_MODULE_ALREADY_INITIALIZED));
    
        nrf_drv_clock_hfclk_request(NULL);
        while (!nrf_drv_clock_hfclk_is_running())
        {
            // spin lock
        }
    
        nrf_drv_clock_lfclk_request(NULL);
        while (!nrf_drv_clock_lfclk_is_running())
        {
            // spin lock
        }
    }
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        ral_irq_handler_import();
        clock_init();
        APP_ERROR_CHECK(bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, NULL));
        bsp_board_leds_off();
        APP_ERROR_CHECK(app_usbd_init(NULL));
        app_usbd_enable();
        app_usbd_start();
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
        for (int i=0; i<300; i++)
        {
            while (app_usbd_event_queue_process());
            if (i % 2)
                bsp_board_leds_off();
            else
                bsp_board_leds_on();
            nrf_delay_ms(10);
        }
        bsp_board_leds_on();
        NRF_LOG_INFO("Blinky device example started.");
        bsp_board_leds_off();
    
        app_task_init();
    
        bool prev_button = bsp_board_button_state_get(CONFIG_BTN_DATA_SEND);
        bool curr_button = 0;
        while (true)
        {
            while (app_usbd_event_queue_process());
            sys_task_run();
            curr_button = bsp_board_button_state_get(CONFIG_BTN_DATA_SEND);
            if (prev_button == 0 && curr_button == 1) {
                send_data();
            }
            prev_button = curr_button;
            if (allow_sleep) {
                allow_sleep = false;
                NRF_LOG_INFO("%d going sleep", sys_time_get());
                sys_sleep_request_ms(10000);
                NRF_LOG_INFO("%d woke up, reason: %d", sys_time_get(), sys_sleep_wakeup_reason());
            }
        }
    }

    <info> app: Blinky device example started.
    <info> app: aprover id is 0
    <info> app: 1138 mlme_reset_conf: 0
    <info> app: ext_addr_set: 0
    <info> app: 3952220 mlme_scan_conf: 0
    <info> app: unscanned channels: 0
    <info> app: results: 1
    <info> app: first result
    <info> app: coord_addr_mode: 2
    <info> app: coord_pan_id: 1234
    <info> app: coord_address.short_address: A
    <info> app: coord_address.long_address: A
    <info> app: logical_channel: 15
    <info> app: superframe_spec.beacon_order 7
    <info> app: superframe_spec.superframe_order 5
    <info> app: superframe_spec.final_cap_slot 15
    <info> app: superframe_spec.battery_life_extension 0
    <info> app: superframe_spec.pan_coordinator 1
    <info> app: superframe_spec.association_permit 1
    <info> app: gts_permit 1
    <info> app: link_quality 255
    <info> app: timestamp 1654218
    <info> app: beacon_order_set: 0
    <info> app: syncing to channel 15
    <info> app: 7560187 mlme_associate_conf: 0
    <info> app: short_address: CDFA
    <info> app: 7560666 going sleep
    <info> app: 7560834 falling_asleep_callback
    <info> app: 7561030 wake_up_callback
    <info> app: 7561217 woke up, reason: 0

    Best regards,

    Marek Czerski

  • Hello,

    Sorry for the late reply. I missed the internal reply. I am sorry.

    So I asked our 802.15.4 team about this, and their reply was:

    --------------------------
    The state of this IEEE 802.15.4 stack is that it was developed externally (therefore it is distributed in a binary form) as a standalone project. By "standalone" I mean the developers who worked on this, extracted from SDK or developed internally any components that required HW access (peripheral drivers, Radio configuration, MDK and cortex libs). And they added a configuration option to indicate whether to use those internal drivers, or the stack has to integrate with external components. The stack available in the nRF5 SDK uses the latter config to integrate with the SDK components (drivers and libraries) and not the internal components of the 802.15.4 stack project.
    Unfortunately the config option is not properly reflected in the API documentation, but the understanding should be, that anything that accesses the nRF52 HW except for the RADIO and TIMERs used by the stack, should be handled externally. In this case the user should call the WFE() CMSIS function to put the device to seelp. The sys_sleep() API can only handle the sleep approving logic (checking if other components approve entering the sleep state).
    --------------------------
    So it looks like the API is not putting the device to sleep. Only providing the events that you can do so in.
    To see how to put the device to sleep, I suggest that you check out some of the other examples in the SDK that uses the nrf_pwr_mgmt_run().

    See e.g. the ble_app_hrs example. In the main loop, you have:
    idle_state_handle() -> nrf_pwr_mgmt_run() -> 
        {
            // Wait for an event.
            __WFE();
            // Clear the internal event register.
            __SEV();
            __WFE();
        }
    Best regards,
    Edvin
Related