This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

High sleep current - pca10059 - Thread - without softdevice

Hello,

I am trying to send some sensor information via thread / mqttsn every 5 min. Therefore. The dongle has to wake up (5 sec.), send the information and return to sleep again.

Since the sensor/dongle is powered by a 3.4 V battery, they should draw as less power as possible during sleep periods. Unfortunately, I cant get the dongle into a "full" sleep mode. For me they should draw just a few µA in sleep mode, but I measure 11 mA. During the sensing I measure 20 mA. So the sleep mode is kind of triggered, but some part stays awake to draw 11 mA. 

I started to test the program with different parts disabled. Like without thread or twi. So I was able to nail it down a bit. Without thread activated, I could reach around 15 µA. So I guess the problem might lie with thread. I even changed the end device from FED to SED, but it didn't help. I tested thread_sleep() and __WFE(), __SEV(), __WFE(), but there was no difference.

Further online investigation resulted in this ticket. My measurement result in a very similar way. But the solution of that ticket didn't work for me. Maybe because I don't use a softdevice.

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

//sensor
#include "boards.h"
#include "pca10056.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "nrf_drv_twi.h"
#include "nrf_delay.h"
#include "nrf.h"

//thread
#include "app_scheduler.h"
#include "app_timer.h"
#include "bsp_thread.h"
#include "nrf_log_ctrl.h"
#include "nrf_log.h"
#include "nrf_log_default_backends.h"
#include "nrf_drv_gpiote.h"

#include "mqttsn_client.h"
#include "thread_utils.h"
#include "nrf_drv_clock.h"

#include <openthread/ip6.h>
#include <openthread/thread.h>

////
// Thread
////
#define DEFAULT_CHILD_TIMEOUT    40                                         /**< Thread child timeout [s]. */
#define DEFAULT_POLL_PERIOD      1000                                       /**< Thread Sleepy End Device polling period when MQTT-SN Asleep. [ms] */
#define SHORT_POLL_PERIOD        100                                        /**< Thread Sleepy End Device polling period when MQTT-SN Awake. [ms] */
#define SEARCH_GATEWAY_TIMEOUT 5  

#define SCHED_QUEUE_SIZE      32                              /**< Maximum number of events in the scheduler queue. */
#define SCHED_EVENT_DATA_SIZE APP_TIMER_SCHED_EVENT_DATA_SIZE /**< Maximum app_scheduler event size. */

APP_TIMER_DEF(m_repeated_timer_id);     /**< Handler for repeated timer used to blink LED 1. */
#define WAKEUP_TIME     10000    //wakeup every 10 sec

bool poweSaveMode = false;

static mqttsn_client_t      m_client;                                       /**< An MQTT-SN client instance. */
static mqttsn_remote_t      m_gateway_addr;                                 /**< A gateway address. */
static uint8_t              m_gateway_id;                                   /**< A gateway ID. */
static mqttsn_connect_opt_t m_connect_opt;                                  /**< Connect options for the MQTT-SN client. */
static uint8_t              m_led_state        = 0;                         /**< Previously sent BSP_LED_2 command. */
static uint16_t             m_msg_id           = 0;                         /**< Message ID thrown with MQTTSN_EVENT_TIMEOUT. */
static char                 m_client_id[]      = "nRF52840_publisher";      /**< The MQTT-SN Client's ID. */
static char                 m_topic_name[]     = "check"; /**< Name of the topic corresponding to subscriber's BSP_LED_2. */
static mqttsn_topic_t       m_topic            =                            /**< Topic corresponding to subscriber's BSP_LED_2. */
{
    .p_topic_name = (unsigned char *)m_topic_name,
    .topic_id     = 0,
};

int buttonNumber = 0;

/***************************************************************************************************
 * @section MQTT-SN
 **************************************************************************************************/

/**@brief Puts MQTT-SN client in sleep mode.
 *
 * @details This function changes Thread Sleepy End Device polling period to default.
 */
static void sleep(void)
{
    otError error;

    error = otLinkSetPollPeriod(thread_ot_instance_get(), DEFAULT_POLL_PERIOD);
    ASSERT(error == OT_ERROR_NONE);
}

/**@brief Puts MQTT-SN client in active mode.
 *
 * @details This function changes Thread Sleepy End Device polling period to short.
 */
