console: re-use the available REPL console API and improve linenoise

Console example doesn't duplicate code in `console` component.
Linenoise has been improved: it now has a parametrized command line
length. It is now possible to paste data efficiently to the console.
Note: this can only be done if the cursor is at the end of the line.

Closes https://github.com/espressif/esp-idf/issues/7057
This commit is contained in:
Omar Chebib
2021-07-15 11:37:11 +08:00
committed by Zim Kalinowski
parent da3038cd7a
commit 0fd74a43c8
53 changed files with 617 additions and 27 deletions

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(console)

View File

@@ -0,0 +1,8 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := console
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,151 @@
# Console Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example illustrates the usage of the [Console Component](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/console.html#console) to create an interactive shell on the ESP32. The interactive shell running on the ESP32 can then be controlled/interacted with over a serial port (UART).
The interactive shell implemented in this example contains a wide variety of commands, and can act as a basis for applications that require a command-line interface (CLI).
## How to use example
### Hardware Required
This example should be able to run on any commonly available ESP32 development board.
### Configure the project
```
idf.py menuconfig
```
* Enable/disable `Example Configuration > Store command history in flash` as necessary
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
Enter the `help` command get a full list of all available commands. The following is a sample session of the Console Example where a variety of commands provided by the Console Example are used. Note that GPIO15 is connected to GND to remove the boot log output.
```
This is an example of ESP-IDF console component.
Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
[esp32]> help
help
Print the list of registered commands
free
Get the total size of heap memory available
restart
Restart the program
deep_sleep [-t <t>] [--io=<n>] [--io_level=<0|1>]
Enter deep sleep mode. Two wakeup modes are supported: timer and GPIO. If no
wakeup option is specified, will sleep indefinitely.
-t, --time=<t> Wake up time, ms
--io=<n> If specified, wakeup using GPIO with given number
--io_level=<0|1> GPIO level to trigger wakeup
join [--timeout=<t>] <ssid> [<pass>]
Join WiFi AP as a station
--timeout=<t> Connection timeout, ms
<ssid> SSID of AP
<pass> PSK of AP
[esp32]> free
257200
[esp32]> deep_sleep -t 1000
I (146929) deep_sleep: Enabling timer wakeup, timeout=1000000us
I (619) heap_init: Initializing. RAM available for dynamic allocation:
I (620) heap_init: At 3FFAE2A0 len 00001D60 (7 KiB): DRAM
I (626) heap_init: At 3FFB7EA0 len 00028160 (160 KiB): DRAM
I (645) heap_init: At 3FFE0440 len 00003BC0 (14 KiB): D/IRAM
I (664) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (684) heap_init: At 40093EA8 len 0000C158 (48 KiB): IRAM
This is an example of ESP-IDF console component.
Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
[esp32]> join --timeout 10000 test_ap test_password
I (182639) connect: Connecting to 'test_ap'
I (184619) connect: Connected
[esp32]> free
212328
[esp32]> restart
I (205639) restart: Restarting
I (616) heap_init: Initializing. RAM available for dynamic allocation:
I (617) heap_init: At 3FFAE2A0 len 00001D60 (7 KiB): DRAM
I (623) heap_init: At 3FFB7EA0 len 00028160 (160 KiB): DRAM
I (642) heap_init: At 3FFE0440 len 00003BC0 (14 KiB): D/IRAM
I (661) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (681) heap_init: At 40093EA8 len 0000C158 (48 KiB): IRAM
This is an example of ESP-IDF console component.
Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
[esp32]>
```
## Troubleshooting
### Line Endings
The line endings in the Console Example are configured to match particular serial monitors. Therefore, if the following log output appears, consider using a different serial monitor (e.g. Putty for Windows) or modify the example's [UART configuration](#Configuring-UART-and-VFS).
```
This is an example of ESP-IDF console component.
Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
Your terminal application does not support escape sequences.
Line editing and history features are disabled.
On Windows, try using Putty instead.
esp32>
```
## Example Breakdown
### Configuring UART
The ``initialize_console()`` function in the example configures some aspects of UART relevant to the operation of the console.
- **Line Endings**: The default line endings are configured to match those expected/generated by common serial monitor programs, such as `screen`, `minicom`, and the `idf_monitor.py` included in the SDK. The default behavior for these commands are:
- When 'enter' key is pressed on the keyboard, `CR` (0x13) code is sent to the serial device.
- To move the cursor to the beginning of the next line, serial device needs to send `CR LF` (0x13 0x10) sequence.
### Line editing
The main source file of the example illustrates how to use `linenoise` library, including line completion, hints, and history.
### Commands
Several commands are registered using `esp_console_cmd_register()` function. See the `register_wifi()` and `register_system()` functions in `cmd_wifi.c` and `cmd_system.c` files.
### Command handling
Main loop inside `app_main()` function illustrates how to use `linenoise` and `esp_console_run()` to implement read/eval loop.
### Argument parsing
Several commands implemented in `cmd_wifi.c` and `cmd_system.c` use the Argtable3 library to parse and check the arguments.
### Command history
Each time a new command line is obtained from `linenoise`, it is written into history and the history is saved into a file in flash memory. On reset, history is initialized from that file.

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "cmd_nvs.c"
INCLUDE_DIRS .
REQUIRES console nvs_flash)

View File

