Optimize power consumption for maximum battery life in nrf52840 BLE Beacon.

I’m working on a BLE-based door sensor project and my focus is to optimize power consumption for maximum battery life. The code I’m using is designed for a beacon using sdk version v2.4.1 and I’m looking for guidance on reducing power usage. I’ve included my prj.conf and overlay files for reference. Any suggestions or insights on achieving the most efficient power management for this sensor would be greatly appreciated. I'm currently testing the current consumption of an nRF52840 using the Power Profiler Kit 2 as part of a BLE door sensor project. The current measured using ppk II is also included. It is approximately near 20mA. 

/* main.c - Application main entry point */

/*
 * Copyright (c) 2015-2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <zephyr/kernel.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>

#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/device.h>

#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
#define SLEEP_TIME_MS   1000
//#define SW0_NODE DT_ALIAS(sw0) 

#define LED0_NODE DT_ALIAS(led4)
#define SW0_NODE DT_ALIAS(sw6)

uint8_t door_status = 0;
uint8_t d[3] = {0x11, 0x66, 0x00};


static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(SW0_NODE, gpios);


/*
 * Set Advertisement data. Based on the Eddystone specification:
 * https://github.com/google/eddystone/blob/master/protocol-specification.md
 * https://github.com/google/eddystone/tree/master/eddystone-url
 */

static struct bt_data ad1[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
	BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x58, 0x36),
	BT_DATA_BYTES(BT_DATA_SVC_DATA16,0x11,0x66,0x00) 
	
};


static const struct bt_data sd[] = {
	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};


static void bt_ready(int err)
{
	char addr_s[BT_ADDR_LE_STR_LEN];
	bt_addr_le_t addr = {0};
	size_t count = 1;

	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}

	printk("Bluetooth initialized\n");

	/* Start advertising */
	err = bt_le_adv_start(BT_LE_ADV_NCONN_IDENTITY, ad1, ARRAY_SIZE(ad1),
			      sd, ARRAY_SIZE(sd));
	if (err) {
		printk("Advertising failed to start (err %d)\n", err);
		return;
	}

	/* For connectable advertising you would use
	 * bt_le_oob_get_local().  For non-connectable non-identity
	 * advertising an non-resolvable private address is used;
	 * there is no API to retrieve that.
	 */

	bt_id_get(&addr, &count);
	bt_addr_le_to_str(&addr, addr_s, sizeof(addr_s));

	printk("Beacon started, advertising as %s\n", addr_s);
}

int main(void)
{
	int err,ret;
	if (!device_is_ready(led.port)) {
		return ;
	}
	if (!device_is_ready(button.port)) {
	 	 return ;
    }

	ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
    if (ret < 0) {
		return ;
    }

	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
	if (ret < 0) {
		return ;
	}

	printk("Starting Beacon Demo\n");

	 /* Initialize the Bluetooth Subsystem */
	err = bt_enable(bt_ready);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
	}
	while(1)
	{
	    bool val = gpio_pin_get_dt(&button);
		gpio_pin_set_dt(&led,val);
		printk("Button Value: %d\n", val);
		door_status = (val == 1) ? 1 : 0;
		printk("Door Status: %s\n", door_status ? "Open" : "Closed");
		d[2]=door_status;
		ad1[2].data = (const uint8_t *)(d);
		err = bt_le_adv_update_data(ad1, ARRAY_SIZE(ad1),sd, ARRAY_SIZE(sd));
		if (err) 
		{
		printk("Advertising update with (err %d)\n", err);
		
	    }
	}
	
	return 0;
}

/*
 * Copyright 2022 Dronetag
 *
 * SPDX-License-Identifier: Apache-2.0
 */
 /{
    coex_gpio: coex {
        compatible = "gpio-radio-coex";
        grant-gpios = <&gpio1 0 (GPIO_PULL_DOWN | GPIO_ACTIVE_LOW)>;
        grant-delay-us = <150>;
    };

};
&radio {
    coex = <&coex_gpio>;
};

