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. 

  • Hi,

    I haven't looked into details, but I think that when you enable the asserts, it enables some more debugging stuff which can maybe prevent the chip from going into sleep or also maybe enable some peripherals that were disabled before.

    Best regards,

    Simon D-M

Reply Children
No Data
Related