1 /* Console example — various system commands
2 
3    This example code is in the Public Domain (or CC0 licensed, at your option.)
4 
5    Unless required by applicable law or agreed to in writing, this
6    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
7    CONDITIONS OF ANY KIND, either express or implied.
8 */
9 
10 #include <stdio.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <unistd.h>
14 #include "esp_log.h"
15 #include "esp_console.h"
16 #include "esp_system.h"
17 #include "esp_sleep.h"
18 #include "esp_spi_flash.h"
19 #include "driver/rtc_io.h"
20 #include "driver/uart.h"
21 #include "argtable3/argtable3.h"
22 #include "freertos/FreeRTOS.h"
23 #include "freertos/task.h"
24 #include "cmd_system.h"
25 #include "sdkconfig.h"
26 
27 #ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
28 #define WITH_TASKS_INFO 1
29 #endif
30 
31 static const char *TAG = "cmd_system";
32 
33 static void register_free(void);
34 static void register_heap(void);
35 static void register_version(void);
36 static void register_restart(void);
37 static void register_deep_sleep(void);
38 static void register_light_sleep(void);
39 #if WITH_TASKS_INFO
40 static void register_tasks(void);
41 #endif
42 
register_system_common(void)43 void register_system_common(void)
44 {
45     register_free();
46     register_heap();
47     register_version();
48     register_restart();
49 #if WITH_TASKS_INFO
50     register_tasks();
51 #endif
52 }
53 
register_system_sleep(void)54 void register_system_sleep(void)
55 {
56     register_deep_sleep();
57     register_light_sleep();
58 }
59 
register_system(void)60 void register_system(void)
61 {
62     register_system_common();
63     register_system_sleep();
64 }
65 
66 /* 'version' command */
get_version(int argc,char ** argv)67 static int get_version(int argc, char **argv)
68 {
69     esp_chip_info_t info;
70     esp_chip_info(&info);
71     printf("IDF Version:%s\r\n", esp_get_idf_version());
72     printf("Chip info:\r\n");
73     printf("\tmodel:%s\r\n", info.model == CHIP_ESP32 ? "ESP32" : "Unknown");
74     printf("\tcores:%d\r\n", info.cores);
75     printf("\tfeature:%s%s%s%s%d%s\r\n",
76            info.features & CHIP_FEATURE_WIFI_BGN ? "/802.11bgn" : "",
77            info.features & CHIP_FEATURE_BLE ? "/BLE" : "",
78            info.features & CHIP_FEATURE_BT ? "/BT" : "",
79            info.features & CHIP_FEATURE_EMB_FLASH ? "/Embedded-Flash:" : "/External-Flash:",
80            spi_flash_get_chip_size() / (1024 * 1024), " MB");
81     printf("\trevision number:%d\r\n", info.revision);
82     return 0;
83 }
84 
register_version(void)85 static void register_version(void)
86 {
87     const esp_console_cmd_t cmd = {
88         .command = "version",
89         .help = "Get version of chip and SDK",
90         .hint = NULL,
91         .func = &get_version,
92     };
93     ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
94 }
95 
96 /** 'restart' command restarts the program */
97 
restart(int argc,char ** argv)98 static int restart(int argc, char **argv)
99 {
100     ESP_LOGI(TAG, "Restarting");
101     esp_restart();
102 }
103 
register_restart(void)104 static void register_restart(void)
105 {
106     const esp_console_cmd_t cmd = {
107         .command = "restart",
108         .help = "Software reset of the chip",
109         .hint = NULL,
110         .func = &restart,
111     };
112     ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
113 }
114 
115 /** 'free' command prints available heap memory */
116 
free_mem(int argc,char ** argv)117 static int free_mem(int argc, char **argv)
118 {
119     printf("%d\n", esp_get_free_heap_size());
120     return 0;
121 }
122 
register_free(void)123 static void register_free(void)
124 {
125     const esp_console_cmd_t cmd = {
126         .command = "free",
127         .help = "Get the current size of free heap memory",
128         .hint = NULL,
129         .func = &free_mem,
130     };
131     ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
132 }
133 
134 /* 'heap' command prints minumum heap size */
heap_size(int argc,char ** argv)135 static int heap_size(int argc, char **argv)
136 {
137     uint32_t heap_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
138     printf("min heap size: %u\n", heap_size);
139     return 0;
140 }
141 
register_heap(void)142 static void register_heap(void)
143 {
144     const esp_console_cmd_t heap_cmd = {
145         .command = "heap",
146         .help = "Get minimum size of free heap memory that was available during program execution",
147         .hint = NULL,
148         .func = &heap_size,
149     };
150     ESP_ERROR_CHECK( esp_console_cmd_register(&heap_cmd) );
151 
152 }
153 
154 /** 'tasks' command prints the list of tasks and related information */
155 #if WITH_TASKS_INFO
156 
tasks_info(int argc,char ** argv)157 static int tasks_info(int argc, char **argv)
158 {
159     const size_t bytes_per_task = 40; /* see vTaskList description */
160     char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task);
161     if (task_list_buffer == NULL) {
162         ESP_LOGE(TAG, "failed to allocate buffer for vTaskList output");
163         return 1;
164     }
165     fputs("Task Name\tStatus\tPrio\tHWM\tTask#", stdout);
166 #ifdef CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
167     fputs("\tAffinity", stdout);
168 #endif
169     fputs("\n", stdout);
170     vTaskList(task_list_buffer);
171     fputs(task_list_buffer, stdout);
172     free(task_list_buffer);
173     return 0;
174 }
175 
register_tasks(void)176 static void register_tasks(void)
177 {
178     const esp_console_cmd_t cmd = {
179         .command = "tasks",
180         .help = "Get information about running tasks",
181         .hint = NULL,
182         .func = &tasks_info,
183     };
184     ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
185 }
186 
187 #endif // WITH_TASKS_INFO
188 
189 /** 'deep_sleep' command puts the chip into deep sleep mode */
190 
191 static struct {
192     struct arg_int *wakeup_time;
193 #if SOC_PM_SUPPORT_EXT_WAKEUP
194     struct arg_int *wakeup_gpio_num;
195     struct arg_int *wakeup_gpio_level;
196 #endif
197     struct arg_end *end;
198 } deep_sleep_args;
199 
200 
deep_sleep(int argc,char ** argv)201 static int deep_sleep(int argc, char **argv)
202 {
203     int nerrors = arg_parse(argc, argv, (void **) &deep_sleep_args);
204     if (nerrors != 0) {
205         arg_print_errors(stderr, deep_sleep_args.end, argv[0]);
206         return 1;
207     }
208     if (deep_sleep_args.wakeup_time->count) {
209         uint64_t timeout = 1000ULL * deep_sleep_args.wakeup_time->ival[0];
210         ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout);
211         ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
212     }
213 
214 #if SOC_PM_SUPPORT_EXT_WAKEUP
215     if (deep_sleep_args.wakeup_gpio_num->count) {
216         int io_num = deep_sleep_args.wakeup_gpio_num->ival[0];
217         if (!esp_sleep_is_valid_wakeup_gpio(io_num)) {
218             ESP_LOGE(TAG, "GPIO %d is not an RTC IO", io_num);
219             return 1;
220         }
221         int level = 0;
222         if (deep_sleep_args.wakeup_gpio_level->count) {
223             level = deep_sleep_args.wakeup_gpio_level->ival[0];
224             if (level != 0 && level != 1) {
225                 ESP_LOGE(TAG, "Invalid wakeup level: %d", level);
226                 return 1;
227             }
228         }
229         ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level",
230                  io_num, level ? "HIGH" : "LOW");
231 
232         ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup(1ULL << io_num, level) );
233         ESP_LOGE(TAG, "GPIO wakeup from deep sleep currently unsupported on ESP32-C3");
234     }
235 #endif // SOC_PM_SUPPORT_EXT_WAKEUP
236 
237 #if CONFIG_IDF_TARGET_ESP32
238     rtc_gpio_isolate(GPIO_NUM_12);
239 #endif //CONFIG_IDF_TARGET_ESP32
240 
241     esp_deep_sleep_start();
242 }
243 
register_deep_sleep(void)244 static void register_deep_sleep(void)
245 {
246     int num_args = 1;
247     deep_sleep_args.wakeup_time =
248         arg_int0("t", "time", "<t>", "Wake up time, ms");
249 #if SOC_PM_SUPPORT_EXT_WAKEUP
250     deep_sleep_args.wakeup_gpio_num =
251         arg_int0(NULL, "io", "<n>",
252                  "If specified, wakeup using GPIO with given number");
253     deep_sleep_args.wakeup_gpio_level =
254         arg_int0(NULL, "io_level", "<0|1>", "GPIO level to trigger wakeup");
255     num_args += 2;
256 #endif
257     deep_sleep_args.end = arg_end(num_args);
258 
259     const esp_console_cmd_t cmd = {
260         .command = "deep_sleep",
261         .help = "Enter deep sleep mode. "
262 #if SOC_PM_SUPPORT_EXT_WAKEUP
263         "Two wakeup modes are supported: timer and GPIO. "
264 #else
265         "Timer wakeup mode is supported. "
266 #endif
267         "If no wakeup option is specified, will sleep indefinitely.",
268         .hint = NULL,
269         .func = &deep_sleep,
270         .argtable = &deep_sleep_args
271     };
272     ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
273 }
274 
275 /** 'light_sleep' command puts the chip into light sleep mode */
276 
277 static struct {
278     struct arg_int *wakeup_time;
279     struct arg_int *wakeup_gpio_num;
280     struct arg_int *wakeup_gpio_level;
281     struct arg_end *end;
282 } light_sleep_args;
283 
light_sleep(int argc,char ** argv)284 static int light_sleep(int argc, char **argv)
285 {
286     int nerrors = arg_parse(argc, argv, (void **) &light_sleep_args);
287     if (nerrors != 0) {
288         arg_print_errors(stderr, light_sleep_args.end, argv[0]);
289         return 1;
290     }
291     esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
292     if (light_sleep_args.wakeup_time->count) {
293         uint64_t timeout = 1000ULL * light_sleep_args.wakeup_time->ival[0];
294         ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout);
295         ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
296     }
297     int io_count = light_sleep_args.wakeup_gpio_num->count;
298     if (io_count != light_sleep_args.wakeup_gpio_level->count) {
299         ESP_LOGE(TAG, "Should have same number of 'io' and 'io_level' arguments");
300         return 1;
301     }
302     for (int i = 0; i < io_count; ++i) {
303         int io_num = light_sleep_args.wakeup_gpio_num->ival[i];
304         int level = light_sleep_args.wakeup_gpio_level->ival[i];
305         if (level != 0 && level != 1) {
306             ESP_LOGE(TAG, "Invalid wakeup level: %d", level);
307             return 1;
308         }
309         ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level",
310                  io_num, level ? "HIGH" : "LOW");
311 
312         ESP_ERROR_CHECK( gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL) );
313     }
314     if (io_count > 0) {
315         ESP_ERROR_CHECK( esp_sleep_enable_gpio_wakeup() );
316     }
317     if (CONFIG_ESP_CONSOLE_UART_NUM >= 0 && CONFIG_ESP_CONSOLE_UART_NUM <= UART_NUM_1) {
318         ESP_LOGI(TAG, "Enabling UART wakeup (press ENTER to exit light sleep)");
319         ESP_ERROR_CHECK( uart_set_wakeup_threshold(CONFIG_ESP_CONSOLE_UART_NUM, 3) );
320         ESP_ERROR_CHECK( esp_sleep_enable_uart_wakeup(CONFIG_ESP_CONSOLE_UART_NUM) );
321     }
322     fflush(stdout);
323     fsync(fileno(stdout));
324     esp_light_sleep_start();
325     esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
326     const char *cause_str;
327     switch (cause) {
328     case ESP_SLEEP_WAKEUP_GPIO:
329         cause_str = "GPIO";
330         break;
331     case ESP_SLEEP_WAKEUP_UART:
332         cause_str = "UART";
333         break;
334     case ESP_SLEEP_WAKEUP_TIMER:
335         cause_str = "timer";
336         break;
337     default:
338         cause_str = "unknown";
339         printf("%d\n", cause);
340     }
341     ESP_LOGI(TAG, "Woke up from: %s", cause_str);
342     return 0;
343 }
344 
register_light_sleep(void)345 static void register_light_sleep(void)
346 {
347     light_sleep_args.wakeup_time =
348         arg_int0("t", "time", "<t>", "Wake up time, ms");
349     light_sleep_args.wakeup_gpio_num =
350         arg_intn(NULL, "io", "<n>", 0, 8,
351                  "If specified, wakeup using GPIO with given number");
352     light_sleep_args.wakeup_gpio_level =
353         arg_intn(NULL, "io_level", "<0|1>", 0, 8, "GPIO level to trigger wakeup");
354     light_sleep_args.end = arg_end(3);
355 
356     const esp_console_cmd_t cmd = {
357         .command = "light_sleep",
358         .help = "Enter light sleep mode. "
359         "Two wakeup modes are supported: timer and GPIO. "
360         "Multiple GPIO pins can be specified using pairs of "
361         "'io' and 'io_level' arguments. "
362         "Will also wake up on UART input.",
363         .hint = NULL,
364         .func = &light_sleep,
365         .argtable = &light_sleep_args
366     };
367     ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
368 }
369