nRF9161 to lowest power mode

I am trying to make the nrf9161 go to very low power mode for 10 seconds and then come back online. 

Actually I would not mind if it were to start all over again when coming back around but currently on my nrf9161dk I cannot achieve < 3.2mA. 

I could not find any right source with an example but if someone has a good example source, please let me know. Otherwise, why doesn't the code that I provide here bring me anywhere below 3.2mA? 

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <hal/nrf_power.h>
#include <hal/nrf_clock.h>
#include <hal/nrf_rtc.h>
#include <modem/lte_lc.h>

#define WAKEUP_SECONDS 10U  // Sleep duration in seconds

// Start LFCLK (needed for RTC)
static void start_lfclk(void)
{
    if (!NRF_CLOCK->LFCLKRUN) {
        NRF_CLOCK->LFCLKSRC = 2U; // LFXO (external crystal)
        NRF_CLOCK->TASKS_LFCLKSTART = 1;
        while (!NRF_CLOCK->EVENTS_LFCLKSTARTED) {}
    }
}

// Configure RTC0 as wake timer
static void setup_rtc_wakeup(uint32_t seconds)
{
    // RTC0 runs at 32.768 kHz / (prescaler + 1)
    nrf_rtc_prescaler_set(NRF_RTC0, 0); 
    nrf_rtc_cc_set(NRF_RTC0, 0, seconds * 32768UL);
    nrf_rtc_int_enable(NRF_RTC0, NRF_RTC_INT_COMPARE0_MASK);
    nrf_rtc_task_trigger(NRF_RTC0, NRF_RTC_TASK_START);
}

// Enter System OFF (deep sleep)
static void enter_system_off(void)
{
    // Must power down LTE modem first
    lte_lc_power_off();

    // Ensure all memory operations completed
    __DSB();
    __WFI(); // Wait for interrupt; chip powers down
}

// Check if we woke from System OFF
static bool woke_from_system_off(void)
{
    return (NRF_POWER->RESETREAS & (1U << 2)) != 0; // Bit 2 = OFF
}

// Clear wake reason
static void clear_wake_reason(void)
{
    NRF_POWER->RESETREAS = NRF_POWER->RESETREAS;
}

int main(void)
{
    while (1) {
        if (woke_from_system_off()) {
            printk("Woke from System OFF!\n");
            clear_wake_reason();
        } else {
            printk("Power-on or other reset\n");
        }

        // Start LFCLK for RTC
        start_lfclk();

        // Set RTC wakeup
        setup_rtc_wakeup(WAKEUP_SECONDS);
        printk("Entering System OFF for %d seconds...\n", WAKEUP_SECONDS);

        // Enter deep sleep
        enter_system_off();

        // Execution never reaches here in System OFF
        while (1) {
            k_sleep(K_MSEC(1000));
        }
    }
    return 0;
}

Parents
  • Hi Mart,

    We have some documentation on power management (link) and also for cellular focused application (link). If you haven't done it yet, I would also recommend checking the Dev Academy, where we have a course on cellular iot (link). It contains a chapter on power saving techniques.

    For a more complete sample, I would recommend checking the simple tracker solution from the Cellular iot course on Dev Academy.

    If you need more information, feel free to ask!

    Best regards,

    Simon D-M

  • Thank you for the answer, indeed I had to take a deeper look. 

    This now is what I achieved and it gets me running to uA levels, very awesome!

    prj.conf

    ########################################
    # Core Zephyr / Kernel
    ########################################
    CONFIG_MAIN_STACK_SIZE=4096
    CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
    
    ########################################
    # Modem / LTE
    ########################################
    CONFIG_NRF_MODEM_LIB=y
    CONFIG_LTE_LINK_CONTROL=y
    
    # Request Power Saving Mode (PSM)
    CONFIG_LTE_PSM_REQ=y
    
    # Requested PSM timers (network may override)
    # TAU ≈ 1 hour
    CONFIG_LTE_PSM_REQ_RPTAU="00100100"
    
    # Active time ≈ 2 seconds
    CONFIG_LTE_PSM_REQ_RAT="00000101"
    
    ########################################
    # Logging (DEBUG ONLY)
    ########################################
    CONFIG_LOG=y
    CONFIG_LOG_MODE_DEFERRED=y
    CONFIG_LOG_DEFAULT_LEVEL=3
    CONFIG_PRINTK=y
    
    ########################################
    # Power optimization
    ########################################
    CONFIG_PM=y
    CONFIG_PM_DEVICE=y
    
    ########################################
    # Disable unused features (important!)
    ########################################
    CONFIG_USB_DEVICE_STACK=n
    CONFIG_SERIAL=n
    CONFIG_UART_CONSOLE=n
    
    

    main.c

    #include <zephyr/kernel.h>
    #include <modem/lte_lc.h>
    #include <modem/nrf_modem_lib.h>
    #include <zephyr/logging/log.h>
    
    LOG_MODULE_REGISTER(psm_example, LOG_LEVEL_INF);
    
    static void lte_handler(const struct lte_lc_evt *const evt)
    {
        switch (evt->type) {
    
        case LTE_LC_EVT_NW_REG_STATUS:
            if ((evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME) ||
                (evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_ROAMING)) {
                LOG_INF("LTE registered");
            }
            break;
    
        case LTE_LC_EVT_RRC_UPDATE:
            LOG_INF("RRC mode: %s",
                    evt->rrc_mode == LTE_LC_RRC_MODE_CONNECTED ?
                    "Connected" : "Idle");
            break;
    
        default:
            break;
        }
    }
    
    static int modem_configure(void)
    {
        int err;
    
        LOG_INF("Initializing modem library");
    
        err = nrf_modem_lib_init();
        if (err) {
            LOG_ERR("Failed to initialize the modem library, error: %d", err);
            return err;
        }
    
        LOG_INF("Connecting to LTE network");
        err = lte_lc_connect_async(lte_handler);
        if (err) {
            LOG_ERR("Error in lte_lc_connect_async, error: %d", err);
            return err;
        }
    
        return 0;
    }
    
    int main(void)
    {
        int err;
    
        LOG_INF("Starting PSM example");
    
        err = modem_configure();
        if (err) {
            LOG_ERR("Failed to configure the modem");
            return 0;
        }
    
        while (1) {
            // Disconnect from LTE to save power
            LOG_INF("Disconnecting from LTE");
            err = lte_lc_func_mode_set(LTE_LC_FUNC_MODE_DEACTIVATE_LTE);
            if (err) {
                LOG_ERR("Failed to deactivate LTE, error: %d", err);
                return 0;
            }
    
            // Wait for 20 seconds
            k_msleep(20000);
    
            // Reconnect to LTE
            LOG_INF("Reconnecting to LTE");
            err = lte_lc_func_mode_set(LTE_LC_FUNC_MODE_NORMAL);
            if (err) {
                LOG_ERR("Failed to activate LTE, error: %d", err);
                return 0;
            }
    
            // Wait for 1 second
            k_msleep(1000);
        }
    
        return 0;
    }
    
    

    The only thing that I am not sure about is why having CONFIG_ASSERT=y in my prj.conf drastically increase the power to over mA levels, even during sleep conditions. 