static void wake_up(void)
{
    otError error;

    error = otLinkSetPollPeriod(thread_ot_instance_get(), SHORT_POLL_PERIOD);
    ASSERT(error == OT_ERROR_NONE);
}

/**@brief Initializes MQTT-SN client's connection options.
 */
static void connect_opt_init(void)
{
    m_connect_opt.alive_duration = MQTTSN_DEFAULT_ALIVE_DURATION,
    m_connect_opt.clean_session  = MQTTSN_DEFAULT_CLEAN_SESSION_FLAG,
    m_connect_opt.will_flag      = MQTTSN_DEFAULT_WILL_FLAG,
    m_connect_opt.client_id_len  = strlen(m_client_id),

    memcpy(m_connect_opt.p_client_id, (unsigned char *)m_client_id, m_connect_opt.client_id_len);
}


/**@brief Processes GWINFO message from a gateway.
 *
 * @details This function updates MQTT-SN Gateway information.
 *
 * @param[in]    p_event  Pointer to MQTT-SN event.
 */
static void gateway_info_callback(mqttsn_event_t * p_event)
{
    m_gateway_addr = *(p_event->event_data.connected.p_gateway_addr);
    m_gateway_id   = p_event->event_data.connected.gateway_id;
}


/**@brief Processes CONNACK message from a gateway.
 *
 * @details This function launches the topic registration procedure if necessary.
 */
static void connected_callback(void)
{
    uint32_t err_code = mqttsn_client_topic_register(&m_client,
                                                     m_topic.p_topic_name,
                                                     strlen(m_topic_name),
                                                     &m_msg_id);
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("REGISTER message could not be sent. Error code: 0x%x\r\n", err_code);
    }
}


/**@brief Processes DISCONNECT message from a gateway. */
static void disconnected_callback(void)
{
    sleep();
}


/**@brief Processes REGACK message from a gateway.
 *
 * @param[in] p_event Pointer to MQTT-SN event.
 */
static void regack_callback(mqttsn_event_t * p_event)
{
    m_topic.topic_id = p_event->event_data.registered.packet.topic.topic_id;
    NRF_LOG_INFO("MQTT-SN event: Topic has been registered with ID: %d.\r\n",
                 p_event->event_data.registered.packet.topic.topic_id);
    
    sleep();
}

/**@brief Processes PUBACK message from a gateway.
 *
 * @details This function puts the client in sleep mode.
 */
static void puback_callback(void)
{
    sleep();
}

/**@brief Processes DISCONNECT message being a response to sleep request.
 *
 * @details This function puts the client in sleep mode.
 */
static void sleep_callback(void)
{
    sleep();
}

/**@brief Processes callback from keep-alive timer timeout.
 *
 * @details This function puts the client in active mode.
 */
static void wakeup_callback(void)
{
    wake_up();
}


/**@brief Processes retransmission limit reached event. */
static void timeout_callback(mqttsn_event_t * p_event)
{
    NRF_LOG_INFO("MQTT-SN event: Timed-out message: %d. Message ID: %d.\r\n",
                  p_event->event_data.error.msg_type,
                  p_event->event_data.error.msg_id);
}


/**@brief Processes results of gateway discovery procedure. */
static void searchgw_timeout_callback(mqttsn_event_t * p_event)
{
    NRF_LOG_INFO("MQTT-SN event: Gateway discovery result: 0x%x.\r\n", p_event->event_data.discovery);
    sleep();
}


