add console component and example

This commit is contained in:
Ivan Grokhotkov 2017-08-15 16:11:19 +08:00
parent 363f201603
commit 5de7c91a54
23 changed files with 7779 additions and 0 deletions

View file

@ -0,0 +1,26 @@
Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
<sheitmann@users.sourceforge.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of STEWART HEITMANN nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,305 @@
/*******************************************************************************
* This file is part of the argtable3 library.
*
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
* <sheitmann@users.sourceforge.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of STEWART HEITMANN nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
#ifndef ARGTABLE3
#define ARGTABLE3
#include <stdio.h> /* FILE */
#include <time.h> /* struct tm */
#ifdef __cplusplus
extern "C" {
#endif
#define ARG_REX_ICASE 1
/* bit masks for arg_hdr.flag */
enum
{
ARG_TERMINATOR=0x1,
ARG_HASVALUE=0x2,
ARG_HASOPTVALUE=0x4
};
typedef void (arg_resetfn)(void *parent);
typedef int (arg_scanfn)(void *parent, const char *argval);
typedef int (arg_checkfn)(void *parent);
typedef void (arg_errorfn)(void *parent, FILE *fp, int error, const char *argval, const char *progname);
/*
* The arg_hdr struct defines properties that are common to all arg_xxx structs.
* The argtable library requires each arg_xxx struct to have an arg_hdr
* struct as its first data member.
* The argtable library functions then use this data to identify the
* properties of the command line option, such as its option tags,
* datatype string, and glossary strings, and so on.
* Moreover, the arg_hdr struct contains pointers to custom functions that
* are provided by each arg_xxx struct which perform the tasks of parsing
* that particular arg_xxx arguments, performing post-parse checks, and
* reporting errors.
* These functions are private to the individual arg_xxx source code
* and are the pointer to them are initiliased by that arg_xxx struct's
* constructor function. The user could alter them after construction
* if desired, but the original intention is for them to be set by the
* constructor and left unaltered.
*/
struct arg_hdr
{
char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */
const char *shortopts; /* String defining the short options */
const char *longopts; /* String defiing the long options */
const char *datatype; /* Description of the argument data type */
const char *glossary; /* Description of the option as shown by arg_print_glossary function */
int mincount; /* Minimum number of occurences of this option accepted */
int maxcount; /* Maximum number of occurences if this option accepted */
void *parent; /* Pointer to parent arg_xxx struct */
arg_resetfn *resetfn; /* Pointer to parent arg_xxx reset function */
arg_scanfn *scanfn; /* Pointer to parent arg_xxx scan function */
arg_checkfn *checkfn; /* Pointer to parent arg_xxx check function */
arg_errorfn *errorfn; /* Pointer to parent arg_xxx error function */
void *priv; /* Pointer to private header data for use by arg_xxx functions */
};
struct arg_rem
{
struct arg_hdr hdr; /* The mandatory argtable header struct */
};
struct arg_lit
{
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args */
};
struct arg_int
{
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args */
int *ival; /* Array of parsed argument values */
};
struct arg_dbl
{
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args */
double *dval; /* Array of parsed argument values */
};
struct arg_str
{
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args */
const char **sval; /* Array of parsed argument values */
};
struct arg_rex
{
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args */
const char **sval; /* Array of parsed argument values */
};
struct arg_file
{
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args*/
const char **filename; /* Array of parsed filenames (eg: /home/foo.bar) */
const char **basename; /* Array of parsed basenames (eg: foo.bar) */
const char **extension; /* Array of parsed extensions (eg: .bar) */
};
struct arg_date
{
struct arg_hdr hdr; /* The mandatory argtable header struct */
const char *format; /* strptime format string used to parse the date */
int count; /* Number of matching command line args */
struct tm *tmval; /* Array of parsed time values */
};
enum {ARG_ELIMIT=1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG};
struct arg_end
{
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of errors encountered */
int *error; /* Array of error codes */
void **parent; /* Array of pointers to offending arg_xxx struct */
const char **argval; /* Array of pointers to offending argv[] string */
};
/**** arg_xxx constructor functions *********************************/
struct arg_rem* arg_rem(const char* datatype, const char* glossary);
struct arg_lit* arg_lit0(const char* shortopts,
const char* longopts,
const char* glossary);
struct arg_lit* arg_lit1(const char* shortopts,
const char* longopts,
const char *glossary);
struct arg_lit* arg_litn(const char* shortopts,
const char* longopts,
int mincount,
int maxcount,
const char *glossary);
struct arg_key* arg_key0(const char* keyword,
int flags,
const char* glossary);
struct arg_key* arg_key1(const char* keyword,
int flags,
const char* glossary);
struct arg_key* arg_keyn(const char* keyword,
int flags,
int mincount,
int maxcount,
const char* glossary);
struct arg_int* arg_int0(const char* shortopts,
const char* longopts,
const char* datatype,
const char* glossary);
struct arg_int* arg_int1(const char* shortopts,
const char* longopts,
const char* datatype,
const char *glossary);
struct arg_int* arg_intn(const char* shortopts,
const char* longopts,
const char *datatype,
int mincount,
int maxcount,
const char *glossary);
struct arg_dbl* arg_dbl0(const char* shortopts,
const char* longopts,
const char* datatype,
const char* glossary);
struct arg_dbl* arg_dbl1(const char* shortopts,
const char* longopts,
const char* datatype,
const char *glossary);
struct arg_dbl* arg_dbln(const char* shortopts,
const char* longopts,
const char *datatype,
int mincount,
int maxcount,
const char *glossary);
struct arg_str* arg_str0(const char* shortopts,
const char* longopts,
const char* datatype,
const char* glossary);
struct arg_str* arg_str1(const char* shortopts,
const char* longopts,
const char* datatype,
const char *glossary);
struct arg_str* arg_strn(const char* shortopts,
const char* longopts,
const char* datatype,
int mincount,
int maxcount,
const char *glossary);
struct arg_rex* arg_rex0(const char* shortopts,
const char* longopts,
const char* pattern,
const char* datatype,
int flags,
const char* glossary);
struct arg_rex* arg_rex1(const char* shortopts,
const char* longopts,
const char* pattern,
const char* datatype,
int flags,
const char *glossary);
struct arg_rex* arg_rexn(const char* shortopts,
const char* longopts,
const char* pattern,
const char* datatype,
int mincount,
int maxcount,
int flags,
const char *glossary);
struct arg_file* arg_file0(const char* shortopts,
const char* longopts,
const char* datatype,
const char* glossary);
struct arg_file* arg_file1(const char* shortopts,
const char* longopts,
const char* datatype,
const char *glossary);
struct arg_file* arg_filen(const char* shortopts,
const char* longopts,
const char* datatype,
int mincount,
int maxcount,
const char *glossary);
struct arg_date* arg_date0(const char* shortopts,
const char* longopts,
const char* format,
const char* datatype,
const char* glossary);
struct arg_date* arg_date1(const char* shortopts,
const char* longopts,
const char* format,
const char* datatype,
const char *glossary);
struct arg_date* arg_daten(const char* shortopts,
const char* longopts,
const char* format,
const char* datatype,
int mincount,
int maxcount,
const char *glossary);
struct arg_end* arg_end(int maxerrors);
/**** other functions *******************************************/
int arg_nullcheck(void **argtable);
int arg_parse(int argc, char **argv, void **argtable);
void arg_print_option(FILE *fp, const char *shortopts, const char *longopts, const char *datatype, const char *suffix);
void arg_print_syntax(FILE *fp, void **argtable, const char *suffix);
void arg_print_syntaxv(FILE *fp, void **argtable, const char *suffix);
void arg_print_glossary(FILE *fp, void **argtable, const char *format);
void arg_print_glossary_gnu(FILE *fp, void **argtable);
void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname);
void arg_freetable(void **argtable, size_t n);
/**** deprecated functions, for back-compatibility only ********/
void arg_free(void **argtable);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,234 @@
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/param.h>
#include "esp_log.h"
#include "esp_console.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "rom/queue.h"
#define ANSI_COLOR_DEFAULT 39 /** Default foreground color */
typedef struct cmd_item_ {
/**
* Command name (statically allocated by application)
*/
const char* command;
/**
* Help text (statically allocated by application), may be NULL.
*/
const char* help;
/**
* Hint text, usually lists possible arguments, dynamically allocated.
* May be NULL.
*/
char* hint;
esp_console_cmd_func_t func; //!< pointer to the command handler
void* argtable; //!< optional pointer to arg table
SLIST_ENTRY(cmd_item_) next; //!< next command in the list
} cmd_item_t;
/** linked list of command structures */
static SLIST_HEAD(cmd_list_, cmd_item_) s_cmd_list;
/** run-time configuration options */
static esp_console_config_t s_config;
/** temporary buffer used for command line parsing */
static char* s_tmp_line_buf;
static const cmd_item_t* find_command_by_name(const char* name);
esp_err_t esp_console_init(const esp_console_config_t* config)
{
if (s_tmp_line_buf) {
return ESP_ERR_INVALID_STATE;
}
memcpy(&s_config, config, sizeof(s_config));
if (s_config.hint_color == 0) {
s_config.hint_color = ANSI_COLOR_DEFAULT;
}
s_tmp_line_buf = calloc(config->max_cmdline_length, 1);
if (s_tmp_line_buf == NULL) {
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
esp_err_t esp_console_deinit()
{
if (!s_tmp_line_buf) {
return ESP_ERR_INVALID_STATE;
}
free(s_tmp_line_buf);
cmd_item_t *it, *tmp;
SLIST_FOREACH_SAFE(it, &s_cmd_list, next, tmp) {
free(it->hint);
free(it);
}
return ESP_OK;
}
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd)
{
cmd_item_t *item = (cmd_item_t *) calloc(1, sizeof(*item));
if (item == NULL) {
return ESP_ERR_NO_MEM;
}
if (cmd->command == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (strchr(cmd->command, ' ') != NULL) {
return ESP_ERR_INVALID_ARG;
}
item->command = cmd->command;
item->help = cmd->help;
if (cmd->hint) {
/* Prepend a space before the hint. It separates command name and
* the hint. arg_print_syntax below adds this space as well.
*/
asprintf(&item->hint, " %s", cmd->hint);
} else if (cmd->argtable) {
/* Generate hint based on cmd->argtable */
char* buf;
size_t buf_size;
FILE* f = open_memstream(&buf, &buf_size);
arg_print_syntax(f, cmd->argtable, NULL);
fclose(f);
item->hint = buf;
}
item->argtable = cmd->argtable;
item->func = cmd->func;
cmd_item_t* last = SLIST_FIRST(&s_cmd_list);
if (last == NULL) {
SLIST_INSERT_HEAD(&s_cmd_list, item, next);
} else {
cmd_item_t* it;
while ((it = SLIST_NEXT(last, next)) != NULL) {
last = it;
}
SLIST_INSERT_AFTER(last, item, next);
}
return ESP_OK;
}
void esp_console_get_completion(const char *buf, linenoiseCompletions *lc)
{
size_t len = strlen(buf);
if (len == 0) {
return;
}
cmd_item_t* it;
SLIST_FOREACH(it, &s_cmd_list, next) {
/* Check if command starts with buf */
if (strncmp(buf, it->command, len) == 0) {
linenoiseAddCompletion(lc, it->command);
}
}
}
const char* esp_console_get_hint(const char *buf, int *color, int *bold)
{
int len = strlen(buf);
cmd_item_t* it;
SLIST_FOREACH(it, &s_cmd_list, next) {
if (strlen(it->command) == len &&
strncmp(buf, it->command, len) == 0) {
*color = s_config.hint_color;
*bold = s_config.hint_bold;
return it->hint;
}
}
return NULL;
}
static const cmd_item_t* find_command_by_name(const char* name)
{
const cmd_item_t* cmd = NULL;
cmd_item_t* it;
SLIST_FOREACH(it, &s_cmd_list, next) {
if (strcmp(name, it->command) == 0) {
cmd = it;
break;
}
}
return cmd;
}
esp_err_t esp_console_run(const char* cmdline, int* cmd_ret)
{
if (s_tmp_line_buf == NULL) {
return ESP_ERR_INVALID_STATE;
}
char** argv = (char**) calloc(s_config.max_cmdline_args, sizeof(char*));
if (argv == NULL) {
return ESP_ERR_NO_MEM;
}
strlcpy(s_tmp_line_buf, cmdline, s_config.max_cmdline_length);
size_t argc = esp_console_split_argv(s_tmp_line_buf, argv,
s_config.max_cmdline_args);
const cmd_item_t* cmd = find_command_by_name(argv[0]);
if (cmd == NULL) {
return ESP_ERR_NOT_FOUND;
}
*cmd_ret = (*cmd->func)(argc, argv);
free(argv);
return ESP_OK;
}
static int help_command(int argc, char** argv)
{
cmd_item_t* it;
/* Print summary of each command */
SLIST_FOREACH(it, &s_cmd_list, next) {
if (it->help == NULL) {
continue;
}
/* First line: command name and hint
* Pad all the hints to the same column
*/
const char* hint = (it->hint) ? it->hint : "";
printf("%-s %s\n", it->command, hint);
/* Second line: print help.
* Argtable has a nice helper function for this which does line
* wrapping.
*/
printf(" "); // arg_print_formatted does not indent the first line
arg_print_formatted(stdout, 2, 78, it->help);
/* Finally, print the list of arguments */
if (it->argtable) {
arg_print_glossary(stdout, (void**) it->argtable, " %12s %s\n");
}
printf("\n");
}
return 0;
}
esp_err_t esp_console_register_help_command()
{
esp_console_cmd_t command = {
.command = "help",
.help = "Print the list of registered commands",
.func = &help_command
};
return esp_console_cmd_register(&command);
}

View file

@ -0,0 +1,2 @@
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_SRCDIRS := linenoise argtable3 .

View file

@ -0,0 +1,181 @@
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stddef.h>
#include "esp_err.h"
// Forward declaration. Definition in linenoise/linenoise.h.
typedef struct linenoiseCompletions linenoiseCompletions;
/**
* @brief Parameters for console initialization
*/
typedef struct {
size_t max_cmdline_length; //!< length of command line buffer, in bytes
size_t max_cmdline_args; //!< maximum number of command line arguments to parse
int hint_color; //!< ASCII color code of hint text
int hint_bold; //!< Set to 1 to print hint text in bold
} esp_console_config_t;
/**
* @brief initialize console module
* Call this once before using other console module features
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM if out of memory
* - ESP_ERR_INVALID_STATE if already initialized
*/
esp_err_t esp_console_init(const esp_console_config_t* config);
/**
* @brief de-initialize console module
* Call this once when done using console module functions
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE if not initialized yet
*/
esp_err_t esp_console_deinit();
/**
* @brief Console command main function
* @param argc number of arguments
* @param argv array with argc entries, each pointing to a zero-terminated string argument
* @return console command return code, 0 indicates "success"
*/
typedef int (*esp_console_cmd_func_t)(int argc, char** argv);
/**
* @brief Console command description
*/
typedef struct {
/**
* Command name. Must not be NULL, must not contain spaces.
* The pointer must be valid until the call to esp_console_deinit.
*/
const char* command; //!< command name
/**
* Help text for the command, shown by help command.
* If set, the pointer must be valid until the call to esp_console_deinit.
* If not set, the command will not be listed in 'help' output.
*/
const char* help;
/**
* Hint text, usually lists possible arguments.
* If set to NULL, and 'argtable' field is non-NULL, hint will be generated
* automatically
*/
const char* hint;
/**
* Pointer to a function which implements the command.
*/
esp_console_cmd_func_t func;
/**
* Array or structure of pointers to arg_xxx structures, may be NULL.
* Used to generate hint text if 'hint' is set to NULL.
* Array/structure which this field points to must end with an arg_end.
* Only used for the duration of esp_console_cmd_register call.
*/
void* argtable;
} esp_console_cmd_t;
/**
* @brief Register console command
* @param cmd pointer to the command description; can point to a temporary value
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM if out of memory
*/
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd);
/**
* @brief Run command line
* @param cmdline command line (command name followed by a number of arguments)
* @param[out] cmd_ret return code from the command (set if command was run)
* @return
* - ESP_OK, if command was run
* - ESP_ERR_NOT_FOUND, if command with given name wasn't registered
* - ESP_ERR_INVALID_STATE, if esp_console_init wasn't called
*/
esp_err_t esp_console_run(const char* cmdline, int* cmd_ret);
/**
* @brief Split command line into arguments in place
*
* - This function finds whitespace-separated arguments in the given input line.
*
* 'abc def 1 20 .3' -> [ 'abc', 'def', '1', '20', '.3' ]
*
* - Argument which include spaces may be surrounded with quotes. In this case
* spaces are preserved and quotes are stripped.
*
* 'abc "123 456" def' -> [ 'abc', '123 456', 'def' ]
*
* - Escape sequences may be used to produce backslash, double quote, and space:
*
* 'a\ b\\c\"' -> [ 'a b\c"' ]
*
* Pointers to at most argv_size - 1 arguments are returned in argv array.
* The pointer after the last one (i.e. argv[argc]) is set to NULL.
*
* @param line pointer to buffer to parse; it is modified in place
* @param argv array where the pointers to arguments are written
* @param argv_size number of elements in argv_array (max. number of arguments)
* @return number of arguments found (argc)
*/
size_t esp_console_split_argv(char *line, char **argv, size_t argv_size);
/**
* @brief Callback which provides command completion for linenoise library
*
* When using linenoise for line editing, command completion support
* can be enabled like this:
*
* linenoiseSetCompletionCallback(&esp_console_get_completion);
*
* @param buf the string typed by the user
* @param lc linenoiseCompletions to be filled in
*/
void esp_console_get_completion(const char *buf, linenoiseCompletions *lc);
/**
* @brief Callback which provides command hints for linenoise library
*
* When using linenoise for line editing, hints support can be enabled as
* follows:
*
* linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);
*
* The extra cast is needed because linenoiseHintsCallback is defined as
* returning a char* instead of const char*.
*
* @param buf line typed by the user
* @param[out] color ANSI color code to be used when displaying the hint
* @param[out] bold set to 1 if hint has to be displayed in bold
* @return string containing the hint text. This string is persistent and should
* not be freed (i.e. linenoiseSetFreeHintsCallback should not be used).
*/
const char *esp_console_get_hint(const char *buf, int *color, int *bold);
/**
* @brief Register a 'help' command
* Default 'help' command prints the list of registered commands along with
* hints and help strings.
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE, if esp_console_init wasn't called
*/
esp_err_t esp_console_register_help_command();

View file

@ -0,0 +1,25 @@
Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,74 @@
/* linenoise.h -- VERSION 1.0
*
* Guerrilla line editing library against the idea that a line editing lib
* needs to be 20,000 lines of C code.
*
* See linenoise.c for more information.
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __LINENOISE_H
#define __LINENOISE_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
typedef void(linenoiseFreeHintsCallback)(void *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
char *linenoise(const char *prompt);
void linenoiseFree(void *ptr);
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
void linenoiseHistoryFree();
void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void);
#ifdef __cplusplus
}
#endif
#endif /* __LINENOISE_H */

View file

@ -0,0 +1,120 @@
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define SS_FLAG_ESCAPE 0x8
typedef enum {
/* parsing the space between arguments */
SS_SPACE = 0x0,
/* parsing an argument which isn't quoted */
SS_ARG = 0x1,
/* parsing a quoted argument */
SS_QUOTED_ARG = 0x2,
/* parsing an escape sequence within unquoted argument */
SS_ARG_ESCAPED = SS_ARG | SS_FLAG_ESCAPE,
/* parsing an escape sequence within a quoted argument */
SS_QUOTED_ARG_ESCAPED = SS_QUOTED_ARG | SS_FLAG_ESCAPE,
} split_state_t;
size_t esp_console_split_argv(char *line, char **argv, size_t argv_size)
{
const int QUOTE = '"';
const int ESCAPE = '\\';
const int SPACE = ' ';
split_state_t state = SS_SPACE;
int argc = 0;
char *next_arg_start = line;
char *out_ptr = line;
for (char *in_ptr = line; argc < argv_size - 1; ++in_ptr) {
int char_in = (unsigned char) *in_ptr;
if (char_in == 0) {
break;
}
int char_out = -1;
/* helper function, called when done with an argument */
void end_arg() {
char_out = 0;
argv[argc++] = next_arg_start;
state = SS_SPACE;
}
switch (state) {
case SS_SPACE:
if (char_in == SPACE) {
/* skip space */
} else if (char_in == QUOTE) {
next_arg_start = out_ptr;
state = SS_QUOTED_ARG;
} else if (char_in == ESCAPE) {
next_arg_start = out_ptr;
state = SS_ARG_ESCAPED;
} else {
next_arg_start = out_ptr;
state = SS_ARG;
char_out = char_in;
}
break;
case SS_QUOTED_ARG:
if (char_in == QUOTE) {
end_arg();
} else if (char_in == ESCAPE) {
state = SS_QUOTED_ARG_ESCAPED;
} else {
char_out = char_in;
}
break;
case SS_ARG_ESCAPED:
case SS_QUOTED_ARG_ESCAPED:
if (char_in == ESCAPE || char_in == QUOTE || char_in == SPACE) {
char_out = char_in;
} else {
/* unrecognized escape character, skip */
}
state = (split_state_t) (state & (~SS_FLAG_ESCAPE));
break;
case SS_ARG:
if (char_in == SPACE) {
end_arg();
} else if (char_in == ESCAPE) {
state = SS_ARG_ESCAPED;
} else {
char_out = char_in;
}
break;
}
/* need to output anything? */
if (char_out >= 0) {
*out_ptr = char_out;
++out_ptr;
}
}
/* make sure the final argument is terminated */
*out_ptr = 0;
/* finalize the last argument */
if (state != SS_SPACE && argc < argv_size - 1) {
argv[argc++] = next_arg_start;
}
/* add a NULL at the end of argv */
argv[argc] = NULL;
return argc;
}

View file

@ -28,6 +28,10 @@ Additional third party copyrighted code is included under the following licenses
* `JSMN`_ JSON Parser (components/jsmn) Copyright (c) 2010 Serge A. Zaitsev and licensed under the MIT license. * `JSMN`_ JSON Parser (components/jsmn) Copyright (c) 2010 Serge A. Zaitsev and licensed under the MIT license.
* `argtable3`_ arugment parsing library Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann and licensed under 3-clause BSD license.
* `linenoise`_ line editing library Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>, Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>, licensed under 2-clause BSD license.
Where source code headers specify Copyright & License information, this information takes precedence over the summaries made here. Where source code headers specify Copyright & License information, this information takes precedence over the summaries made here.
ROM Source Code Copyrights ROM Source Code Copyrights
@ -106,3 +110,6 @@ Copyright (C) 2011, ChaN, all right reserved.
.. _FreeBSD net80211: https://github.com/freebsd/freebsd/tree/master/sys/net80211 .. _FreeBSD net80211: https://github.com/freebsd/freebsd/tree/master/sys/net80211
.. _TJpgDec: http://elm-chan.org/fsw/tjpgd/00index.html .. _TJpgDec: http://elm-chan.org/fsw/tjpgd/00index.html
.. _JSMN: http://zserge.com/jsmn.html .. _JSMN: http://zserge.com/jsmn.html
.. _argtable3: https://github.com/argtable/argtable3
.. _linenoise: https://github.com/antirez/linenoise

143
docs/api-guides/console.rst Normal file
View file

@ -0,0 +1,143 @@
Console
=======
ESP-IDF provides ``console`` component, which includes building blocks needed to develop an interactive console over serial port. This component includes following facilities:
- Line editing, provided by `linenoise`_ library. This includes handling of backspace and arrow keys, scrolling through command history, command auto-completion, and argument hints.
- Splitting of command line into arguments.
- Argument parsing, provided by `argtable3`_ library. This library includes APIs useful for parsing GNU style command line arguments.
- Functions for registration and dispatching of commands.
These facilities can be used together or independently. For example, it is possible to use line editing and command registration features, but use ``getopt`` or custom code for argument parsing, instead of `argtable3`_. Likewise, it is possible to use simpler means of command input (such as ``fgets``) together with the rest of the means for command splitting and argument parsing.
Line editing
------------
Line editing feature lets users compose commands by typing them, erasing symbols using 'backspace' key, navigating within the command using left/right keys, navigating to previously typed commands using up/down keys, and performing autocompletion using 'tab' key.
.. note:: This feature relies on ANSI escape sequence support in the terminal application. As such, serial monitors which display raw UART data can not be used together with the line editing library. If you see ``[6n`` or similar escape sequence when running get_started/console example instead of a command prompt (``[esp32]>``), it means that the serial monitor does not support escape sequences. Programs which are known to work are GNU screen, minicom, and idf_monitor.py (which can be invoked using ``make monitor`` from project directory).
Here is an overview of functions provided by `linenoise`_ library.
Configuration
^^^^^^^^^^^^^
Linenoise library does not need explicit initialization. However, some configuration defaults may need to be changed before invoking the main line editing function.
``linenoiseClearScreen``
Clear terminal screen using an escape sequence and position the cursor at the top left corner.
``linenoiseSetMultiLine``
Switch between single line and multi line editing modes. In single line mode, if the length of the command exceeds the width of the terminal, the command text is scrolled within the line to show the end of the text. In this case the beginning of the text is hidden. Single line needs less data to be sent to refresh screen on each key press, so exhibits less glitching compared to the multi line mode. On the flip side, editing commands and copying command text from terminal in single line mode is harder. Default is single line mode.
Main loop
^^^^^^^^^
``linenoise``
In most cases, console applications have some form of read/eval loop. ``linenoise`` is the single function which handles user's key presses and returns completed line once 'enter' key is pressed. As such, it handles the 'read' part of the loop.
``linenoiseFree``
This function must be called to release the command line buffer obtained from ``linenoise`` function.
Hints and completions
^^^^^^^^^^^^^^^^^^^^^
``linenoiseSetCompletionCallback``
When user presses 'tab' key, linenoise library invokes completion callback. The callback should inspect the contents of the command typed so far and provide a list of possible completions using calls to ``linenoiseAddCompletion`` function. ``linenoiseSetCompletionCallback`` function should be called to register this completion callback, if completion feature is desired.
``console`` component provides a ready made function to provide completions for registered commands, ``esp_console_get_completion`` (see below).
``linenoiseAddCompletion``
Function to be called by completion callback to inform the library about possible completions of the currently typed command.
``linenoiseSetHintsCallback``
Whenever user input changes, linenoise invokes hints callback. This callback can inspect the command line typed so far, and provide a string with hints (which can include list of command arguments, for example). The library then displays the hint text on the same line where editing happens, possibly with a different color.
``linenoiseSetFreeHintsCallback``
If hint string returned by hints callback is dynamically allocated or needs to be otherwise recycled, the function which performs such cleanup should be registered via ``linenoiseSetFreeHintsCallback``.
History
^^^^^^^
``linenoiseHistorySetMaxLen``
This function sets the number of most recently typed commands to be kept in memory. Users can navigate the history using up/down arrows.
``linenoiseHistoryAdd``
Linenoise does not automatically add commands to history. Instead, applications need to call this function to add command strings to the history.
``linenoiseHistorySave``
Function saves command history from RAM to a text file, for example on an SD card or on a filesystem in flash memory.
``linenoiseHistoryLoad``
Counterpart to ``linenoiseHistorySave``, loads history from a file.
``linenoiseHistoryFree``
Releases memory used to store command history. Call this function when done working with linenoise library.
Splitting of command line into arguments
----------------------------------------
``console`` component provides ``esp_console_split_argv`` function to split command line string into arguments. The function returns the number of arguments found (``argc``) and fills an array of pointers which can be passed as ``argv`` argument to any function which accepts arguments in ``argc, argv`` format.
The command line is split into arguments according to the following rules:
- Arguments are separated by spaces
- If spaces within arguments are required, they can be escaped using ``\`` (backslash) character.
- Other escape sequences which are recognized are ``\\`` (which produces literal backslash) and ``\"``, which produces a double quote.
- Arguments can be quoted using double quotes. Quotes may appear only in the beginning and at the end of the argument. Quotes within the argument must be escaped as mentioned above. Quotes surrounding the argument are stripped by ``esp_console_split_argv`` function.
Examples:
- ``abc def 1 20 .3`` ⟶ [ ``abc``, ``def``, ``1``, ``20``, ``.3`` ]
- ``abc "123 456" def`` ⟶ [ ``abc``, ``123 456``, ``def`` ]
- ```a\ b\\c\"`` ⟶ [ ``a b\c"`` ]
Argument parsing
----------------
For argument parsing, ``console`` component includes `argtable3`_ library. Please see `tutorial`_ for an introduction to `argtable3`_. Github repository also includes `examples`_.
.. _argtable3: http://www.argtable.org/
.. _linenoise: https://github.com/antirez/linenoise
.. _tutorial: http://www.argtable.org/tutorial/
.. _examples: https://github.com/argtable/argtable3/tree/master/examples
Command registration and dispatching
------------------------------------
``console`` component includes utility functions which handle registration of commands, matching commands typed by the user to registered ones, and calling these commands with the arguments given on the command line.
Application first initializes command registration module using a call to ``esp_console_init``, and calls ``esp_console_cmd_register`` function to register command handlers.
For each command, application provides the following information (in the form of ``esp_console_cmd_t`` structure):
- Command name (string without spaces)
- Help text explaining what the command does
- Optional hint text listing the arguments of the command. If application uses Argtable3 for argument parsing, hint text can be generated automatically by providing a pointer to argtable argument definitions structure instead.
- The command handler function.
A few other functions are provided by the command registration module:
``esp_console_run``
This function takes the command line string, splits it into argc/argv argument list using ``esp_console_split_argv``, looks up the command in the list of registered components, and if it is found, executes its handler.
``esp_console_split_argv``
Adds ``help`` command to the list of registered commands. This command prints the list of all the registered commands, along with their arguments and help texts.
``esp_console_get_completion``
Callback function to be used with ``linenoiseSetCompletionCallback`` from linenoise library. Provides completions to linenoise based on the list of registered commands.
``esp_console_get_hint``
Callback function to be used with ``linenoiseSetHintsCallback`` from linenoise library. Provides argument hints for registered commands to linenoise.
Example
-------
Example application illustrating usage of the ``console`` component is available in ``examples/system/console`` directory. This example shows how to initialize UART and VFS functions, set up linenoise library, read and handle commands from UART, and store command history in Flash. See README.md in the example directory for more details.

View file

@ -16,5 +16,6 @@ API Guides
ULP Coprocessor <ulp> ULP Coprocessor <ulp>
Unit Testing <unit-tests> Unit Testing <unit-tests>
Application Level Tracing <app_trace> Application Level Tracing <app_trace>
Console Component <console>
ROM debug console <romconsole> ROM debug console <romconsole>
WiFi Driver <wifi> WiFi Driver <wifi>

View file

@ -0,0 +1,9 @@
#
# 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,108 @@
# Console example
This example illustrates usage of `console` component to create an interactive shell.
## Configuring UART and VFS
``initialize_console`` function configures some aspects of UART and VFS relevant to the operation of console:
- By default `stdin` and `stdout` are buffered streams. This means that the text written to `stdout` will not be immediately sent to UART. This is not the desirable behavior for the console, so buffering for `stdin` and `stdout` is disabled using `setvbuf` function.
- 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 is:
- 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.
- UART driver is initialized, and VFS is configured to use UART driver's interrupt-driver read and write functions.
## 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 `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 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.
# Example output
Here is an sample session with the console example. GPIO15 is connected to GND to remove 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]>
```
---
See the README.md file in the upper level 'examples' directory for more information about examples.

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,15 @@
/* 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
// Register system functions
void register_system();
// Register WiFi functions
void register_wifi();

View file

@ -0,0 +1,246 @@
/* 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 "esp_log.h"
#include "esp_console.h"
#include "esp_system.h"
#include "driver/rtc_io.h"
#include "argtable3/argtable3.h"
#include "cmd_decl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/rtc_cntl_reg.h"
static void register_free();
static void register_restart();
static void register_deep_sleep();
static void register_make();
void register_system()
{
register_free();
register_restart();
register_deep_sleep();
register_make();
}
/** 'restart' command restarts the program */
static int restart(int argc, char** argv)
{
ESP_LOGI(__func__, "Restarting");
esp_restart();
}
static void register_restart()
{
const esp_console_cmd_t cmd = {
.command = "restart",
.help = "Restart the program",
.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()
{
const esp_console_cmd_t cmd = {
.command = "free",
.help = "Get the total size of heap memory available",
.hint = NULL,
.func = &free_mem,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'deep_sleep' command puts the chip into deep 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;
} 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(__func__, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_deep_sleep_enable_timer_wakeup(timeout) );
}
if (deep_sleep_args.wakeup_gpio_num->count) {
int io_num = deep_sleep_args.wakeup_gpio_num->ival[0];
if (!rtc_gpio_is_valid_gpio(io_num)) {
ESP_LOGE(__func__, "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(__func__, "Invalid wakeup level: %d", level);
return 1;
}
}
ESP_LOGI(__func__, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
ESP_ERROR_CHECK( esp_deep_sleep_enable_ext1_wakeup(1ULL << io_num, level) );
}
esp_deep_sleep_start();
}
static void register_deep_sleep()
{
deep_sleep_args.wakeup_time =
arg_int0("t", "time", "<t>", "Wake up time, ms");
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");
deep_sleep_args.end = arg_end(3);
const esp_console_cmd_t cmd = {
.command = "deep_sleep",
.help = "Enter deep sleep mode. "
"Two wakeup modes are supported: timer and GPIO. "
"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) );
}
/** This command helps maintain sanity when testing console example from a console */
static int make(int argc, char** argv)
{
int count = REG_READ(RTC_CNTL_STORE0_REG);
if (++count >= 3) {
printf("This is not the console you are looking for.\n");
return 0;
}
REG_WRITE(RTC_CNTL_STORE0_REG, count);
const char* make_output =
R"(LD build/console.elf
esptool.py v2.1-beta1
)";
const char* flash_output[] = {
R"(Flashing binaries to serial port )" CONFIG_ESPTOOLPY_PORT R"( (app at offset 0x10000)...
esptool.py v2.1-beta1
Connecting....
)",
R"(Chip is ESP32D0WDQ6 (revision 0)
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
Configuring flash size...
Auto-detected Flash size: 4MB
Flash params set to 0x0220
Compressed 15712 bytes to 9345...
)",
R"(Wrote 15712 bytes (9345 compressed) at 0x00001000 in 0.1 seconds (effective 1126.9 kbit/s)...
Hash of data verified.
Compressed 333776 bytes to 197830...
)",
R"(Wrote 333776 bytes (197830 compressed) at 0x00010000 in 3.3 seconds (effective 810.3 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 82...
)",
R"(Wrote 3072 bytes (82 compressed) at 0x00008000 in 0.0 seconds (effective 1588.4 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting...
)"
};
const char* monitor_output =
R"(MONITOR
)" LOG_COLOR_W R"(--- idf_monitor on )" CONFIG_ESPTOOLPY_PORT R"( 115200 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --
)" LOG_RESET_COLOR;
bool need_make = false;
bool need_flash = false;
bool need_monitor = false;
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "all") == 0) {
need_make = true;
} else if (strcmp(argv[i], "flash") == 0) {
need_make = true;
need_flash = true;
} else if (strcmp(argv[i], "monitor") == 0) {
need_monitor = true;
} else if (argv[i][0] == '-') {
/* probably -j option */
} else if (isdigit((int) argv[i][0])) {
/* might be an argument to -j */
} else {
printf("make: *** No rule to make target `%s'. Stop.\n", argv[i]);
/* Technically this is an error, but let's not spoil the output */
return 0;
}
}
if (argc == 1) {
need_make = true;
}
if (need_make) {
printf("%s", make_output);
}
if (need_flash) {
size_t n_items = sizeof(flash_output) / sizeof(flash_output[0]);
for (int i = 0; i < n_items; ++i) {
printf("%s", flash_output[i]);
vTaskDelay(200/portTICK_PERIOD_MS);
}
}
if (need_monitor) {
printf("%s", monitor_output);
esp_restart();
}
return 0;
}
static void register_make()
{
const esp_console_cmd_t cmd = {
.command = "make",
.help = NULL, /* hide from 'help' output */
.hint = "all | flash | monitor",
.func = &make,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

View file

@ -0,0 +1,123 @@
/* 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 "tcpip_adapter.h"
#include "esp_event_loop.h"
static EventGroupHandle_t wifi_event_group;
const int CONNECTED_BIT = BIT0;
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch(event->event_id) {
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
break;
default:
break;
}
return ESP_OK;
}
static void initialise_wifi(void)
{
esp_log_level_set("wifi", ESP_LOG_WARN);
static bool initialized = false;
if (initialized) {
return;
}
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
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 };
strncpy((char*) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
if (pass) {
strncpy((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(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_connect() );
int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
1, 1, 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]);
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()
{
join_args.timeout = arg_int0(NULL, "timeout", "<t>", "Connection timeout, ms");
join_args.timeout->ival[0] = 5000; // set default value
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,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,147 @@
/* 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"
static const char* TAG = "example";
/* 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()
{
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 (0x%x)", err);
return;
}
}
#endif // CONFIG_STORE_HISTORY
static void initialize_console()
{
/* Disable buffering on stdin and stdout */
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK( uart_driver_install(CONFIG_CONSOLE_UART_NUM,
256, 0, 0, NULL, 0) );
/* Tell VFS to use UART driver */
esp_vfs_dev_uart_use_driver(CONFIG_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);
#if CONFIG_STORE_HISTORY
/* Load command history from filesystem */
linenoiseHistoryLoad(HISTORY_PATH);
#endif
}
void app_main()
{
#if CONFIG_STORE_HISTORY
initialize_filesystem();
#endif
initialize_console();
/* Register commands */
esp_console_register_help_command();
register_system();
register_wifi();
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");
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
const char* prompt = LOG_COLOR_I "[esp32]> " LOG_RESET_COLOR;
/* Main loop */
char *line;
/* Get a line using linenoise.
* The line is returned when ENTER is pressed.
*/
while((line = linenoise(prompt)) != NULL) {
if (strlen(line) > 0) { /* Ignore empty lines */
/* Add the command to the history */
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_OK && ret != ESP_OK) {
printf("Command returned non-zero error code: 0x%x\n", ret);
} else if (err != ESP_OK) {
printf("Internal error: 0x%x\n", err);
}
}
/* linenoise allocates line buffer on the heap, so need to free it */
linenoiseFree(line);
}
}

View file

@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
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 change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
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,13 @@
# Reduce bootloader log verbosity
CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y
CONFIG_LOG_BOOTLOADER_LEVEL=2
# Increase main task stack size
CONFIG_MAIN_TASK_STACK_SIZE=6144
# Enable filesystem
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
CONFIG_APP_OFFSET=0x10000