Page 1 of 1

Is it possible to log the time between deep sleep wake up stubs function calls?

Posted: Sat Oct 14, 2017 9:52 pm
by mustafacc
I'm writing a program that detects the type of clicks on a GPIO pin (single click/double click/ etc...) while the device is on deep sleep. I'm basing my code on Ivan's deep sleep click count example:
https://gist.github.com/igrr/54f7fbe051 ... d7fbecfeab

So far I got that example to work on my end, however I want to calculate the time between clicks while in deep-sleep. Is that possible?
Also can I maybe leverage a timer inside the wakeup stub to get this functionality?

Cheers,
Mustafa

Re: Is it possible to log the time between deep sleep wake up stubs function calls?

Posted: Sun Oct 15, 2017 5:11 am
by ESP_igrr
You can read the RTC counter values, by adding code similar to this one:
components/soc/esp32/rtc_time.c#L129-L136

This counter runs from RTC_SLOW_CLK, which is 160kHz if the internal oscillator is used or 32kHz if external 32k XTAL is used. You can get the calibration value (period of RTC_SLOW_CLK measured using main XTAL as a reference) from esp_clk_slowclk_cal_get (you may ignore the warning in this header file about it not being public; it will be made public soon).

Re: Is it possible to log the time between deep sleep wake up stubs function calls?

Posted: Sun Oct 15, 2017 6:02 am
by mustafacc
Thanks for getting back to me. I tried implementing the rtc_time_get function in the wake-stub, however, I'm getting nonsense values for t whenever I print "t" in wake stub call. Most times it's either t == 1073343027 and in the range of t=48770 (either increasing or decreasing randomly).
I'm thinking leveraging a timer in the wakestub would make this easier, is that possible?

Re: Is it possible to log the time between deep sleep wake up stubs function calls?

Posted: Sun Oct 15, 2017 11:20 am
by ESP_igrr
Leveraging which timer? Did I get that right that you want to measure time between wakeups? If so, then note that only the RTC controller remains powered on in deep sleep, and it has only one counter which applications can use — that's the one queried by rtc_time_get.

Perhaps you could post the code you have, which exhibits the issue?

Re: Is it possible to log the time between deep sleep wake up stubs function calls?

Posted: Sun Oct 15, 2017 4:56 pm
by mustafacc
The idea is to detect the type of clicks, so If I start a timer once a single click is detected, click count = 1, a timer starts (<.5 sec or so) and the system goes back to sleep. If a second click is detected then click count = 2 and thus the click type is a quick double-click. If the original timer times out then a second click was not detected and the click type is a single click. Thus it's not necessary to calculate the time difference between wake stub calls, just start a timer that times out after a certain duration.

Below is the modified version of your code implemented on MOS, the click count while in deep sleep does work. The code snippet marked "Calculate timer difference" doesn't seem to be.

Code: Select all

#include "mgos.h"
#include "mgos_gpio.h"
#include "mgos_adc.h"
#include "mgos_timers.h"
#include "mgos_mqtt.h"
#include "esp_sleep.h"
#include "mgos_hal.h"

#include "stdio.h"
#include "stdlib.h"
#include "stdio.h"
#include "mgos_sys_config.h"
#include "mgos_app.h"
#include "common/platform.h"
#include "driver/rtc_io.h"

#include "esp_attr.h"
#include "rom/rtc.h"
#include "rom/ets_sys.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/uart_reg.h"
#include "soc/timer_group_reg.h"
#include "soc/timer_group_struct.h"
#include "driver/periph_ctrl.h"
#include "driver/timer.h"

#include <time.h>
#include <sys/time.h>

// Assign GPIO and analog inputs based on pin values stored in config settings
#define LIGHT get_cfg()->iD_app.led_pin
#define RGB_LED get_cfg()->iD_app.rgb_led_pin
#define BUTTON get_cfg()->iD_app.button_pin
#define BUTTON_SENSOR get_cfg()->iD_app.button_sensor
#define WAKEUP_PIN get_cfg()->iD_app.wakeup_pin
#define BATTERY get_cfg()->iD_app.battery_sensor

// Assign time to wait in config mode before going to sleep
#define CONFIG_TIMEOUT 0.25                        // minutes 

mgos_timer_id sleep_timer = false;

// Deep sleep wake variables
RTC_DATA_ATTR int click_count;
RTC_DATA_ATTR int click_count_threshold = 20;