/**@brief Function for handling MQTT-SN events. */
void mqttsn_evt_handler(mqttsn_client_t * p_client, mqttsn_event_t * p_event)
{
    switch(p_event->event_id)
    {
        case MQTTSN_EVENT_GATEWAY_FOUND:
            NRF_LOG_INFO("MQTT-SN event: Client has found an active gateway.\r\n");
            nrf_drv_gpiote_out_toggle(BSP_LED_2);
            gateway_info_callback(p_event);
            break;

        case MQTTSN_EVENT_CONNECTED:
            NRF_LOG_INFO("MQTT-SN event: Client connected.\r\n");
            nrf_drv_gpiote_out_toggle(BSP_LED_2);
            nrf_drv_gpiote_out_toggle(BSP_LED_3);
            connected_callback();
            break;

        case MQTTSN_EVENT_DISCONNECTED:
            NRF_LOG_INFO("MQTT-SN event: Client disconnected.\r\n");
            nrf_drv_gpiote_out_toggle(BSP_LED_1);
            disconnected_callback();
            break;

        case MQTTSN_EVENT_REGISTERED:
            NRF_LOG_INFO("MQTT-SN event: Client registered topic.\r\n");
            regack_callback(p_event);
            break;

        case MQTTSN_EVENT_PUBLISHED:
            NRF_LOG_INFO("MQTT-SN event: Client has successfully published content.\r\n");
            break;
        
        case MQTTSN_EVENT_SLEEP_PERMIT:
            NRF_LOG_INFO("MQTT-SN event: Client permitted to sleep.\r\n");
            sleep_callback();
            break;

        case MQTTSN_EVENT_SLEEP_STOP:
            NRF_LOG_INFO("MQTT-SN event: Client wakes up.\r\n");
            wakeup_callback();
            break;

        case MQTTSN_EVENT_TIMEOUT:
            NRF_LOG_INFO("MQTT-SN event: Retransmission retries limit has been reached.\r\n");
            nrf_drv_gpiote_out_toggle(BSP_LED_1);
            timeout_callback(p_event);
            break;

        case MQTTSN_EVENT_SEARCHGW_TIMEOUT:
            NRF_LOG_INFO("MQTT-SN event: Gateway discovery procedure has finished.\r\n");
            searchgw_timeout_callback(p_event);
            break;

        default:
            break;
    }
}

/***************************************************************************************************
 * @section State
 **************************************************************************************************/

static void thread_state_changed_callback(uint32_t flags, void * p_context)
{
    NRF_LOG_INFO("State changed! Flags: 0x%08x Current role: %d\r\n",
                 flags, otThreadGetDeviceRole(p_context));
}

/***************************************************************************************************
 * @section Buttons
 **************************************************************************************************/
static void publish(void)
{
    uint32_t err_code = mqttsn_client_publish(&m_client, m_topic.topic_id, "open!", 12, &m_msg_id);
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("PUBLISH message could not be sent. Error code: 0x%x\r\n", err_code)
    }
}

static void bsp_event_handler(bsp_event_t event)
{ 
    if (otThreadGetDeviceRole(thread_ot_instance_get()) < OT_DEVICE_ROLE_CHILD )
    {
        (void)event;
        return;
    }
    
    switch (event)
    {
        case BSP_EVENT_KEY_0:
        {   
            // search mqttsn gateway
            if (buttonNumber == 0){

                wake_up();
                
                buttonNumber++;     
                nrf_drv_gpiote_out_toggle(BSP_LED_2);           
                uint32_t err_code = mqttsn_client_search_gateway(&m_client, SEARCH_GATEWAY_TIMEOUT);
                if (err_code != NRF_SUCCESS)
                {
                    NRF_LOG_ERROR("SEARCH GATEWAY message could not be sent. Error: 0x%x\r\n", err_code);
                }
                break;
            }

            //connect to mqttsn gateway
            if (buttonNumber == 1){

                wake_up();

                uint32_t err_code;
                buttonNumber++;        
                nrf_drv_gpiote_out_toggle(BSP_LED_2);     
                nrf_drv_gpiote_out_toggle(BSP_LED_3);         

                if (mqttsn_client_state_get(&m_client) == MQTTSN_CLIENT_CONNECTED)
                {
                    err_code = mqttsn_client_disconnect(&m_client);
                    if (err_code != NRF_SUCCESS)
                    {
                        NRF_LOG_ERROR("DISCONNECT message could not be sent. Error: 0x%x\r\n", err_code);
                    }
                }
                else
                {
                    err_code = mqttsn_client_connect(&m_client, &m_gateway_addr, m_gateway_id, &m_connect_opt);
                    if (err_code != NRF_SUCCESS)
                    {
                        NRF_LOG_ERROR("CONNECT message could not be sent. Error: 0x%x\r\n", err_code);
                    }
                }
                break;
            }

            
            //- start timer
            if (buttonNumber == 2){

                wake_up();
                
                buttonNumber++;
                
                nrf_drv_gpiote_out_toggle(BSP_LED_3);  
                
                app_timer_start(m_repeated_timer_id, APP_TIMER_TICKS(WAKEUP_TIME), NULL);
                
                poweSaveMode = true;
                
                break;
            }

            
        }

        default:
            break;
    }
}