/{
    leds{
        compatible = "gpio-leds";
        led4: led_4 {
            gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
            label = "Yellow LED 0";
            status="okay";
        };
    };
    
    buttons {
        compatible = "gpio-keys";
        button6: button_6 {
            //gpios = <&gpio0 15 (GPIO_PULL_DOWN | GPIO_ACTIVE_LOW)>;
            gpios = <&gpio0 15( GPIO_ACTIVE_LOW)>;
            label = "Push button switch 6";
        };
    };
    
        aliases {
            led4=&led4;
            sw6=&button6;
        };
    };

    
    &pwm_led0 {
	
        status = "disabled";	
    };
    &button1 {
        
        status = "disabled";	
    };
    &button2 {
        
        status = "disabled";	
    };
    &button3 {	
        status = "disabled";	
    };
    
    &led0 {
        status = "disabled";
    };
    &led1 {
        status = "disabled";
    };
    &led2 {
        status = "disabled";
    };
    &led3 {
        status = "disabled";
    };
    /* Disable unused peripherals to reduce power consumption */
    &uart0 {
        status = "disabled";
    };
    &adc {
        status = "disabled";
    };
    &uart1 {
        status = "disabled";
    };
    &i2c0 {
        status = "disabled";
    };
    &pwm0 {
        status = "disabled";
    };
    &spi1 {
        status = "disabled";
    };
    &spi0 {
        status = "disabled";
    };
    &usbd {
        status = "disabled";
    };
    
    
    &i2c1 {
        status = "disabled";
    };
    &spi2 {
        status = "disabled";
    };
    
    &qspi {
        status = "disabled";
    };
    
    &timer0 {
        status = "disabled";
    };
    
    &timer1 {
        status = "disabled";
    };
    
    &timer2 {
        status = "disabled";
    };
    
    &timer3 {
        status = "disabled";
    };
    
    &timer4 {
        status = "disabled";
    };
    &nvic {
        status = "disabled";
    };
    
    &gpiote {
        status = "disabled";
    };
    &temp {
        status = "disabled";
    };
    &rtc0 {
        status = "disabled";
    };
    
    &rtc1 {
        status = "disabled";
    };
    &ecb {
        status = "disabled";
    };
    
    &ccm {
        status = "disabled";
    };
    
    &egu0 {
        status = "disabled";
    };
    
    &egu1 {
        status = "disabled";
    };
    
    &egu2 {
        status = "disabled";
    };
    
    &egu3 {
        status = "disabled";
    };
    
    &egu4 {
        status = "disabled";
    };
    
    &egu5 {
        status = "disabled";
    };
    &pwm0 {
        status = "disabled";
    };
    
    &acl {
        status = "disabled";
    };
    
    &ppi {
        status = "disabled";
    };
    
    &mwu {
        status = "disabled";
    };
    
    &usbd {
        status = "disabled";
    };
    
    &spi3 {
        status = "disabled";
    };
    
    &wdt0 {
        status = "disabled";
    };
    
    &rng_hci {
        status = "disabled";
    };
    &ieee802154{
        status = "disabled";
    };
      

CONFIG_BT=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_DEVICE_NAME="Door Sensorrr"

###################################################
CONFIG_PM=y
CONFIG_LOG=n
CONFIG_SERIAL=n
CONFIG_PM_DEVICE=y
CONFIG_BT_CTLR_TX_PWR_ANTENNA=0

Parents
  • Hi,

    Serial and logging are disabled which is good.

    I would also check out the peripheral lbs example if you haven't yet. It's basically doing exactly what you want and more, so you could even strip stuff away.

    Also make sure that all un-needed peripherals are disabled in a board overlay file. You can check this quickly by either reading the zephyr.dts file in you build/sephyr folder to check for anything that has status="okay" that you arent using. Or you could use the dts visualizer tool and just click stuff to turn them off (which makes or edits the overlay file previously mentioned).

    If you dont have any peripherals that need GPIOTE make sure that is status="disabled". If you have to have gpiote for one of your peripherals and you have an interrupt to handle like your door signal you can use the sense method on it instead of gpiote which will save some power. You can use sense on many low frequency interrupts instead of the task-event method that gpiote uses. You can use the sense-edge-mask property to tell it which pins to use sense on. In the example below, 0x8800 = 0b1000 1000 0000 0000 so I'm telling it to use sense on pins 15 and 11.

    &gpio1 {
    	status = "okay";
    	sense-edge-mask = <0x8800>;
    };

    One main aspect of low power is letting the chip go into low power mode, which happens when there is no processing to do. In your case it seems like your main thread is constantly running with no delay. If you add a delay in there the chip can sleep during that time. Also instead of polling the button state you could try using an interrupt instead (again, take a look at the peripheral_lbs example). If you want to go ballistic on it, then you can find an example that shows how to put the chip in system on or even off mode and wake it up with the actual door signal.

Reply
  • Hi,

    Serial and logging are disabled which is good.

    I would also check out the peripheral lbs example if you haven't yet. It's basically doing exactly what you want and more, so you could even strip stuff away.

    Also make sure that all un-needed peripherals are disabled in a board overlay file. You can check this quickly by either reading the zephyr.dts file in you build/sephyr folder to check for anything that has status="okay" that you arent using. Or you could use the dts visualizer tool and just click stuff to turn them off (which makes or edits the overlay file previously mentioned).

    If you dont have any peripherals that need GPIOTE make sure that is status="disabled". If you have to have gpiote for one of your peripherals and you have an interrupt to handle like your door signal you can use the sense method on it instead of gpiote which will save some power. You can use sense on many low frequency interrupts instead of the task-event method that gpiote uses. You can use the sense-edge-mask property to tell it which pins to use sense on. In the example below, 0x8800 = 0b1000 1000 0000 0000 so I'm telling it to use sense on pins 15 and 11.

    &gpio1 {
    	status = "okay";
    	sense-edge-mask = <0x8800>;
    };

    One main aspect of low power is letting the chip go into low power mode, which happens when there is no processing to do. In your case it seems like your main thread is constantly running with no delay. If you add a delay in there the chip can sleep during that time. Also instead of polling the button state you could try using an interrupt instead (again, take a look at the peripheral_lbs example). If you want to go ballistic on it, then you can find an example that shows how to put the chip in system on or even off mode and wake it up with the actual door signal.

Children
No Data
Related