#define PULSE_CNT_RTC_GPIO_NUM 13
#define PULSE_CNT_GPIO_NUM 33

#define PULSE_CNT_IS_LOW() \
	((REG_GET_FIELD(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT) \
	& BIT(PULSE_CNT_RTC_GPIO_NUM)) == 0)

////////////////////////////////////////////////////////////////////////////////
// Deep sleep implementation
////////////////////////////////////////////////////////////////////////////////
// Identify wake-up reason and respond accordingly
static esp_sleep_wakeup_cause_t identify_wakeup_reason() {

	esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();

	switch(wakeup_reason) {

	case ESP_SLEEP_WAKEUP_EXT0:
		LOG(LL_INFO, ("Wake up cause: Ext0 RTC_IO"));
		break;  		

	case ESP_SLEEP_WAKEUP_EXT1:
		LOG(LL_INFO, ("Wake up cause: Ext1 RTC_IO")); 
		break;  		
		
	case ESP_SLEEP_WAKEUP_TIMER: 
		LOG(LL_INFO, ("Wake up cause: Ext RTC_CNTL")); 
		break;
		
	case ESP_SLEEP_WAKEUP_TOUCHPAD: 
		LOG(LL_INFO, ("Wake up cause: Touchpad")); 
		break;	
		
	case ESP_SLEEP_WAKEUP_ULP: 
		LOG(LL_INFO, ("Wake up cause: ULP")); 
		break;
		
	case ESP_SLEEP_WAKEUP_UNDEFINED: 
		LOG(LL_INFO, ("Wake up cause: Undefined")); 
		break;
		
	default:
		LOG(LL_INFO, ("Wake up cause: Default")); 
		break;			
	}
	LOG(LL_INFO, ("Click count: %d", click_count));   
	return wakeup_reason;
}


RTC_DATA_ATTR uint64_t RTC_time;


// Code that executes immediately upon wake-up before other code is run
static void RTC_IRAM_ATTR wake_stub() {

	static RTC_RODATA_ATTR const char fmt_str[] = "Click count too small: %d.  Going back to sleep.\n";
	click_count++;
	esp_default_wake_deep_sleep();

	// Calculate timer different EXPERIMENTAL****************************
    SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE);
    while (GET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID) == 0) {
        ets_delay_us(1); // might take 1 RTC slowclk period, don't flood RTC bus
    }
    SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG, RTC_CNTL_TIME_VALID_INT_CLR);
    uint64_t t = READ_PERI_REG(RTC_CNTL_TIME0_REG);
    t |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32;
	
	RTC_time = t;
	static RTC_RODATA_ATTR const char t_str[] = "Current time: %d\n";
	ets_printf(t_str, RTC_time);
	// Calculate timer different EXPERIMENTAL****************************
	
	
	// Experimental code for putting device back to sleep
	if (click_count >= click_count_threshold) {
		esp_default_wake_deep_sleep();
		static RTC_RODATA_ATTR const char fmt_str[] = "Click count: %d.  Waking up!\n";
		ets_printf(fmt_str, click_count);
		return;
	}

	//////////////////////////////////////////////////////////////////////////////
	// If click_count_threshold is set to 1, only the code above executes and the
	// device wakes normally.  But if the click_count_threshold is > 1, the device
	// goes back to sleep immediately after the first click (as it should), but 
	// can't wake again.  There must be a problem in the code below (I suspect in 
	// the first 'do' loop - see my comments below).
	//////////////////////////////////////////////////////////////////////////////

	ets_printf(fmt_str, click_count);    


	do {
		// CODE GETS STUCK HERE - UNCOMMENT THE LOG LINE TO SEE THE ISSUE
		while (PULSE_CNT_IS_LOW()) {

			REG_WRITE(TIMG_WDTFEED_REG(0), 1);
			
		}
		// debounce, 10ms
		ets_delay_us(10000);
	} while (PULSE_CNT_IS_LOW());

	// Wait for UART to end transmitting.
	while (REG_GET_FIELD(UART_STATUS_REG(0), UART_ST_UTX_OUT)) {
		;
	}

	
	// Set the pointer of the wake stub function.
	REG_WRITE(RTC_ENTRY_ADDR_REG, (uint32_t)&wake_stub);

	// Go to sleep.
	// This line will only print a G, since there is not enough time to transmit the entire string before the device sleeps.
	static RTC_RODATA_ATTR const char slp_str[] = "Going back to sleep %d\n";
	ets_printf(slp_str, click_count);    
	
	CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN);
	SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN);

	// A few CPU cycles may be necessary for the sleep to start...
	while (true) {
		;
	}

}