/***************************************************************************************************
 * @section Initialization
 **************************************************************************************************/

/**@brief Function for initializing the Application Timer Module.
 */
static void timer_init(void)
{
    uint32_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);
}

/**@brief Function for initializing the nrf log module.
 */
static void log_init(void)
{
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}


/**@brief Function for initializing the Thread Stack.
 */
static void thread_instance_init(void)
{
    thread_configuration_t thread_configuration =
    {
        .radio_mode             = THREAD_RADIO_MODE_RX_ON_WHEN_IDLE,
        .autocommissioning      = false,
        .poll_period            = DEFAULT_POLL_PERIOD,
        .default_child_timeout  = DEFAULT_CHILD_TIMEOUT,
        //.autostart_disable = true,
    };

    thread_init(&thread_configuration);
    thread_cli_init();
    thread_state_changed_callback_set(thread_state_changed_callback);    

    //button init
    uint32_t err_code = bsp_init(BSP_INIT_BUTTONS, bsp_event_handler);
    APP_ERROR_CHECK(err_code);

    err_code = bsp_thread_init(thread_ot_instance_get());
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for initializing the MQTTSN client.
 */
static void mqttsn_init(void)
{
    uint32_t err_code = mqttsn_client_init(&m_client,
                                           MQTTSN_DEFAULT_CLIENT_PORT,
                                           mqttsn_evt_handler,
                                           thread_ot_instance_get());
    APP_ERROR_CHECK(err_code);

    connect_opt_init();
}


/**@brief Function for initializing scheduler module.
 */
static void scheduler_init(void)
{
    APP_SCHED_INIT(SCHED_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
}

static void gpio_init(void)
{
    nrf_drv_gpiote_init();    
    nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(true);
    nrf_drv_gpiote_out_init(BSP_LED_0, &out_config); //G alone
    nrf_drv_gpiote_out_init(BSP_LED_1, &out_config); //R
    nrf_drv_gpiote_out_init(BSP_LED_2, &out_config); //G
    nrf_drv_gpiote_out_init(BSP_LED_3, &out_config); //B 
    nrf_drv_gpiote_out_init(VL53L1X_POWER, &out_config); 
}

/**@brief Function starting the internal LFCLK oscillator.
 *
 * @details This is needed by RTC1 which is used by the Application Timer
 *          (When SoftDevice is enabled the LFCLK is always running and this is not needed).
 */
static void lfclk_request(void)
{
    nrf_drv_clock_init();
    nrf_drv_clock_lfclk_request(NULL);
}

/**@brief Timeout handler for the repeated timer.
 */
static void repeated_timer_handler(void * p_context)
{   
    nrf_drv_gpiote_out_toggle(BSP_LED_0);  
    nrf_delay_ms(5000);
    nrf_drv_gpiote_out_toggle(BSP_LED_0);   
}

/**@brief Create timers.
 */
static void create_timers()
{
    // Create timers
    app_timer_create(&m_repeated_timer_id,
                        APP_TIMER_MODE_REPEATED,
                        repeated_timer_handler);
    
}

/***************************************************************************************************
 * @section Main
 **************************************************************************************************/

int main(int argc, char *argv[])
{ 
    lfclk_request();
    log_init();
    scheduler_init();
    timer_init();

    gpio_init();
    create_timers();    

    thread_instance_init();
    mqttsn_init();

    while (true)
    {   
        thread_process();
        app_sched_execute();

         if(poweSaveMode){
        
            thread_sleep();
        }

        //if(poweSaveMode){
        //    //Wait for an event.
        //    __WFE();
        //    // Clear the internal event register.
        //    __SEV();
        //    __WFE();
        //}



    }
}

/**
 *@}
 **/

For better understanding, I skipped the sensor/twi inside the code. Now I just connect mqttsn via button pressing and every 10 sec. the app_timer interrupt is called and a LED lights up for 5 sec. (sensor has almost no impact on the drawn power) The power results are the same. Does anyone have an idea, which part of the chip is still active?

Ps: I use Linux, SDK17, VS-Code, pca10059

Related