| 
					
				 | 
			
			
				@@ -7,7 +7,11 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include <stdio.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "freertos/FreeRTOS.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "freertos/timers.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include "esp_log.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "esp_sleep.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "driver/rtc_io.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include "driver/gpio.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include "driver/ledc.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include "driver/i2c.h" 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -20,6 +24,8 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include "globdefs.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include "accessors.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include "messaging.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "buttons.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "services.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 extern void battery_svc_init(void); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 extern void monitor_svc_init(void); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -30,11 +36,19 @@ int i2c_system_speed = 400000; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 int spi_system_host = SPI_SYSTEM_HOST; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 int spi_system_dc_gpio = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 int rmt_system_base_channel = RMT_CHANNEL_0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 pwm_system_t pwm_system = {  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		.timer = LEDC_TIMER_0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		.base_channel = LEDC_CHANNEL_0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		.max = (1 << LEDC_TIMER_13_BIT), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	};		 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+};		 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static EXT_RAM_ATTR struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint64_t wake_gpio, wake_level; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t delay; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} sleep_config; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static EXT_RAM_ATTR void (*sleep_hooks[16])(void);     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static const char *TAG = "services"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -60,6 +74,9 @@ void set_chip_power_gpio(int gpio, char *value) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if (parsed) ESP_LOGI(TAG, "set GPIO %u to %s", gpio, value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }	 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/**************************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 void set_exp_power_gpio(int gpio, char *value) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	bool parsed = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -75,8 +92,113 @@ void set_exp_power_gpio(int gpio, char *value) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} else parsed = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if (parsed) ESP_LOGI(TAG, "set expanded GPIO %u to %s", gpio, value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- }	 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/**************************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void sleep_gpio_handler(void *id, button_event_e event, button_press_e mode, bool long_press) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (event == BUTTON_PRESSED) services_sleep_activate(SLEEP_ONGPIO); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}   
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/**************************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void sleep_init(void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    char *config = config_alloc_get(NVS_TYPE_STR, "sleep_config"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    char *p; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // do we want delay sleep 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    PARSE_PARAM(config, "delay", '=', sleep_config.delay); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    sleep_config.delay *= 60*1000; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (sleep_config.delay) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ESP_LOGI(TAG, "Sleep inactivity of %d minute(s)", sleep_config.delay / (60*1000)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // get the wake criteria 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if ((p = strcasestr(config, "wake"))) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        char list[32] = "", item[8]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		sscanf(p, "%*[^=]=%31[^,]", list); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        p = list - 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        while (p++ && sscanf(p, "%7[^|]", item)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            int level = 0, gpio = atoi(item); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!rtc_gpio_is_valid_gpio(gpio)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ESP_LOGE(TAG, "invalid wake GPIO %d (not in RTC domain)", gpio); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                sleep_config.wake_gpio |= 1LL << gpio; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (sscanf(item, "%*[^:]:%d", &level)) sleep_config.wake_level |= level << gpio; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            p = strchr(p, '|'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // when moving to esp-idf more recent than 4.4.x, multiple gpio wake-up with level specific can be done 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (sleep_config.wake_gpio) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ESP_LOGI(TAG, "Sleep wake-up gpio bitmap 0x%llx (active 0x%llx)", sleep_config.wake_gpio, sleep_config.wake_level);     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // then get the gpio that activate sleep (we could check that we have a valid wake) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if ((p = strcasestr(config, "sleep"))) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        int gpio, level = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		char sleep[8] = ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		sscanf(p, "%*[^=]=%7[^,]", sleep); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		gpio = atoi(sleep); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if ((p = strchr(sleep, ':')) != NULL) level = atoi(p + 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ESP_LOGI(TAG, "Sleep activation gpio %d (active %d)", gpio, level);         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        button_create(NULL, gpio, level ? BUTTON_HIGH : BUTTON_LOW, true, 0, sleep_gpio_handler, 0, -1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/**************************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void services_sleep_callback(uint32_t elapsed) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (sleep_config.delay && elapsed >= sleep_config.delay) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        services_sleep_activate(SLEEP_ONTIMER); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/**************************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void services_sleep_activate(sleep_cause_e cause) {    
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // call all sleep hooks that might want to do something 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for (void (**hook)(void) = sleep_hooks; *hook; hook++) (*hook)(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // isolate all possible GPIOs, except the wake-up ones 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    esp_sleep_config_gpio_isolate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for (int i = 0; i < GPIO_NUM_MAX; i++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!rtc_gpio_is_valid_gpio(i) || ((1LL << i) & sleep_config.wake_gpio)) continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        rtc_gpio_isolate(i); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // is there just one GPIO 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (sleep_config.wake_gpio & (sleep_config.wake_gpio - 1)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ESP_LOGI(TAG, "going to sleep cause %d, wake-up on multiple GPIO, any '1' wakes up 0x%llx", cause, sleep_config.wake_gpio); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        esp_sleep_enable_ext1_wakeup(sleep_config.wake_gpio, ESP_EXT1_WAKEUP_ANY_HIGH); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        int gpio = __builtin_ctz(sleep_config.wake_gpio); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        int level = (sleep_config.wake_level >> gpio) & 0x01; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ESP_LOGI(TAG, "going to sleep cause %d, wake-up on GPIO %d level %d", cause, gpio, level); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        esp_sleep_enable_ext0_wakeup(gpio, level); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // we need to use a timer in case the same button is used for sleep and wake-up and it's "pressed" vs "released" selected 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (cause == SLEEP_ONKEY) xTimerStart(xTimerCreate("sleepTimer", pdMS_TO_TICKS(1000), pdFALSE, NULL, (void (*)(void*)) esp_deep_sleep_start), 0);		 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else esp_deep_sleep_start(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/**************************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void services_sleep_sethook(void (*hook)(void)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for (int i = 0; i < sizeof(sleep_hooks)/sizeof(void(*)(void)); i++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!sleep_hooks[i]) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            sleep_hooks[i] = hook; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /**************************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  *  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -147,5 +269,6 @@ void services_init(void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	led_svc_init(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	battery_svc_init(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	monitor_svc_init(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	monitor_svc_init();  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    sleep_init(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 |