// Function for putting system to sleep
static void go_to_sleep() {

	LOG(LL_INFO, ("Going to sleep...zzz"));
	
	esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_AUTO);
	esp_sleep_enable_ext0_wakeup(WAKEUP_PIN, 0);
	esp_set_deep_sleep_wake_stub(&wake_stub);
	esp_deep_sleep_start();
}

////////////////////////////////////////////////////////////////////////////////
// Main app function 
////////////////////////////////////////////////////////////////////////////////
enum mgos_app_init_result mgos_app_init(void) {

	// Set GPIO pin modes and initialize 
	mgos_gpio_set_mode(LIGHT, MGOS_GPIO_MODE_OUTPUT);
	mgos_gpio_set_mode(RGB_LED, MGOS_GPIO_MODE_OUTPUT);
	mgos_gpio_set_mode(BUTTON, MGOS_GPIO_MODE_INPUT);
	mgos_gpio_set_mode(BUTTON_SENSOR, MGOS_GPIO_MODE_INPUT);

	// Create a timer for going to sleep when timer triggers
	sleep_timer = mgos_set_timer(CONFIG_TIMEOUT*30*1000, 0, go_to_sleep, NULL);

	// Initialize light to 'on' state
	LOG(LL_INFO, ("AWAKE!!! Turning light on"));
	mgos_gpio_write(LIGHT, 1);
	identify_wakeup_reason();

	return MGOS_APP_INIT_SUCCESS;
}

Re: Is it possible to log the time between deep sleep wake up stubs function calls?

Posted: Mon Oct 16, 2017 1:49 am
by mustafacc
Also a question regarding the code snippet where you put the device back to deep-sleep inside the wake-stub:

Code: Select all

   // Set the pointer of the wake stub function.
   REG_WRITE(RTC_ENTRY_ADDR_REG, (uint32_t)&wake_stub);
   // Go to sleep. 
   
   CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN);
   SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN);

   // A few CPU cycles may be necessary for the sleep to start...
   while (true) {
      ;
   }
Is it possible to modify this such that the device goes back to sleep for only a certain amount of time before waking up and booting normally?

Re: Is it possible to log the time between deep sleep wake up stubs function calls?

Posted: Sun Oct 22, 2017 6:25 am
by ESP_igrr
Okay, i understand now. What you need is to go back to deep sleep for certain amount of time (0.5s), while still being able to wake up from deep sleep using EXT0 wakeup.

To wake up after some time, you can do something like

Code: Select all

    // get current time
    SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE);
    while (GET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID) == 0) {
        ;
    }
    SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG, RTC_CNTL_TIME_VALID_INT_CLR);
    uint64_t t_now = READ_PERI_REG(RTC_CNTL_TIME0_REG);
    t_now |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32;
    // Get number of microseconds per RTC clock tick (scaled by 2^19)
    const uint32_t rtc_clk_cal = REG_READ(RTC_SLOW_CLK_CAL_REG);
    // Calculate number of RTC clock ticks until wakeup
    const uint64_t wakeup_delay_us = 500000; // 0.5 sec
    const uint64_t wakeup_delay_ticks = wakeup_delay_us * (1 << RTC_CLK_CAL_FRACT) / rtc_clk_cal;
    // Calculate RTC clock value for wakeup
    const uint64_t t_wake = t + wakeup_delay_ticksticks;
    // Write clock value to RTC:
    WRITE_PERI_REG(RTC_CNTL_SLP_TIMER0_REG, t_wake & UINT32_MAX);
    WRITE_PERI_REG(RTC_CNTL_SLP_TIMER1_REG, t_wake >> 32);

Re: Is it possible to log the time between deep sleep wake up stubs function calls?

Posted: Sun Feb 17, 2019 10:20 am
by Jeroen88
I would like to wake up de cpu's by the ULP after exactly 5 minutes, independent off the time the ulp has run (I understand the ulp wake up timer starts again after halt, and when halt is called is dependent on the time the ulp has run), and independent off the time drift of the RTC_SLOW_CLK. Is that possible? I also would like to sync the ESP with an NTP server (once, or once every day or week, depending on how often is necessary) and have the first ULP wake up at exactly a 5 minute time interval.