@@ -0,0 +1,598 @@
/* Console example — NVS commands
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_err.h"
#include "cmd_nvs.h"
#include "nvs.h"
typedef struct {
nvs_type_t type;
const char *str;
} type_str_pair_t;
static const type_str_pair_t type_str_pair[] = {
{ NVS_TYPE_I8, "i8" },
{ NVS_TYPE_U8, "u8" },
{ NVS_TYPE_U16, "u16" },
{ NVS_TYPE_I16, "i16" },
{ NVS_TYPE_U32, "u32" },
{ NVS_TYPE_I32, "i32" },
{ NVS_TYPE_U64, "u64" },
{ NVS_TYPE_I64, "i64" },
{ NVS_TYPE_STR, "str" },
{ NVS_TYPE_BLOB, "blob" },
{ NVS_TYPE_ANY, "any" },
};
static const size_t TYPE_STR_PAIR_SIZE = sizeof(type_str_pair) / sizeof(type_str_pair[0]);
static const char *ARG_TYPE_STR = "type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob";
static char current_namespace[16] = "storage";
static const char *TAG = "cmd_nvs";
static struct {
struct arg_str *key;
struct arg_str *type;
struct arg_str *value;
struct arg_end *end;
} set_args;
static struct {
struct arg_str *key;
struct arg_str *type;
struct arg_end *end;
} get_args;
static struct {
struct arg_str *key;
struct arg_end *end;
} erase_args;
static struct {
struct arg_str *namespace;
struct arg_end *end;
} erase_all_args;
static struct {
struct arg_str *namespace;
struct arg_end *end;
} namespace_args;
static struct {
struct arg_str *partition;
struct arg_str *namespace;
struct arg_str *type;
struct arg_end *end;
} list_args;
static nvs_type_t str_to_type(const char *type)
{
for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) {
const type_str_pair_t *p = &type_str_pair[i];
if (strcmp(type, p->str) == 0) {
return p->type;
}
}
return NVS_TYPE_ANY;
}
static const char *type_to_str(nvs_type_t type)
{
for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) {
const type_str_pair_t *p = &type_str_pair[i];
if (p->type == type) {
return p->str;
}
}
return "Unknown";
}
static esp_err_t store_blob(nvs_handle_t nvs, const char *key, const char *str_values)
{
uint8_t value;
size_t str_len = strlen(str_values);
size_t blob_len = str_len / 2;
if (str_len % 2) {
ESP_LOGE(TAG, "Blob data must contain even number of characters");
return ESP_ERR_NVS_TYPE_MISMATCH;
}
char *blob = (char *)malloc(blob_len);
if (blob == NULL) {
return ESP_ERR_NO_MEM;
}
for (int i = 0, j = 0; i < str_len; i++) {
char ch = str_values[i];
if (ch >= '0' && ch <= '9') {
value = ch - '0';
} else if (ch >= 'A' && ch <= 'F') {
value = ch - 'A' + 10;
} else if (ch >= 'a' && ch <= 'f') {
value = ch - 'a' + 10;
} else {
ESP_LOGE(TAG, "Blob data contain invalid character");
free(blob);
return ESP_ERR_NVS_TYPE_MISMATCH;
}
if (i & 1) {
blob[j++] += value;
} else {
blob[j] = value << 4;
}
}
esp_err_t err = nvs_set_blob(nvs, key, blob, blob_len);
free(blob);
if (err == ESP_OK) {
err = nvs_commit(nvs);
}
return err;
}
static void print_blob(const char *blob, size_t len)
{
for (int i = 0; i < len; i++) {
printf("%02x", blob[i]);
}
printf("\n");
}
static esp_err_t set_value_in_nvs(const char *key, const char *str_type, const char *str_value)
{
esp_err_t err;
nvs_handle_t nvs;
bool range_error = false;
nvs_type_t type = str_to_type(str_type);
if (type == NVS_TYPE_ANY) {
ESP_LOGE(TAG, "Type '%s' is undefined", str_type);
return ESP_ERR_NVS_TYPE_MISMATCH;
}
err = nvs_open(current_namespace, NVS_READWRITE, &nvs);
if (err != ESP_OK) {
return err;
}
if (type == NVS_TYPE_I8) {
int32_t value = strtol(str_value, NULL, 0);
if (value < INT8_MIN || value > INT8_MAX || errno == ERANGE) {
range_error = true;
} else {
err = nvs_set_i8(nvs, key, (int8_t)value);
}
} else if (type == NVS_TYPE_U8) {
uint32_t value = strtoul(str_value, NULL, 0);
if (value > UINT8_MAX || errno == ERANGE) {
range_error = true;
} else {
err = nvs_set_u8(nvs, key, (uint8_t)value);
}
} else if (type == NVS_TYPE_I16) {
int32_t value = strtol(str_value, NULL, 0);
if (value < INT16_MIN || value > INT16_MAX || errno == ERANGE) {
range_error = true;
} else {
err = nvs_set_i16(nvs, key, (int16_t)value);
}
} else if (type == NVS_TYPE_U16) {
uint32_t value = strtoul(str_value, NULL, 0);
if (value > UINT16_MAX || errno == ERANGE) {
range_error = true;
} else {
err = nvs_set_u16(nvs, key, (uint16_t)value);
}
} else if (type == NVS_TYPE_I32) {
int32_t value = strtol(str_value, NULL, 0);
if (errno != ERANGE) {
err = nvs_set_i32(nvs, key, value);
}
} else if (type == NVS_TYPE_U32) {
uint32_t value = strtoul(str_value, NULL, 0);
if (errno != ERANGE) {
err = nvs_set_u32(nvs, key, value);
}
} else if (type == NVS_TYPE_I64) {
int64_t value = strtoll(str_value, NULL, 0);
if (errno != ERANGE) {
err = nvs_set_i64(nvs, key, value);
}
} else if (type == NVS_TYPE_U64) {
uint64_t value = strtoull(str_value, NULL, 0);
if (errno != ERANGE) {
err = nvs_set_u64(nvs, key, value);
}
} else if (type == NVS_TYPE_STR) {
err = nvs_set_str(nvs, key, str_value);
} else if (type == NVS_TYPE_BLOB) {
err = store_blob(nvs, key, str_value);
}
if (range_error || errno == ERANGE) {
nvs_close(nvs);
return ESP_ERR_NVS_VALUE_TOO_LONG;
}
if (err == ESP_OK) {
err = nvs_commit(nvs);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Value stored under key '%s'", key);
}
}
nvs_close(nvs);
return err;
}
static esp_err_t get_value_from_nvs(const char *key, const char *str_type)
{
nvs_handle_t nvs;
esp_err_t err;
nvs_type_t type = str_to_type(str_type);
if (type == NVS_TYPE_ANY) {
ESP_LOGE(TAG, "Type '%s' is undefined", str_type);
return ESP_ERR_NVS_TYPE_MISMATCH;
}
err = nvs_open(current_namespace, NVS_READONLY, &nvs);
if (err != ESP_OK) {
return err;
}
if (type == NVS_TYPE_I8) {
int8_t value;
err = nvs_get_i8(nvs, key, &value);
if (err == ESP_OK) {
printf("%d\n", value);
}
} else if (type == NVS_TYPE_U8) {
uint8_t value;
err = nvs_get_u8(nvs, key, &value);
if (err == ESP_OK) {
printf("%u\n", value);
}
} else if (type == NVS_TYPE_I16) {
int16_t value;
err = nvs_get_i16(nvs, key, &value);
if (err == ESP_OK) {
printf("%u\n", value);
}
} else if (type == NVS_TYPE_U16) {
uint16_t value;
if ((err = nvs_get_u16(nvs, key, &value)) == ESP_OK) {
printf("%u\n", value);
}
} else if (type == NVS_TYPE_I32) {
int32_t value;
if ((err = nvs_get_i32(nvs, key, &value)) == ESP_OK) {
printf("%d\n", value);
}
} else if (type == NVS_TYPE_U32) {
uint32_t value;
if ((err = nvs_get_u32(nvs, key, &value)) == ESP_OK) {
printf("%u\n", value);
}
} else if (type == NVS_TYPE_I64) {
int64_t value;
if ((err = nvs_get_i64(nvs, key, &value)) == ESP_OK) {
printf("%lld\n", value);
}
} else if (type == NVS_TYPE_U64) {
uint64_t value;
if ( (err = nvs_get_u64(nvs, key, &value)) == ESP_OK) {
printf("%llu\n", value);
}
} else if (type == NVS_TYPE_STR) {
size_t len;
if ( (err = nvs_get_str(nvs, key, NULL, &len)) == ESP_OK) {
char *str = (char *)malloc(len);
if ( (err = nvs_get_str(nvs, key, str, &len)) == ESP_OK) {
printf("%s\n", str);
}
free(str);
}
} else if (type == NVS_TYPE_BLOB) {
size_t len;
if ( (err = nvs_get_blob(nvs, key, NULL, &len)) == ESP_OK) {
char *blob = (char *)malloc(len);
if ( (err = nvs_get_blob(nvs, key, blob, &len)) == ESP_OK) {
print_blob(blob, len);
}
free(blob);
}
}
nvs_close(nvs);
return err;
}
static esp_err_t erase(const char *key)
{
nvs_handle_t nvs;
esp_err_t err = nvs_open(current_namespace, NVS_READWRITE, &nvs);
if (err == ESP_OK) {
err = nvs_erase_key(nvs, key);
if (err == ESP_OK) {
err = nvs_commit(nvs);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Value with key '%s' erased", key);
}
}
nvs_close(nvs);
}
return err;
}
static esp_err_t erase_all(const char *name)
{
nvs_handle_t nvs;
esp_err_t err = nvs_open(name, NVS_READWRITE, &nvs);
if (err == ESP_OK) {
err = nvs_erase_all(nvs);
if (err == ESP_OK) {
err = nvs_commit(nvs);
}
}
ESP_LOGI(TAG, "Namespace '%s' was %s erased", name, (err == ESP_OK) ? "" : "not");
nvs_close(nvs);
return ESP_OK;
}
static int list(const char *part, const char *name, const char *str_type)
{
nvs_type_t type = str_to_type(str_type);
nvs_iterator_t it = nvs_entry_find(part, NULL, type);
if (it == NULL) {
ESP_LOGE(TAG, "No such enty was found");
return 1;
}
do {
nvs_entry_info_t info;
nvs_entry_info(it, &info);
it = nvs_entry_next(it);
printf("namespace '%s', key '%s', type '%s' \n",
info.namespace_name, info.key, type_to_str(info.type));
} while (it != NULL);
return 0;
}
static int set_value(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &set_args);
if (nerrors != 0) {
arg_print_errors(stderr, set_args.end, argv[0]);
return 1;
}
const char *key = set_args.key->sval[0];
const char *type = set_args.type->sval[0];
const char *values = set_args.value->sval[0];
esp_err_t err = set_value_in_nvs(key, type, values);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s", esp_err_to_name(err));
return 1;
}
return 0;
}
static int get_value(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &get_args);
if (nerrors != 0) {
arg_print_errors(stderr, get_args.end, argv[0]);
return 1;
}
const char *key = get_args.key->sval[0];
const char *type = get_args.type->sval[0];
esp_err_t err = get_value_from_nvs(key, type);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s", esp_err_to_name(err));
return 1;
}
return 0;
}
static int erase_value(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &erase_args);
if (nerrors != 0) {
arg_print_errors(stderr, erase_args.end, argv[0]);
return 1;
}
const char *key = erase_args.key->sval[0];
esp_err_t err = erase(key);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s", esp_err_to_name(err));
return 1;
}
return 0;
}
static int erase_namespace(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &erase_all_args);
if (nerrors != 0) {
arg_print_errors(stderr, erase_all_args.end, argv[0]);
return 1;
}
const char *name = erase_all_args.namespace->sval[0];
esp_err_t err = erase_all(name);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s", esp_err_to_name(err));
return 1;
}
return 0;
}
static int set_namespace(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &namespace_args);
if (nerrors != 0) {
arg_print_errors(stderr, namespace_args.end, argv[0]);
return 1;
}
const char *namespace = namespace_args.namespace->sval[0];
strlcpy(current_namespace, namespace, sizeof(current_namespace));
ESP_LOGI(TAG, "Namespace set to '%s'", current_namespace);
return 0;
}
static int list_entries(int argc, char **argv)
{
list_args.partition->sval[0] = "";
list_args.namespace->sval[0] = "";
list_args.type->sval[0] = "";
int nerrors = arg_parse(argc, argv, (void **) &list_args);
if (nerrors != 0) {
arg_print_errors(stderr, list_args.end, argv[0]);
return 1;
}
const char *part = list_args.partition->sval[0];
const char *name = list_args.namespace->sval[0];
const char *type = list_args.type->sval[0];
return list(part, name, type);
}
void register_nvs(void)
{
set_args.key = arg_str1(NULL, NULL, "<key>", "key of the value to be set");
set_args.type = arg_str1(NULL, NULL, "<type>", ARG_TYPE_STR);
set_args.value = arg_str1("v", "value", "<value>", "value to be stored");
set_args.end = arg_end(2);
get_args.key = arg_str1(NULL, NULL, "<key>", "key of the value to be read");
get_args.type = arg_str1(NULL, NULL, "<type>", ARG_TYPE_STR);
get_args.end = arg_end(2);
erase_args.key = arg_str1(NULL, NULL, "<key>", "key of the value to be erased");
erase_args.end = arg_end(2);
erase_all_args.namespace = arg_str1(NULL, NULL, "<namespace>", "namespace to be erased");
erase_all_args.end = arg_end(2);
namespace_args.namespace = arg_str1(NULL, NULL, "<namespace>", "namespace of the partition to be selected");
namespace_args.end = arg_end(2);
list_args.partition = arg_str1(NULL, NULL, "<partition>", "partition name");
list_args.namespace = arg_str0("n", "namespace", "<namespace>", "namespace name");
list_args.type = arg_str0("t", "type", "<type>", ARG_TYPE_STR);
list_args.end = arg_end(2);
const esp_console_cmd_t set_cmd = {
.command = "nvs_set",
.help = "Set key-value pair in selected namespace.\n"
"Examples:\n"
" nvs_set VarName i32 -v 123 \n"
" nvs_set VarName srt -v YourString \n"
" nvs_set VarName blob -v 0123456789abcdef \n",
.hint = NULL,
.func = &set_value,
.argtable = &set_args
};
const esp_console_cmd_t get_cmd = {
.command = "nvs_get",
.help = "Get key-value pair from selected namespace. \n"
"Example: nvs_get VarName i32",
.hint = NULL,
.func = &get_value,
.argtable = &get_args
};
const esp_console_cmd_t erase_cmd = {
.command = "nvs_erase",
.help = "Erase key-value pair from current namespace",
.hint = NULL,
.func = &erase_value,
.argtable = &erase_args
};
const esp_console_cmd_t erase_namespace_cmd = {
.command = "nvs_erase_namespace",
.help = "Erases specified namespace",
.hint = NULL,
.func = &erase_namespace,
.argtable = &erase_all_args
};
const esp_console_cmd_t namespace_cmd = {
.command = "nvs_namespace",
.help = "Set current namespace",
.hint = NULL,
.func = &set_namespace,
.argtable = &namespace_args
};
const esp_console_cmd_t list_entries_cmd = {
.command = "nvs_list",
.help = "List stored key-value pairs stored in NVS."
"Namespace and type can be specified to print only those key-value pairs.\n"
"Following command list variables stored inside 'nvs' partition, under namespace 'storage' with type uint32_t"
"Example: nvs_list nvs -n storage -t u32 \n",
.hint = NULL,
.func = &list_entries,
.argtable = &list_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&set_cmd));
ESP_ERROR_CHECK(esp_console_cmd_register(&get_cmd));
ESP_ERROR_CHECK(esp_console_cmd_register(&erase_cmd));
ESP_ERROR_CHECK(esp_console_cmd_register(&namespace_cmd));
ESP_ERROR_CHECK(esp_console_cmd_register(&list_entries_cmd));
ESP_ERROR_CHECK(esp_console_cmd_register(&erase_namespace_cmd));
}

View File

@@ -0,0 +1,20 @@
/* Console example — declarations of command registration functions.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Register NVS functions
void register_nvs(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,10 @@
#
# Component Makefile
#
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
# this will take the sources in the src/ directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the SDK documents if you need to do this.
#
COMPONENT_ADD_INCLUDEDIRS := .

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "cmd_system.c"
INCLUDE_DIRS .
REQUIRES console spi_flash)

View File

@@ -0,0 +1,368 @@
/* Console example — various system commands
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "esp_log.h"
#include "esp_console.h"
#include "esp_system.h"
#include "esp_sleep.h"
#include "esp_spi_flash.h"
#include "driver/rtc_io.h"
#include "driver/uart.h"
#include "argtable3/argtable3.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "cmd_system.h"
#include "sdkconfig.h"
#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
#define WITH_TASKS_INFO 1
#endif
static const char *TAG = "cmd_system";
static void register_free(void);
static void register_heap(void);
static void register_version(void);
static void register_restart(void);
static void register_deep_sleep(void);
static void register_light_sleep(void);
#if WITH_TASKS_INFO
static void register_tasks(void);
#endif
void register_system_common(void)
{
register_free();
register_heap();
register_version();
register_restart();
#if WITH_TASKS_INFO
register_tasks();
#endif
}
void register_system_sleep(void)
{
register_deep_sleep();
register_light_sleep();
}
void register_system(void)
{
register_system_common();
register_system_sleep();
}
/* 'version' command */
static int get_version(int argc, char **argv)
{
esp_chip_info_t info;
esp_chip_info(&info);
printf("IDF Version:%s\r\n", esp_get_idf_version());
printf("Chip info:\r\n");
printf("\tmodel:%s\r\n", info.model == CHIP_ESP32 ? "ESP32" : "Unknow");
printf("\tcores:%d\r\n", info.cores);
printf("\tfeature:%s%s%s%s%d%s\r\n",
info.features & CHIP_FEATURE_WIFI_BGN ? "/802.11bgn" : "",
info.features & CHIP_FEATURE_BLE ? "/BLE" : "",
info.features & CHIP_FEATURE_BT ? "/BT" : "",
info.features & CHIP_FEATURE_EMB_FLASH ? "/Embedded-Flash:" : "/External-Flash:",
spi_flash_get_chip_size() / (1024 * 1024), " MB");
printf("\trevision number:%d\r\n", info.revision);
return 0;
}
static void register_version(void)
{
const esp_console_cmd_t cmd = {
.command = "version",
.help = "Get version of chip and SDK",
.hint = NULL,
.func = &get_version,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'restart' command restarts the program */
static int restart(int argc, char **argv)
{
ESP_LOGI(TAG, "Restarting");
esp_restart();
}
static void register_restart(void)
{
const esp_console_cmd_t cmd = {
.command = "restart",
.help = "Software reset of the chip",
.hint = NULL,
.func = &restart,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'free' command prints available heap memory */
static int free_mem(int argc, char **argv)
{
printf("%d\n", esp_get_free_heap_size());
return 0;
}
static void register_free(void)
{
const esp_console_cmd_t cmd = {
.command = "free",
.help = "Get the current size of free heap memory",
.hint = NULL,
.func = &free_mem,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/* 'heap' command prints minumum heap size */
static int heap_size(int argc, char **argv)
{
uint32_t heap_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
printf("min heap size: %u\n", heap_size);
return 0;
}
static void register_heap(void)
{
const esp_console_cmd_t heap_cmd = {
.command = "heap",
.help = "Get minimum size of free heap memory that was available during program execution",
.hint = NULL,
.func = &heap_size,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&heap_cmd) );
}
/** 'tasks' command prints the list of tasks and related information */
#if WITH_TASKS_INFO
static int tasks_info(int argc, char **argv)
{
const size_t bytes_per_task = 40; /* see vTaskList description */
char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task);
if (task_list_buffer == NULL) {
ESP_LOGE(TAG, "failed to allocate buffer for vTaskList output");
return 1;
}
fputs("Task Name\tStatus\tPrio\tHWM\tTask#", stdout);
#ifdef CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
fputs("\tAffinity", stdout);
#endif
fputs("\n", stdout);
vTaskList(task_list_buffer);
fputs(task_list_buffer, stdout);
free(task_list_buffer);
return 0;
}
static void register_tasks(void)
{
const esp_console_cmd_t cmd = {
.command = "tasks",
.help = "Get information about running tasks",
.hint = NULL,
.func = &tasks_info,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
#endif // WITH_TASKS_INFO
/** 'deep_sleep' command puts the chip into deep sleep mode */
static struct {
struct arg_int *wakeup_time;
#if SOC_PM_SUPPORT_EXT_WAKEUP
struct arg_int *wakeup_gpio_num;
struct arg_int *wakeup_gpio_level;
#endif
struct arg_end *end;
} deep_sleep_args;
static int deep_sleep(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &deep_sleep_args);
if (nerrors != 0) {
arg_print_errors(stderr, deep_sleep_args.end, argv[0]);
return 1;
}
if (deep_sleep_args.wakeup_time->count) {
uint64_t timeout = 1000ULL * deep_sleep_args.wakeup_time->ival[0];
ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
}
#if SOC_PM_SUPPORT_EXT_WAKEUP
if (deep_sleep_args.wakeup_gpio_num->count) {
int io_num = deep_sleep_args.wakeup_gpio_num->ival[0];
if (!esp_sleep_is_valid_wakeup_gpio(io_num)) {
ESP_LOGE(TAG, "GPIO %d is not an RTC IO", io_num);
return 1;
}
int level = 0;
if (deep_sleep_args.wakeup_gpio_level->count) {
level = deep_sleep_args.wakeup_gpio_level->ival[0];
if (level != 0 && level != 1) {
ESP_LOGE(TAG, "Invalid wakeup level: %d", level);
return 1;
}
}
ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup(1ULL << io_num, level) );
ESP_LOGE(TAG, "GPIO wakeup from deep sleep currently unsupported on ESP32-C3");
}
#endif // SOC_PM_SUPPORT_EXT_WAKEUP
#if CONFIG_IDF_TARGET_ESP32
rtc_gpio_isolate(GPIO_NUM_12);
#endif //CONFIG_IDF_TARGET_ESP32
esp_deep_sleep_start();
}
static void register_deep_sleep(void)
{
int num_args = 1;
deep_sleep_args.wakeup_time =
arg_int0("t", "time", "<t>", "Wake up time, ms");
#if SOC_PM_SUPPORT_EXT_WAKEUP
deep_sleep_args.wakeup_gpio_num =
arg_int0(NULL, "io", "<n>",
"If specified, wakeup using GPIO with given number");
deep_sleep_args.wakeup_gpio_level =
arg_int0(NULL, "io_level", "<0|1>", "GPIO level to trigger wakeup");
num_args += 2;
#endif
deep_sleep_args.end = arg_end(num_args);
const esp_console_cmd_t cmd = {
.command = "deep_sleep",
.help = "Enter deep sleep mode. "
#if SOC_PM_SUPPORT_EXT_WAKEUP
"Two wakeup modes are supported: timer and GPIO. "
#else
"Timer wakeup mode is supported. "
#endif
"If no wakeup option is specified, will sleep indefinitely.",
.hint = NULL,
.func = &deep_sleep,
.argtable = &deep_sleep_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'light_sleep' command puts the chip into light sleep mode */
static struct {
struct arg_int *wakeup_time;
struct arg_int *wakeup_gpio_num;
struct arg_int *wakeup_gpio_level;
struct arg_end *end;
} light_sleep_args;
static int light_sleep(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &light_sleep_args);
if (nerrors != 0) {
arg_print_errors(stderr, light_sleep_args.end, argv[0]);
return 1;
}
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
if (light_sleep_args.wakeup_time->count) {
uint64_t timeout = 1000ULL * light_sleep_args.wakeup_time->ival[0];
ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
}
int io_count = light_sleep_args.wakeup_gpio_num->count;
if (io_count != light_sleep_args.wakeup_gpio_level->count) {
ESP_LOGE(TAG, "Should have same number of 'io' and 'io_level' arguments");
return 1;
}
for (int i = 0; i < io_count; ++i) {
int io_num = light_sleep_args.wakeup_gpio_num->ival[i];
int level = light_sleep_args.wakeup_gpio_level->ival[i];
if (level != 0 && level != 1) {
ESP_LOGE(TAG, "Invalid wakeup level: %d", level);
return 1;
}
ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
ESP_ERROR_CHECK( gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL) );
}
if (io_count > 0) {
ESP_ERROR_CHECK( esp_sleep_enable_gpio_wakeup() );
}
if (CONFIG_ESP_CONSOLE_UART_NUM >= 0 && CONFIG_ESP_CONSOLE_UART_NUM <= UART_NUM_1) {
ESP_LOGI(TAG, "Enabling UART wakeup (press ENTER to exit light sleep)");
ESP_ERROR_CHECK( uart_set_wakeup_threshold(CONFIG_ESP_CONSOLE_UART_NUM, 3) );
ESP_ERROR_CHECK( esp_sleep_enable_uart_wakeup(CONFIG_ESP_CONSOLE_UART_NUM) );
}
fflush(stdout);
fsync(fileno(stdout));
esp_light_sleep_start();
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
const char *cause_str;
switch (cause) {
case ESP_SLEEP_WAKEUP_GPIO:
cause_str = "GPIO";
break;
case ESP_SLEEP_WAKEUP_UART:
cause_str = "UART";
break;
case ESP_SLEEP_WAKEUP_TIMER:
cause_str = "timer";
break;
default:
cause_str = "unknown";
printf("%d\n", cause);
}
ESP_LOGI(TAG, "Woke up from: %s", cause_str);
return 0;
}
static void register_light_sleep(void)
{
light_sleep_args.wakeup_time =
arg_int0("t", "time", "<t>", "Wake up time, ms");
light_sleep_args.wakeup_gpio_num =
arg_intn(NULL, "io", "<n>", 0, 8,
"If specified, wakeup using GPIO with given number");
light_sleep_args.wakeup_gpio_level =
arg_intn(NULL, "io_level", "<0|1>", 0, 8, "GPIO level to trigger wakeup");
light_sleep_args.end = arg_end(3);
const esp_console_cmd_t cmd = {
.command = "light_sleep",
.help = "Enter light sleep mode. "
"Two wakeup modes are supported: timer and GPIO. "
"Multiple GPIO pins can be specified using pairs of "
"'io' and 'io_level' arguments. "
"Will also wake up on UART input.",
.hint = NULL,
.func = &light_sleep,
.argtable = &light_sleep_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

View File

@@ -0,0 +1,26 @@
/* Console example — various system commands
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Register all system functions
void register_system(void);
// Register common system functions: "version", "restart", "free", "heap", "tasks"
void register_system_common(void);
// Register deep and light sleep functions
void register_system_sleep(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,10 @@
#
# Component Makefile
#
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
# this will take the sources in the src/ directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the SDK documents if you need to do this.
#
COMPONENT_ADD_INCLUDEDIRS := .

View File

@@ -0,0 +1,22 @@
# type: ignore
from __future__ import print_function
import ttfw_idf
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC', target=['esp32', 'esp32c3'])
def test_examples_system_console_advanced(env, _):
dut = env.get_dut('console_example', 'examples/system/console/advanced', app_config_name='history')
print('Using binary path: {}'.format(dut.app.binary_path))
dut.start_app()
dut.expect('Command history enabled')
env.close_dut(dut.name)
dut = env.get_dut('console_example', 'examples/system/console/advanced', app_config_name='nohistory')
print('Using binary path: {}'.format(dut.app.binary_path))
dut.start_app()
dut.expect('Command history disabled')
if __name__ == '__main__':
test_examples_system_console_advanced()

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "cmd_wifi.c"
"console_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,11 @@
menu "Example Configuration"
config STORE_HISTORY
bool "Store command history in flash"
default y
help
Linenoise line editing library provides functions to save and load
command history. If this option is enabled, initalizes a FAT filesystem
and uses it to store command history.
endmenu

View File

@@ -0,0 +1,21 @@
/* Console example — declarations of command registration functions.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "cmd_system.h"
#include "cmd_wifi.h"
#include "cmd_nvs.h"
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,132 @@
/* Console example — WiFi commands
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "cmd_decl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "cmd_wifi.h"
#define JOIN_TIMEOUT_MS (10000)
static EventGroupHandle_t wifi_event_group;
const int CONNECTED_BIT = BIT0;
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
}
}
static void initialise_wifi(void)
{
esp_log_level_set("wifi", ESP_LOG_WARN);
static bool initialized = false;
if (initialized) {
return;
}
ESP_ERROR_CHECK(esp_netif_init());
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();
assert(ap_netif);
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_NULL) );
ESP_ERROR_CHECK( esp_wifi_start() );
initialized = true;
}
static bool wifi_join(const char *ssid, const char *pass, int timeout_ms)
{
initialise_wifi();
wifi_config_t wifi_config = { 0 };
strlcpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
if (pass) {
strlcpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
}
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
esp_wifi_connect();
int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
pdFALSE, pdTRUE, timeout_ms / portTICK_PERIOD_MS);
return (bits & CONNECTED_BIT) != 0;
}
/** Arguments used by 'join' function */
static struct {
struct arg_int *timeout;
struct arg_str *ssid;
struct arg_str *password;
struct arg_end *end;
} join_args;
static int connect(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &join_args);
if (nerrors != 0) {
arg_print_errors(stderr, join_args.end, argv[0]);
return 1;
}
ESP_LOGI(__func__, "Connecting to '%s'",
join_args.ssid->sval[0]);
/* set default value*/
if (join_args.timeout->count == 0) {
join_args.timeout->ival[0] = JOIN_TIMEOUT_MS;
}
bool connected = wifi_join(join_args.ssid->sval[0],
join_args.password->sval[0],
join_args.timeout->ival[0]);
if (!connected) {
ESP_LOGW(__func__, "Connection timed out");
return 1;
}
ESP_LOGI(__func__, "Connected");
return 0;
}
void register_wifi(void)
{
join_args.timeout = arg_int0(NULL, "timeout", "<t>", "Connection timeout, ms");
join_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
join_args.password = arg_str0(NULL, NULL, "<pass>", "PSK of AP");
join_args.end = arg_end(2);
const esp_console_cmd_t join_cmd = {
.command = "join",
.help = "Join WiFi AP as a station",
.hint = NULL,
.func = &connect,
.argtable = &join_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&join_cmd) );
}

View File

@@ -0,0 +1,20 @@
/* Console example — declarations of command registration functions.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Register WiFi functions
void register_wifi(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,219 @@
/* Console example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "esp_system.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "cmd_decl.h"
#include "esp_vfs_fat.h"
#include "nvs.h"
#include "nvs_flash.h"
#ifdef CONFIG_ESP_CONSOLE_USB_CDC
#error This example is incompatible with USB CDC console. Please try "console_usb" example instead.
#endif // CONFIG_ESP_CONSOLE_USB_CDC
static const char* TAG = "example";
#define PROMPT_STR CONFIG_IDF_TARGET
/* Console command history can be stored to and loaded from a file.
* The easiest way to do this is to use FATFS filesystem on top of
* wear_levelling library.
*/
#if CONFIG_STORE_HISTORY
#define MOUNT_PATH "/data"
#define HISTORY_PATH MOUNT_PATH "/history.txt"
static void initialize_filesystem(void)
{
static wl_handle_t wl_handle;
const esp_vfs_fat_mount_config_t mount_config = {
.max_files = 4,
.format_if_mount_failed = true
};
esp_err_t err = esp_vfs_fat_spiflash_mount(MOUNT_PATH, "storage", &mount_config, &wl_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
return;
}
}
#endif // CONFIG_STORE_HISTORY
static void initialize_nvs(void)
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK( nvs_flash_erase() );
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
}
static void initialize_console(void)
{
/* Drain stdout before reconfiguring it */
fflush(stdout);
fsync(fileno(stdout));
/* Disable buffering on stdin */
setvbuf(stdin, NULL, _IONBF, 0);
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
/* Configure UART. Note that REF_TICK is used so that the baud rate remains
* correct while APB frequency is changing in light sleep mode.
*/
const uart_config_t uart_config = {
.baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
.source_clk = UART_SCLK_REF_TICK,
#else
.source_clk = UART_SCLK_XTAL,
#endif
};
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK( uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM,
256, 0, 0, NULL, 0) );
ESP_ERROR_CHECK( uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config) );
/* Tell VFS to use UART driver */
esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
/* Initialize the console */
esp_console_config_t console_config = {
.max_cmdline_args = 8,
.max_cmdline_length = 256,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
#endif
};
ESP_ERROR_CHECK( esp_console_init(&console_config) );
/* Configure linenoise line completion library */
/* Enable multiline editing. If not set, long commands will scroll within
* single line.
*/
linenoiseSetMultiLine(1);
/* Tell linenoise where to get command completions and hints */
linenoiseSetCompletionCallback(&esp_console_get_completion);
linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);
/* Set command history size */
linenoiseHistorySetMaxLen(100);
/* Set command maximum length */
linenoiseSetMaxLineLen(console_config.max_cmdline_length);
/* Don't return empty lines */
linenoiseAllowEmpty(false);
#if CONFIG_STORE_HISTORY
/* Load command history from filesystem */
linenoiseHistoryLoad(HISTORY_PATH);
#endif
}
void app_main(void)
{
initialize_nvs();
#if CONFIG_STORE_HISTORY
initialize_filesystem();
ESP_LOGI(TAG, "Command history enabled");
#else
ESP_LOGI(TAG, "Command history disabled");
#endif
initialize_console();
/* Register commands */
esp_console_register_help_command();
register_system();
register_wifi();
register_nvs();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
const char* prompt = LOG_COLOR_I PROMPT_STR "> " LOG_RESET_COLOR;
printf("\n"
"This is an example of ESP-IDF console component.\n"
"Type 'help' to get the list of commands.\n"
"Use UP/DOWN arrows to navigate through command history.\n"
"Press TAB when typing command name to auto-complete.\n"
"Press Enter or Ctrl+C will terminate the console environment.\n");
/* Figure out if the terminal supports escape sequences */
int probe_status = linenoiseProbe();
if (probe_status) { /* zero indicates success */
printf("\n"
"Your terminal application does not support escape sequences.\n"
"Line editing and history features are disabled.\n"
"On Windows, try using Putty instead.\n");
linenoiseSetDumbMode(1);
#if CONFIG_LOG_COLORS
/* Since the terminal doesn't support escape sequences,
* don't use color codes in the prompt.
*/
prompt = PROMPT_STR "> ";
#endif //CONFIG_LOG_COLORS
}
/* Main loop */
while(true) {
/* Get a line using linenoise.
* The line is returned when ENTER is pressed.
*/
char* line = linenoise(prompt);
if (line == NULL) { /* Break on EOF or error */
break;
}
/* Add the command to the history if not empty*/
if (strlen(line) > 0) {
linenoiseHistoryAdd(line);
#if CONFIG_STORE_HISTORY
/* Save command history to filesystem */
linenoiseHistorySave(HISTORY_PATH);
#endif
}
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
printf("Unrecognized command\n");
} else if (err == ESP_ERR_INVALID_ARG) {
// command was empty
} else if (err == ESP_OK && ret != ESP_OK) {
printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret));
} else if (err != ESP_OK) {
printf("Internal error: %s\n", esp_err_to_name(err));
}
/* linenoise allocates line buffer on the heap, so need to free it */
linenoiseFree(line);
}
ESP_LOGE(TAG, "Error or end-of-input, terminating console");
esp_console_deinit();
}

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, fat, , 1M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, fat, , 1M,

View File

@@ -0,0 +1,4 @@
CONFIG_STORE_HISTORY=y
# IDF-3090
CONFIG_ESP32C3_REV_MIN_0=y
CONFIG_ESP32C3_REV_MIN=0

View File

@@ -0,0 +1,4 @@
CONFIG_STORE_HISTORY=n
# IDF-3090
CONFIG_ESP32C3_REV_MIN_0=y
CONFIG_ESP32C3_REV_MIN=0

View File

@@ -0,0 +1,17 @@
# Reduce bootloader log verbosity
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_BOOTLOADER_LOG_LEVEL=2
# Increase main task stack size
CONFIG_ESP_MAIN_TASK_STACK_SIZE=7168
# Enable filesystem
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
# Enable FreeRTOS stats formatting functions, needed for 'tasks' command
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y