Reg the sleep modes and its configuration or library requirements (#includes) for nRF52833

Hi, 

We are working on the nRF52833 in one of our Gateway applications. We want to keep our Gateway in deep sleep (System OFF) mode if it is not configured or On-boarded. And should be in wakeup sleep mode after the On-boarding. We need 2 wakeup mechanisms in our firmware as below.

1. Before Onboarding - Should be in deep sleep mode and should allow only the GPIO interrupt.

2. After Onboarding - Device should be in sleep mode and should be able wakeup with the timer interrupt or GPIO interrupt.

Development Environment: VS Code (NCS v2.5.1)

I found there are many tickets raised in this regard, but unable to understand that the explanations or solutions are not the same in the discussions.

I want to understand the function calls, project config and header files (#include) requirements for the sleep modes. Because I am unable to access the functions which were share in the other tickets.

Please help me with the sleep functions and its library requirements to use in my code to achieve the best sleep mode currents for the longer battery life.

  • Hello,

    Whenever you enter system off mode, whatever gpios you set up for interrupts before you went to system off will still be active. Note that all timers (LFCLK) will be stopped when entering system off mode. But what you would need is to enable GPIO interrupts before going to system off, as long as you are not onboarded.

    When you are onboarded, all you need to do is to make sure that you call k_sleep() in one of your threads. This will wake you up when the timer times out again. It will also wake up on GPIO interrupts if that happens before the timer runs out.

    Please look at our samples. You can refer to the peripheral_uart found in ncs\nrf\samples\bluetooth\peripheral_uart, which has a button handler (button_changed()) and a k_sleep() in it's main thread.

    Best regards,

    Edvin

  • Hi Edvin,

    Thanks for your information. Somehow it is clear.

    I tried testing the sample code "peripheral_uart" and found the sample code is consuming around ~200uA even after commenting the LED and the BLE advertising logic. By default, the same code consuming the few mA if I use the sample code directly due to LED and the BLE advertising.

    I need few clarifications and help in these sleep modes.

    System OFF sleep mode:

    - Please help with the logic for this sleep mode and its dependent libraries and configurations. Advise if there is any sample code available for this. 

    We understood initializing the GPIO interrupt to wake up from this sleep mode.

    System ON sleep mode:

    As per your information, it seems the k_sleep() can do the system ON sleep in idle mode and allows the GPIO and timer interrupts to handle the application.

    - Is this k_sleep() alone can help in achieving the best sleep currents? Or do we need add any additional commands or functions to it?

    - There are 2 active UARTs in my application. Will they go into sleep in both the sleep modes? or do we need to make the UARTs sleep while entering into the sleep mode? Please advise.

    I am just sharing my application block diagram for your reference.

  • The reason why you see the peripheral_uart drawing more power is probably due to the UART being left on. It requires the HFCLK to run, which is why you see that increased current consumption.

    To enter system off mode, you can use either the native nRF API: NVIC_Systemoff(); or you can use Zephyr's: sys_poweroff();

    Vivek_P said:
    - There are 2 active UARTs in my application. Will they go into sleep in both the sleep modes? or do we need to make the UARTs sleep while entering into the sleep mode? Please advise.

    Indeed, UARTs are power hungry, so if you want to conserve energy, you would need to disable them. This is to make the HFCLK stop when you enter system on mode. This will however mean that you don't have the possibility to receive UART messages while these are disabled. 

    To disable the UART you can use
    pm_device_action_run(uart_dev, PM_DEVICE_ACTION_SUSPEND);

    as described here:

     Problems putting UART into PM_DEVICE_ACTION_SUSPEND 

    Vivek_P said:
    Is this k_sleep() alone can help in achieving the best sleep currents?

    This in itself should be sufficient, but please use this as a default in all your threads (if you have several). If one thread is stuck in a while(true) loop then it will not enter system off. Using k_fifo_get() like it is done in one of the threads in the peripheral_uart sample is also fine. It will enter system on mode until this is given.

    Best regards,

    Edvin

  • Hi Edvin,

    Thanks for your detailed inputs.

    We tested the System ON sleep with our application logic and achieved the default sleep currents of ~100uA when UARTs are active and also ~22uA when the UARTs are inactive with the command pm_device_action_run(uart_dev, PM_DEVICE_ACTION_SUSPEND).

     

    We still want to achieve the best sleep currents. Do we have any other sleep modes or do we need to improve anything in our code to achieve the best sleep current like less than 10uA?

    Here I am sharing our application code with all peripherals and threads initialized (not the complete application code) for your reference.

    /*
     * Copyright (c) 2012-2014 Wind River Systems, Inc.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/devicetree.h>
    #include <stdio.h>
    #include <stdint.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <stddef.h>
    #include <hal/nrf_gpio.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/pm/device.h>
    // #include <zephyr/sys/cmsis_os.h>
    #include <zephyr/sys/poweroff.h>
    #include <soc.h>
    #include <zephyr/drivers/flash.h>
    #include <zephyr/storage/flash_map.h>
    #include <zephyr/fs/nvs.h>
    
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/sys/byteorder.h>
    
    #include <zephyr/drivers/uart.h>
    #include <zephyr/logging/log.h>
    
    #include <cJSON.h>
    
    uint8_t tmr_expired = 0;
    uint8_t sleep_flag=0,exit_flag=0;
    uint8_t tc=10;
    static uint16_t default_conn_handle;
    
    uint8_t dev_Awake=0, onboard=0, Ack=0, uart_flag=0, json_flag=0, mac_avail=0;
    const char *dataa;
    uint8_t buff[200]={0};
    #define BUFFER_ID 1
    
    //Bluetooth
    static struct bt_uuid_128 discover_uuid = BT_UUID_INIT_128(0);
    static struct bt_gatt_discover_params discover_params;
    static struct bt_gatt_subscribe_params subscribe_params;
    static struct bt_gatt_read_params read_params;
    struct bt_gatt_write_params write_params;//Gatt write
    uint16_t z;
    
    static uint8_t notify_func(struct bt_conn *conn,struct bt_gatt_subscribe_params *params,const void *data, uint16_t length);
    static void read_cb(struct bt_conn *conn, uint8_t err,struct bt_gatt_read_params *params,const void *data, uint16_t length);
    static uint8_t write_cb(struct bt_conn *conn, uint8_t err,struct bt_gatt_write_params *params,const void *data,uint16_t length);
    static uint8_t notifi_func(struct bt_conn *conn,
    			   struct bt_gatt_subscribe_params *params,
    			   const void *data, uint16_t length);
    
    //Data
    uint8_t *tx_buffer;
    uint16_t tx_length;
    bool conn_ver=false;
    uint8_t json_parse_flag=0;
    uint8_t tx_flag=0;
    uint8_t timer_flag=0;
    uint8_t conn_flag=0,retry_flag=0,devNo=0;
    uint8_t led_flag=0;
    uint8_t s=0;
    static struct bt_conn *default_conn;
    uint16_t rcc;
    uint32_t counter = 1U;
    
    //UART
    struct uart_data_t *rx;
    struct uart_data_t *tx_buf;
    	// #define UART_WAIT_FOR_RX CONFIG_BT_NUS_UART_RX_WAIT_TIME
    static const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(uart1));
    static const struct device *cons = DEVICE_DT_GET(DT_NODELABEL(uart0));
    static struct k_work_delayable uart_work;
    static bool rx_completed = false;
    #define LOG_MODULE_NAME uart_only
    LOG_MODULE_REGISTER(LOG_MODULE_NAME);
    #define UART_BUF_SIZE 300
    #define UART_WAIT_FOR_BUF_DELAY K_MSEC(50)
    #define UART_WAIT_FOR_RX 50000
    
    static K_FIFO_DEFINE(fifo_uart_tx_data);
    static K_FIFO_DEFINE(fifo_uart_rx_data);
    
    struct uart_data_t {
        void *fifo_reserved;
        uint16_t data[UART_BUF_SIZE];
        uint16_t len;
    };
    
    //Timer
    #define TIMER_NODE DT_INST(0, nordic_nrf_timer)
    static struct k_timer my_timer;
    
    //NVS
    static struct nvs_fs fs;
    struct flash_pages_info info;
    #define NVS_PARTITION			storage_partition
    #define NVS_PARTITION_DEVICE	FIXED_PARTITION_DEVICE(NVS_PARTITION)
    #define NVS_PARTITION_OFFSET	FIXED_PARTITION_OFFSET(NVS_PARTITION)
    
    //Leds and Buttons
    #define LED1_NODE DT_ALIAS(led1)
    #define LED2_NODE DT_ALIAS(led2)
    #define LED3_NODE DT_ALIAS(led3)
    #define SW1_NODE  DT_ALIAS(sw1)
    static struct gpio_dt_spec gp_led1 = GPIO_DT_SPEC_GET_OR(LED1_NODE, gpios, {0});
    static struct gpio_dt_spec gp_led2 = GPIO_DT_SPEC_GET_OR(LED2_NODE, gpios, {0});
    static struct gpio_dt_spec esp_ctrl = GPIO_DT_SPEC_GET_OR(LED3_NODE, gpios, {0});
    static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW1_NODE, gpios, {0});
    
    // const struct device *const cons = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
    
    static struct gpio_callback button_cb_data;
    
    void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
    {
    	printf("\n button pressed");
    	pm_device_action_run(uart, PM_DEVICE_ACTION_RESUME);
    	pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);
        // k_timer_start(&my_timer, K_MSEC(60000), K_MSEC(0));
    }
    
    void timer_expiry_handler(struct k_timer *timer_id)
    {
        // Timer expiry handler code
        printk("Timer expired!\n");
    	tmr_expired = 1;
    
    }
    
    void hexStringToByteArray(const char* hexString, uint8_t* byteArray) {
        size_t stringLength = strlen(hexString);
    
        for (size_t i = 0; i < stringLength; i += 2) {
            // Extract two characters at a time from the hex string
            char hexPair[3];
            hexPair[0] = hexString[i];
            hexPair[1] = hexString[i + 1];
            hexPair[2] = '\0';
    
            // Convert the pair of characters into a byte value
            *byteArray++ = (uint8_t)strtol(hexPair, NULL, 16);
        }
    }
    
    
    static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
    {
    	ARG_UNUSED(dev);
    
    	static size_t aborted_len;
    	struct uart_data_t *buf;
    	static uint8_t *aborted_buf;
    	static bool disable_req;
    
    	switch (evt->type) {
    	case UART_TX_DONE:
    		// LOG_DBG("UART_TX_DONE");
    		printk("\n UART_TX_DONE");
    		if ((evt->data.tx.len == 0) ||
    		    (!evt->data.tx.buf)) {
    			return;
    		}
    
    		if (aborted_buf) {
    			buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
    					   data);
    			aborted_buf = NULL;
    			aborted_len = 0;
    		} else {
    			buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t,
    					   data);
    		}
    
    		k_free(buf);
    
    		buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT);
    		if (!buf) {
    			return;
    		}
    		break;
    
        case UART_RX_RDY:
            // LOG_DBG("UART_RX_RDY");
            buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data);
            buf->len += evt->data.rx.len;
    
            if (disable_req) {
                return;
            }
    
            if ((buf->len > 0) && (evt->data.rx.buf[buf->len - 1] == '#'))
    		{
    			// printk("Size : %d\n", buf->len);
                // printk("\nRX Data:");	
                // printk("%d - %.*s \n", buf->len, buf->data); //uart_info
    			
    			if(strncmp(buf->data,"dev_Awake",9) == 0){
    				printk("\nRX dev_Awake...");
    				dev_Awake=1;
    			}
    			else if(strncmp(buf->data,"Ack",3) == 0){
    				printk("\nRX Ack...");
    				Ack = 1;
    			}
    			else{
    				// printk("\nRX CMD:%.*s\n", buf->len, buf->data);
    				// uart_flag=1;
    				dataa = buf->data;
    				// strcpy(dataa,buf->data);
    				printk("\ndataa:%.*s\n", strlen(dataa), dataa);
    			 	// json_flag=1;
    				//if(my_timer.status == 1)
    				// {
    				// 	printk("\nTimer stopped...");
    				// 	k_timer_stop(&my_timer);
    				// }
    				// json_validate(dataa);
    			}			
                buf->len=0;
                // disable_req = true;
                uart_rx_disable(uart);
            }
    		
            break;
    
    	case UART_RX_DISABLED:
    		// LOG_DBG("UART_RX_DISABLED");
    		disable_req = false;
    
    		buf = k_malloc(sizeof(*buf));
    		if (buf) {
    			buf->len = 0;
    		} else {
    			LOG_WRN("Not able to allocate UART receive buffer");
    			k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    			return;
    		}
    
    		uart_rx_enable(uart, buf->data, sizeof(buf->data),
    			       UART_WAIT_FOR_RX);
    
    		break;
    
    	case UART_RX_BUF_REQUEST:
    		// LOG_DBG("UART_RX_BUF_REQUEST");
    		buf = k_malloc(sizeof(*buf));
    		if (buf) {
    			buf->len = 0;
    			uart_rx_buf_rsp(uart, buf->data, sizeof(buf->data));
    		} else {
    			LOG_WRN("Not able to allocate UART receive buffer");
    		}
    
    		break;
    
    	case UART_RX_BUF_RELEASED:
    		// LOG_DBG("UART_RX_BUF_RELEASED");
    		buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t,
    				   data);
    
    		if (buf->len > 0) {
    			k_fifo_put(&fifo_uart_rx_data, buf);
    		} else {
    			k_free(buf);
    		}
    
    		break;
    
    	case UART_TX_ABORTED:
    		// LOG_DBG("UART_TX_ABORTED");
    		if (!aborted_buf) {
    			aborted_buf = (uint8_t *)evt->data.tx.buf;
    		}
    
    		aborted_len += evt->data.tx.len;
    		buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
    				   data);
    		break;
    
    	default:
    		break;
    	}
    }
    
    
    static int uart_init(void)
    {
    	int err;
    	int pos;
    	struct uart_data_t *rx;
    
    	tx_buf = k_malloc(sizeof(struct uart_data_t));
    
    	if (!device_is_ready(uart)) {
    		return -ENODEV;
    	}
    
    	rx = k_malloc(sizeof(*rx));
    	if (rx) {
    		rx->len = 0;
    	} else {
    		return -ENOMEM;
    	}
    
    	err = uart_callback_set(uart, uart_cb, NULL);
    	if (err) {
    		k_free(rx);
    		LOG_ERR("Cannot initialize UART callback");
    		return err;
    	}
    
    	err = uart_rx_enable(uart, rx->data, sizeof(rx->data), 50);
    	if (err) {
    		LOG_ERR("Cannot enable uart reception (err: %d)", err);
    		/* Free the rx buffer only because the tx buffer will be handled in the callback */
    		k_free(rx);
    	}
    
    	return err;
    }
    
    
    int main(void)
    {
    	uint8_t rc,st,err;
    	int rcr;
    	printf("Sleep Test! %s\n", CONFIG_BOARD);
    
    //NVS Initialization
    	fs.flash_device = NVS_PARTITION_DEVICE;
        if (!device_is_ready(fs.flash_device)) {
            printk("Flash device %s is not ready\n", fs.flash_device->name);
            return 0;
        }
    
        fs.offset = NVS_PARTITION_OFFSET;
    	rc = flash_get_page_info_by_offs(fs.flash_device, fs.offset, &info);
    	if (rc) {
    		printk("Unable to get page info\n");
    		return 0;
    	}
    	fs.sector_size = info.size;
    	fs.sector_count = 3U;
        rc = nvs_mount(&fs);
        if (rc) {
            printk("Flash Init failed\n");
            return 0;
        }
    
    //LED Initializations
    	st = gpio_pin_configure_dt(&gp_led1, GPIO_OUTPUT);
    	if (!gpio_is_ready_dt(&gp_led1)) {
    		return 0;
    	}
    	else{
    		// printk("\ngp_led1 - 1");
    		gpio_pin_set_dt(&gp_led1, 1);
    	}
    
    	st = gpio_pin_configure_dt(&gp_led2, GPIO_OUTPUT);
    	if (!gpio_is_ready_dt(&gp_led2)) {
    		return 0;
    	}
    	else{
    		// printk("\ngp_led2 - 1");
    		gpio_pin_set_dt(&gp_led2, 1);
    	}
    
    	st = gpio_pin_configure_dt(&esp_ctrl, GPIO_OUTPUT);
    	if (!gpio_is_ready_dt(&esp_ctrl)) {
    		return 0;
    	}
    	else{
    		printk("\nesp_ctrl - 1");
    		gpio_pin_set_dt(&esp_ctrl, 0);
    	}
    //Switch Initialization
    	st = gpio_pin_configure_dt(&button, GPIO_INPUT);
    	if(st != 0)
    	{
    		printk("\nError:%d Failed to configure %s pin %d\n");
    	}
    
    	st = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
    	if(st != 0)
    	{
    		printk("\nError %d:failed to configure interrupt on %s  pin %d\n",st,button.port->name,button.pin);
    		return 0;
    	}
    
    	gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
        gpio_add_callback(button.port, &button_cb_data);
        printk("\n Set up button at %s pin %d", button.port->name, button.pin);
    
    //UART Initialization
    	err = uart_init();
    	if (err) {
    		printk("uart_init failed (err %d)\n", err);
    		return 0;
    	}
    	printk("UART Initialized\n");
    
    //Bluetooth Enable
    	err = bt_enable(NULL);
    	if (err) {
    		printk("bt_enable failed (err %d)\n", err);
    		return 0;
    	}
    	printk("Bluetooth Initialized\n");
    
    //Timer Initialization
        const struct device *const timer_dev = DEVICE_DT_GET(TIMER_NODE);
        if (!timer_dev) {
            printk("Timer device not found\n");
            return;
        }	
    	k_timer_init(&my_timer, timer_expiry_handler, NULL);
    	// k_timer_stop(&my_timer);
    	k_timer_start(&my_timer, K_MSEC(60000), K_MSEC(0));
    
    //for sleep mode pm_device_action_run
    	// if (!device_is_ready(cons)) {
    	// 	printk("%s: device not ready.\n", cons->name);
    	// 	return 0;
    	// }
    
    //NVS read
    	rcr = nvs_read(&fs, BUFFER_ID, &buff, sizeof(buff));
    	printk("\nrcr:%d",rcr);
    	if (rcr > 0) {
    		// json_validate(buff);
    		printk("Buffer value: %s\n", buff);
    		if((buff[0] - '0') <= 10){
    			mac_avail=1;
    		}
    	}
    	else{
    		printk("\nNVS is empty\n");
    	}		
    
    	return 0;
    }
    
    void control_thread(void)
    {
    	uint8_t st;
    	while(true)
    	{
    		if(onboard == 1 && Ack == 1)
    		{
    			//if(my_timer.status == 1)
    			// k_timer_stop(&my_timer);
    			k_timer_start(&my_timer, K_MSEC(180000), K_MSEC(0));
    			onboard = 0;
    			Ack = 0;
    			printk("\n Timer update");
    		}
    		if(dev_Awake == 1)
    		{
    			// printk("\n abc");
    			if (tx_buf) {
    				printk("\n onboard");
    
    				strcpy(tx_buf->data,"gateway_onboarding");
    				printk("\ntx_buf data:%s\n",tx_buf->data);
    				// printk("\ntx_buf len:%d\n",tx_buf->len);
    				uart_tx(uart, tx_buf->data, 18, SYS_FOREVER_US);
    
    				dev_Awake=0;
    				onboard = 1;
    			}
    		}
    		// k_msleep(1000);
    		if(tmr_expired == 1){
    			printf("while");
    			tmr_expired = 0;
    			st = pm_device_action_run(uart, PM_DEVICE_ACTION_SUSPEND);
    			if(st != 0){
    				printf("sus1 failed");
    			}else{
    				printf("sus1 success");
    			}
    
    			st = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
    			if(st != 0){
    				printf("sus2 failed");
    			}else{
    				printf("sus2 success");
    			}
    		}
    		k_msleep(1000);
    	}
    }
    K_THREAD_DEFINE(control, 2048, control_thread, NULL, NULL, NULL, -1, 0, 1000);
    

Related