# Nano-Shell
## Contents
- [Hot Key Bind](#hot-key-bind)
- [Add Your Command](#add-your-command)
- [HOW:](#how)
- [Example:](#example)
- [Configuring](#configuring)
- [readline configurations:](#readline-configurations)
- [command configurations:](#command-configurations)
- [shell configurations:](#shell-configurations)
- [shell io configurations:](#shell-io-configurations)
- [Porting nano-shell to your project](#porting-nano-shell-to-your-project)
---
Nano-Shell is a light but powerful shell designed for embedded systems.
- with or without an operating system;
- `` or ``;
- highly configurable;
- powerful: command line editing, history record, multi-line input, hot key bind, etc...
- memory friendly: **NO** malloc and free;
- light (build with arm-none-eabi-gcc 7.3.1 20180622, -O3):
| | .text(1) | .rodata | .bss(2) | .data |
|:------------------------------------------:|:------:|:-------:|:-----:|:-----:|
| main loop mode,
all configurations on | 2.5KB | 1.03KB | 852B | 8B |
| main loop mode,
all configurations off(3) | 616B | 600B | 180B | 0B |
| react mode,
all configurations on | 2.52KB | 1.03KB | 852B | 8B |
| react mode,
all configurations off(3) | 608B | 600B | 180B | 0B |
> 1: include built-in `help` command.
>
> 2: include `input buffer`(default 128Bytes) and `hisroty record buffer`(defaut 650Bytes(5*(128+2)))
>
> 3: except `CONFIG_SHELL_CMD_BUILTIN_HELP`.
---
## Hot Key Bind
nano-shell has internally bound these hotkeys:
| HOT KEY | ASCII/ANSI-Escape Code
(Xterm, VT100) | Function |
|---------|------------------------|----------|
| Ctrl-A | 1 | Home
Move curosr to the start of line.|
| Ctrl-E | 5 | End
Move curosr to the end of line.|
| Ctrl-P | 16 | Up arrow(-->)
Move cursor right one char.|
| Ctrl-N | 14 | Down arrow(-->)
Move cursor right one char.|
| Ctrl-B | 2 | Left arrow(<--)
Move cursor left one char.|
| Ctrl-F | 6 | Right arrow(-->)
Move cursor right one char.|
| Ctrl-D | 4 | Delete
Delete the character under the cursor.|
| Ctrl-K | 11 | Erase forward
Clears all characters from the cursor position to the end of the line.|
| Ctrl-U | 21 | Erase backword
Clears all characters from the cursor position to the start of the line..|
| Ctrl-C | 3 | Kill the line.|
| Home | Esc[H | Move curosr to the beginning of line.|
| End | Esc[F | Move curosr to the end of line.|
| Up Arrow | Esc[A | Get the previous history. |
| Down Arrow | Esc[B | Get the next history. |
| Left Arrow | Esc[D | Left arrow(<--)
Move cursor left one char. |
| Right Arrow | Esc[C | Right arrow(-->)
Move cursor right one char.|
| Delete | Esc[3~ | Delete the character under the cursor.|
---
## Add Your Command
### HOW:
Commands are added to nano-shell by creating a new command structure.
This is done by first including `command/command.h`, then using the `NANO_SHELL_ADD_CMD()` macro to fill in a `shell_cmd_t` struct.
``` c
NANO_SHELL_ADD_CMD(_name, _func, _brief, _help)
```
`_name`: name of the command. Note: **THIS IS NOT** a string.
`_func`: function pointer: `(*cmd)(const shell_cmd_t *, int, int, char *const[])`.
`_brief`: brief summaries of the command. This is a string.
`_help`: detailed help information of the command. This is a string.
Commands with sub-commands can easily be created with a combination of `NANO_SHELL_DEFINE_SUBCMDS`,
`NANO_SHELL_SUBCMD_ENTRY` and `NANO_SHELL_ADD_CMD`. See examples for more details.
### Example 1: Simple command:
```c
int _do_demo(const shell_cmd_t *pcmd, int argc, char *const argv[])
{
for (int i=0; i
Run `help` and `help demo` in terminal:
### Example 2: Command with sub-commands:
It is possible to create commands with sub-commands. More nested command can also be created.
```c
/* Create a bunch of commands to be run as a demo */
int _top_command_fallback_fct(const shell_cmd_t* pCmdt, int argc, char* const argv[])
{
if(argc > 1) {
shell_printf(" '%s' is not a subcommand of %s\r\n", argv[1], argv[0]);
}
else {
shell_printf(" Hey, there is subcommands here, type '%s help' for more info\r\n", argv[0]);
}
return 0;
}
int _do_subcommand1(const shell_cmd_t* pCmdt, int argc, char* const argv[]) {
shell_puts(" This is sub-command 1\r\n");
return 0;
}
int _do_subsubcommand1(const shell_cmd_t* pCmdt, int argc, char* const argv[]) {
shell_puts(" This is sub-sub-command 1\r\n");
return 0;
}
int _do_subsubcommand2(const shell_cmd_t* pCmdt, int argc, char* const argv[]) {
shell_puts(" This is sub-sub-command 2\r\n");
return 0;
}
// Sub-Sub commands group
NANO_SHELL_DEFINE_SUBCMDS(subcommand2_group,
NULL,
NANO_SHELL_SUBCMD_ENTRY(subsubcommand1,
_do_subsubcommand1,
"first sub-sub-command",
""),
NANO_SHELL_SUBCMD_ENTRY(subsubcommand2,
_do_subsubcommand2,
"second sub-sub-command",
""));
// Sub commands group
NANO_SHELL_DEFINE_SUBCMDS(top_command_group,
_top_command_fallback_fct,
NANO_SHELL_SUBCMD_ENTRY(subcommand1,
_do_subcommand1,
"first subcommand",
""),
NANO_SHELL_SUBCMD_ENTRY(subcommand2,
NANO_SHELL_SUBCMDS_FCT(subcommand2_group),
"second subcommand with sub-sub commands",
""));
// Command with sub commands
NANO_SHELL_ADD_CMD(top_command,
NANO_SHELL_SUBCMDS_FCT(top_command_group),
"A command with subcommand",
" This command have 2 sub-commands and one sub-sub-command\r\n");
```
In a terminal, you get:
---
## Configuring
@file: [`shell_config.h`](/shell_config.h)
### readline configurations:
- CONFIG_SHELL_INPUT_BUFFSIZE (127U)
- default: `(127U)`
- config the command line input buffer size (in byte).
- CONFIG_SHELL_LINE_EDITING
- default: `1(enabled)`
- set this to `0` will disable command line editing.
- CONFIG_SHELL_KEY_SEQ_BIND
- default: `1(enabled)`
- set this to `0` will disable ANSI-Escape-Sequence. nano-shell will not be able to detect Home/End/Delete/Arrow keys. Doesn't affect Ctrl-P, Ctrl-N, etc...
- CONFIG_SHELL_MULTI_LINE
- default: `1(enabled)`
- use Backslash('\\') for line continuation when enabled, set this to `0` will disable line continuation.
- line continuation example:
- CONFIG_SHELL_HIST_MIN_RECORD
- default: `(5U)`
- set this to `0` will disable history record.
- nano-shell will take `CONFIG_SHELL_HIST_MIN_RECORD*(2+CONFIG_SHELL_INPUT_BUFFSIZE)` bytes to record **At Least** `CONFIG_SHELL_HIST_MIN_RECORD` histroys. The max history records depends on the average length of the input.
### command configurations:
- CONFIG_SHELL_CMD_BRIEF_USAGE
- default: `1(enabled)`
- command structure `shell_cmd_t` has a pointer point to "brief usage information of the command", set this to `0` will remove it.
- CONFIG_SHELL_CMD_LONG_HELP
- default: `1(enabled)`
- command structure `shell_cmd_t` has a pointer point to "detailed help information of the command", set this to `0` will remove it.
- CONFIG_SHELL_CMD_BUILTIN_HELP
- default: `1(enabled)`
- nano-shell provides a built-in `help` command, set this to `0` will remove the deault `help` command.
- CONFIG_SHELL_CMD_MAX_ARGC
- default: `(10U)`
- config the max number of arguments, must be no less than 1.
### shell configurations:
- CONFIG_SHELL_PROMPT
- default: `"Nano-Shell >> "`
- config the shell promot that will displayed at the start of line. If you don't need it, set this to `NULL` or `""`.
### shell io configurations:
- CONFIG_SHELL_PRINTF_BUFFER_SIZE
- default: `(128U)`
- config the buffer size of `shell_printf()`.
---
## Porting nano-shell to your project
### 1. add nano-shell root path to your project include path.
### 2. implement these functions([`@file shell_io.h`](/shell_io/shell_io.h)) in your project:
this file may help: [`/shell_io/shell_io.c`](/shell_io/shell_io.c).
```c
/**
* @brief send a chararcter...
*
*/
extern void shell_putc(char ch);
/**
* @brief send string...
*
*/
extern void shell_puts(const char *str);
/**
* @brief printf() for nano-shell
*
*/
extern int shell_printf(const char *format, ...) __attribute__((format(printf, 1, 2)));
/**
* @brief: Get next character available from stream.
*
* @param ch: Return the character in `ch` if there was...
* @return: Result is non-zero if there was a character, or 0 if there wasn't.
*
*/
extern int shell_getc(char *ch);
```
Note:
- `int shell_getc(char *ch)` is **NOT USED** in ``
- If you run nano-shell in ``, to avoid losing characters, you'd better use a low layer receive fifo.
Take uart for example, you can detect incoming data using interrupts and then store each received character in a first-in-first-out (FIFO) buffer:
```c
void your_uart_interrupt_handler(void)
{
/* your uart receive code */
char ch = uart_get_char();
/* store character in fifo */
fifo_push(ch);
}
```
then `shell_getc(char *ch)` may be:
```c
int shell_getc(char *ch)
{
if (fifo_empty()) { // if no character in fifo,
return 0; // return false
}
*ch = fifo_pop(); // fifo is not empty, get a character from fifo.
return 1; // return true
}
```
I write a simple and lock free fifo based on ring buffer in [`@file: /shell_io/static_fifo.h`](/shell_io/static_fifo.h), maybe helpful...
### 3. then modify the configuration file: [`shell_config.h`](/shell_config.h)
### 4. according to your system, you can:
#### 4.1 without os, main loop mode:
```c
#include "nano_shell.h"
int main(void)
{
/* system init code... */
/* nano-shell infinite loop. */
nano_shell_loop(NULL);
}
```
#### 4.2 without os, react mode(non-block):
you can use it in interrupt, take UART for example:
```c
void your_uart_interrupt_handler (void)
{
/* your uart receive code */
char ch = uart_get_char();
/* nano-shell isr interface */
nano_shell_react(ch);
}
```
Note:
- `nano_shell_react()` is non-blocked (unless there was an infinite loop in your command function), you can call it when get a new character.
- ~~It is recommended to disable some configurations in `shell_config.h` if it was called in interrupt.~~
#### 4.3 with os, take freertos for example:
```c
#include "nano_shell.h"
int main(void)
{
/* system init code... */
/* create nano_shell task */
TaskHandle_t shellTaskHandle = NULL;
xTaskCreate(nano_shell_loop, "shellTask", , NULL,
, &shellTaskHandle);
/* start rtos task scheduler */
vTaskStartScheduler();
}
```
Note:
- When determining the stack size for nano-shell, you should consider the memory occupied by commands added in nano-shell.
### 5. define nano_shell section in your linker script file:
add these 5 lines to your linker script file:
```ld
.nano_shell : {
. = ALIGN(4);
KEEP (*(SORT(.nano_shell*)))
. = ALIGN(4);
} >FLASH
```
### 6. build, flash and try it.