1 /*
2  * Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr/kernel.h>
7 #include <zephyr/pm/pm.h>
8 #include <zephyr/pm/device.h>
9 #include <zephyr/pm/policy.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <esp_sleep.h>
12 #include <driver/gpio.h>
13 
14 /* Most development boards have "boot" button attached to GPIO0.
15  * You can also change this to another pin.
16  */
17 #define SW0_NODE	DT_ALIAS(sw0)
18 
19 #if !DT_NODE_HAS_STATUS_OKAY(SW0_NODE)
20 #error "unsupported board: sw0 devicetree alias is not defined"
21 #endif
22 
23 /* Add an extra delay when sleeping to make sure that light sleep
24  * is chosen.
25  */
26 #define LIGHT_SLP_EXTRA_DELAY	(50UL)
27 
28 static const struct gpio_dt_spec button =
29 	GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
30 
main(void)31 int main(void)
32 {
33 	if (!gpio_is_ready_dt(&button)) {
34 		printk("Error: button device %s is not ready\n", button.port->name);
35 		return 0;
36 	}
37 
38 	const int wakeup_level = (button.dt_flags & GPIO_ACTIVE_LOW) ? 0 : 1;
39 
40 	esp_gpio_wakeup_enable(button.pin,
41 			wakeup_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
42 
43 	while (true) {
44 		/* Wake up in 2 seconds, or when button is pressed */
45 		esp_sleep_enable_timer_wakeup(2000000);
46 		esp_sleep_enable_gpio_wakeup();
47 
48 		/* Wait until GPIO goes high */
49 		if (gpio_pin_get_dt(&button) == wakeup_level) {
50 			printk("Waiting for GPIO%d to go high...\n", button.pin);
51 			do {
52 				k_busy_wait(10000);
53 			} while (gpio_pin_get_dt(&button) == wakeup_level);
54 		}
55 
56 		printk("Entering light sleep\n");
57 		/* To make sure the complete line is printed before entering sleep mode,
58 		 * need to wait until UART TX FIFO is empty
59 		 */
60 		k_busy_wait(10000);
61 
62 		/* Get timestamp before entering sleep */
63 		int64_t t_before_ms = k_uptime_get();
64 
65 		/* Sleep triggers the idle thread, which makes the pm subsystem select some
66 		 * pre-defined power state. Light sleep is used here because there is enough
67 		 * time to consider it, energy-wise, worthy.
68 		 */
69 		k_sleep(K_USEC(DT_PROP(DT_NODELABEL(light_sleep), min_residency_us) +
70 					LIGHT_SLP_EXTRA_DELAY));
71 		/* Execution continues here after wakeup */
72 
73 		/* Get timestamp after waking up from sleep */
74 		int64_t t_after_ms = k_uptime_get();
75 
76 		/* Determine wake up reason */
77 		const char *wakeup_reason;
78 
79 		switch (esp_sleep_get_wakeup_cause()) {
80 		case ESP_SLEEP_WAKEUP_TIMER:
81 			wakeup_reason = "timer";
82 			break;
83 		case ESP_SLEEP_WAKEUP_GPIO:
84 			wakeup_reason = "pin";
85 			break;
86 		default:
87 			wakeup_reason = "other";
88 			break;
89 		}
90 
91 		printk("Returned from light sleep, reason: %s, t=%lld ms, slept for %lld ms\n",
92 				wakeup_reason, t_after_ms, (t_after_ms - t_before_ms));
93 	}
94 
95 	return 0;
96 }
97