Reply
  • Thank you for the answer, indeed I had to take a deeper look. 

    This now is what I achieved and it gets me running to uA levels, very awesome!

    prj.conf

    ########################################
    # Core Zephyr / Kernel
    ########################################
    CONFIG_MAIN_STACK_SIZE=4096
    CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
    
    ########################################
    # Modem / LTE
    ########################################
    CONFIG_NRF_MODEM_LIB=y
    CONFIG_LTE_LINK_CONTROL=y
    
    # Request Power Saving Mode (PSM)
    CONFIG_LTE_PSM_REQ=y
    
    # Requested PSM timers (network may override)
    # TAU ≈ 1 hour
    CONFIG_LTE_PSM_REQ_RPTAU="00100100"
    
    # Active time ≈ 2 seconds
    CONFIG_LTE_PSM_REQ_RAT="00000101"
    
    ########################################
    # Logging (DEBUG ONLY)
    ########################################
    CONFIG_LOG=y
    CONFIG_LOG_MODE_DEFERRED=y
    CONFIG_LOG_DEFAULT_LEVEL=3
    CONFIG_PRINTK=y
    
    ########################################
    # Power optimization
    ########################################
    CONFIG_PM=y
    CONFIG_PM_DEVICE=y
    
    ########################################
    # Disable unused features (important!)
    ########################################
    CONFIG_USB_DEVICE_STACK=n
    CONFIG_SERIAL=n
    CONFIG_UART_CONSOLE=n
    
    

    main.c

    #include <zephyr/kernel.h>
    #include <modem/lte_lc.h>
    #include <modem/nrf_modem_lib.h>
    #include <zephyr/logging/log.h>
    
    LOG_MODULE_REGISTER(psm_example, LOG_LEVEL_INF);
    
    static void lte_handler(const struct lte_lc_evt *const evt)
    {
        switch (evt->type) {
    
        case LTE_LC_EVT_NW_REG_STATUS:
            if ((evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME) ||
                (evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_ROAMING)) {
                LOG_INF("LTE registered");
            }
            break;
    
        case LTE_LC_EVT_RRC_UPDATE:
            LOG_INF("RRC mode: %s",
                    evt->rrc_mode == LTE_LC_RRC_MODE_CONNECTED ?
                    "Connected" : "Idle");
            break;
    
        default:
            break;
        }
    }
    
    static int modem_configure(void)
    {
        int err;
    
        LOG_INF("Initializing modem library");
    
        err = nrf_modem_lib_init();
        if (err) {
            LOG_ERR("Failed to initialize the modem library, error: %d", err);
            return err;
        }
    
        LOG_INF("Connecting to LTE network");
        err = lte_lc_connect_async(lte_handler);
        if (err) {
            LOG_ERR("Error in lte_lc_connect_async, error: %d", err);
            return err;
        }
    
        return 0;
    }
    
    int main(void)
    {
        int err;
    
        LOG_INF("Starting PSM example");
    
        err = modem_configure();
        if (err) {
            LOG_ERR("Failed to configure the modem");
            return 0;
        }
    
        while (1) {
            // Disconnect from LTE to save power
            LOG_INF("Disconnecting from LTE");
            err = lte_lc_func_mode_set(LTE_LC_FUNC_MODE_DEACTIVATE_LTE);
            if (err) {
                LOG_ERR("Failed to deactivate LTE, error: %d", err);
                return 0;
            }
    
            // Wait for 20 seconds
            k_msleep(20000);
    
            // Reconnect to LTE
            LOG_INF("Reconnecting to LTE");
            err = lte_lc_func_mode_set(LTE_LC_FUNC_MODE_NORMAL);
            if (err) {
                LOG_ERR("Failed to activate LTE, error: %d", err);
                return 0;
            }
    
            // Wait for 1 second
            k_msleep(1000);
        }
    
        return 0;
    }
    
    

    The only thing that I am not sure about is why having CONFIG_ASSERT=y in my prj.conf drastically increase the power to over mA levels, even during sleep conditions. 

Children
Related